@@ -14,16 +14,22 @@ C ABI FFI layer over `iscc-lib`, enabling integration from C, Go (cgo), Java (JN
1414
1515```
1616crates/iscc-ffi/
17- Cargo.toml # cdylib + staticlib, depends only on iscc-lib
18- cbindgen.toml # Header generation config
17+ Cargo.toml # cdylib + staticlib, depends on iscc-lib; csbindgen build-dep
18+ cbindgen.toml # C header generation config
19+ build.rs # Generates C# P/Invoke bindings via csbindgen
20+ include/
21+ iscc.h # Generated C header (checked in, regenerate after API changes)
1922 src/
2023 lib.rs # All extern "C" functions, #[repr(C)] types, memory management
2124 tests/
2225 test_iscc.c # C test program that links against the shared library
26+ examples/
27+ iscc_sum.c # Streaming example: reads a file, produces ISCC-CODE
28+ CMakeLists.txt # CMake build for the example
2329```
2430
2531Single-file crate. All FFI symbols, helper functions, ` #[repr(C)] ` types, and memory management live
26- in ` src/lib.rs ` . Do not split into submodules unless the file exceeds ~ 1500 lines.
32+ in ` src/lib.rs ` . Do not split into submodules unless the file exceeds ~ 2500 lines.
2733
2834## cbindgen Configuration
2935
@@ -33,21 +39,41 @@ in `src/lib.rs`. Do not split into submodules unless the file exceeds ~1500 line
3339- System includes: ` stdint.h ` , ` stdbool.h ` , ` stddef.h `
3440- Export prefix: ` iscc_ ` (applied to types like ` IsccByteBuffer ` -> ` iscc_IsccByteBuffer ` )
3541- Function prefix: none (functions already use ` iscc_ ` prefix via naming convention)
36- - Header is NOT checked into the repo; generate on demand :
42+ - Header is checked into the repo at ` include/iscc.h ` ; regenerate after API changes :
3743 ``` bash
3844 cbindgen --config crates/iscc-ffi/cbindgen.toml --crate iscc-ffi --output crates/iscc-ffi/include/iscc.h
3945 ```
4046
47+ # # csbindgen (C# P/Invoke)
48+
49+ - Build dependency: ` csbindgen = " 1.9.7" ` in ` Cargo.toml`
50+ - ` build.rs` reads ` src/lib.rs` and generates ` packages/dotnet/Iscc.Lib/NativeMethods.g.cs`
51+ - DLL name: ` iscc_ffi` , namespace: ` Iscc.Lib` , class: ` NativeMethods`
52+ - Runs automatically on every ` cargo build` ; no manual step needed
53+
4154# # C ABI Conventions
4255
4356# ## Naming
4457
4558- All exported symbols prefixed with ` iscc_`
46- - Gen functions: ` iscc_gen_meta_code_v0` , ` iscc_gen_text_code_v0` , etc.
47- - Text utilities: ` iscc_text_clean` , ` iscc_text_collapse` , etc.
48- - Algorithm primitives: ` iscc_alg_simhash` , ` iscc_alg_minhash_256` , ` iscc_alg_cdc_chunks`
59+ - Gen functions: ` iscc_gen_meta_code_v0` , ` iscc_gen_text_code_v0` , ` iscc_gen_image_code_v0` ,
60+ ` iscc_gen_audio_code_v0` , ` iscc_gen_video_code_v0` , ` iscc_gen_mixed_code_v0` ,
61+ ` iscc_gen_data_code_v0` , ` iscc_gen_instance_code_v0` , ` iscc_gen_iscc_code_v0` ,
62+ ` iscc_gen_sum_code_v0`
63+ - Text utilities: ` iscc_text_clean` , ` iscc_text_collapse` , ` iscc_text_remove_newlines` ,
64+ ` iscc_text_trim`
65+ - Codec: ` iscc_encode_component` , ` iscc_decode` , ` iscc_decompose` , ` iscc_encode_base64` ,
66+ ` iscc_json_to_data_url` , ` iscc_sliding_window`
67+ - Algorithm primitives: ` iscc_alg_simhash` , ` iscc_alg_minhash_256` , ` iscc_alg_cdc_chunks` ,
68+ ` iscc_soft_hash_video_v0`
69+ - Algorithm constants: ` iscc_meta_trim_name` , ` iscc_meta_trim_description` , ` iscc_meta_trim_meta` ,
70+ ` iscc_io_read_size` , ` iscc_text_ngram_size`
71+ - Streaming hashers: ` iscc_data_hasher_new` , ` iscc_data_hasher_update` , ` iscc_data_hasher_finalize` ,
72+ ` iscc_data_hasher_free` , ` iscc_instance_hasher_new` , ` iscc_instance_hasher_update` ,
73+ ` iscc_instance_hasher_finalize` , ` iscc_instance_hasher_free`
4974- Memory management: ` iscc_free_string` , ` iscc_free_string_array` , ` iscc_free_byte_buffer` ,
50- ` iscc_free_byte_buffer_array`
75+ ` iscc_free_byte_buffer_array` , ` iscc_free_decode_result` , ` iscc_free_sum_code_result`
76+ - WASM allocator: ` iscc_alloc` , ` iscc_dealloc`
5177- Error retrieval: ` iscc_last_error`
5278- Diagnostics: ` iscc_conformance_selftest`
5379
@@ -73,16 +99,22 @@ in `src/lib.rs`. Do not split into submodules unless the file exceeds ~1500 line
7399
74100# # Rust-to-C Type Mapping
75101
76- | Rust type | C type | Notes |
77- | -------------------------- | ------------------------------------- | ------------------------------------------------------------ |
78- | ` * const c_char` | ` const char * ` | Input strings (null-terminated UTF-8) |
79- | ` * mut c_char` | ` char * ` | Output strings (caller frees) |
80- | ` * const u8` / ` * const i32` | ` const uint8_t * ` / ` const int32_t * ` | Input byte/int buffers |
81- | ` usize` | ` size_t` | Buffer lengths |
82- | ` u32` | ` uint32_t` | ` bits` parameter, feature values |
83- | ` bool` | ` bool` | Requires ` stdbool.h` |
84- | ` IsccByteBuffer` | ` struct iscc_IsccByteBuffer` | ` # [repr(C)]`: `{data: *mut u8, len: usize}` |
85- | ` IsccByteBufferArray` | ` struct iscc_IsccByteBufferArray` | ` # [repr(C)]`: `{buffers: *mut IsccByteBuffer, count: usize}` |
102+ | Rust type | C type | Notes |
103+ | -------------------------- | ---------------------------------------- | ---------------------------------------------------------------- |
104+ | ` * const c_char` | ` const char * ` | Input strings (null-terminated UTF-8) |
105+ | ` * mut c_char` | ` char * ` | Output strings (caller frees) |
106+ | ` * const u8` / ` * const i32` | ` const uint8_t * ` / ` const int32_t * ` | Input byte/int buffers |
107+ | ` usize` | ` uintptr_t` | Buffer lengths (cbindgen maps ` usize` to ` uintptr_t` ) |
108+ | ` u32` | ` uint32_t` | ` bits` parameter, feature values |
109+ | ` u64` | ` uint64_t` | ` filesize` in ` IsccSumCodeResult` |
110+ | ` u8` | ` uint8_t` | ` mtype` , ` stype` , ` version` , ` length` in codec functions |
111+ | ` bool` | ` bool` | Requires ` stdbool.h` |
112+ | ` IsccByteBuffer` | ` struct iscc_IsccByteBuffer` | ` # [repr(C)]`: `{data: *mut u8, len: usize}` |
113+ | ` IsccByteBufferArray` | ` struct iscc_IsccByteBufferArray` | ` # [repr(C)]`: `{buffers: *mut IsccByteBuffer, count: usize}` |
114+ | ` IsccDecodeResult` | ` struct iscc_IsccDecodeResult` | ` # [repr(C)]` : ` {ok, maintype, subtype, version, length, digest}` |
115+ | ` IsccSumCodeResult` | ` struct iscc_IsccSumCodeResult` | ` # [repr(C)]` : ` {ok, iscc, datahash, filesize, units}` |
116+ | ` FfiDataHasher` | ` struct iscc_FfiDataHasher` (opaque) | Not ` # [repr(C)]` ; interact only through function pointers |
117+ | ` FfiInstanceHasher` | ` struct iscc_FfiInstanceHasher` (opaque) | Not ` # [repr(C)]` ; interact only through function pointers |
86118
87119# # Build Commands
88120
@@ -116,7 +148,8 @@ LD_LIBRARY_PATH=target/debug /tmp/test_iscc
116148# ## C tests (`tests/test_iscc.c`)
117149
118150- Links against the shared library and ` # include "iscc.h"`
119- - Uses assertion macros: ` ASSERT_STR_EQ` , ` ASSERT_NULL` , ` ASSERT_NOT_NULL` , ` ASSERT_EQ`
151+ - Uses assertion macros: ` ASSERT_STR_EQ` , ` ASSERT_STR_STARTS_WITH` , ` ASSERT_NULL` ,
152+ ` ASSERT_NOT_NULL` , ` ASSERT_EQ`
120153- Mirrors the Rust unit test cases with identical expected values
121154- Reports pass/fail counts and exits non-zero on any failure
122155- Every returned string/buffer is freed to validate the free functions
@@ -129,7 +162,7 @@ LD_LIBRARY_PATH=target/debug /tmp/test_iscc
129162- Validate UTF-8 for all ` * const c_char` inputs via ` CStr::from_ptr` + `.to_str ()`
130163- Use ` # [unsafe(no_mangle)]` (Rust 2024 edition syntax) on all exported functions
131164- Use ` # [repr(C)]` only on types returned across the FFI boundary (` IsccByteBuffer` ,
132- ` IsccByteBufferArray` )
165+ ` IsccByteBufferArray` , ` IsccDecodeResult ` , ` IsccSumCodeResult ` )
133166- Never expose ` Vec` , ` String` , ` Box` , or any Rust-specific type across FFI
134167- Use `into_boxed_slice ()` + ` Box::into_raw()` to transfer Vec ownership across FFI; reconstruct
135168 with ` Box::from_raw(slice::from_raw_parts_mut(ptr, len))` to free
@@ -150,8 +183,14 @@ LD_LIBRARY_PATH=target/debug /tmp/test_iscc
150183- ** Missing free functions** : if you add a new return type, you must add a corresponding
151184 ` iscc_free_* ` function.
152185- ** cbindgen regeneration** : after adding or changing exported functions or ` # [repr(C)]` types,
153- regenerate the header. The C test will fail to compile if the header is stale.
186+ regenerate the C header. The C test will fail to compile if the header is stale.
187+ - ** csbindgen regeneration** : C# bindings regenerate automatically via ` build.rs` on every
188+ ` cargo build` . After changing function signatures, verify the generated
189+ ` packages/dotnet/Iscc.Lib/NativeMethods.g.cs` is correct.
154190- ** Optional parameters** : use NULL for optional ` * const c_char` params (e.g., ` description` and
155191 ` meta` in ` iscc_gen_meta_code_v0` ). Use ` ptr_to_optional_str` for these, not ` ptr_to_str` .
156192- ** Array parameters** : multi-element inputs (frame signatures, code arrays, digest arrays) use
157193 parallel arrays of pointers and lengths rather than a single struct array.
194+ - ** Struct return types** : ` IsccDecodeResult` and ` IsccSumCodeResult` are returned by value (not
195+ pointer). Each has a dedicated ` iscc_free_* ` function that must be called to release interior
196+ heap allocations, even on error.
0 commit comments