§ Interop
Every entry point is declared with [LibraryImport], so the marshalling stubs are source-generated and compiled in rather than reflected at runtime, and the assembly stays trimmable and ahead-of-time compilable.
Every verb takes one argument struct, as the C interface does, and holds a reference on each SafeHandle the struct names for the length of the C call, so a finalizer can never free a handle out from under a running verb, and the free of one a deferred call still names waits for that call.
§ What This Binding Does Not Expose
- Progress handlers. Every verb passes the C
progresscallback as null, so a longInsert,Refine,Absorb,Split,SampleorSaveruns to its end, andStatus.Cancellednever arises. - Deferred cursors and streams.
CursorandSearchStreampull blocking, so aDenseIndexDeferredOnlacks them. - Constructors on receivers. The constructors address no index yet, so no receiver offers them; the ones the C interface lets fan out take an
executorargument and run on the caller’s thread without one. - Version numbers and class names.
usearch_versionand the CPU and GPU class spellings stay unbound;USearchCapabilities.Version()answers the version as text.
§ Design
- A thin layer over the C ABI. Every name follows
include/usearch/: a C verbusearch_dense_<verb>is a<Verb>method here, and enums mirror C enums, never strings. - The receiver chooses, the verb stays. The index runs a verb on the calling thread,
On(executor)blocks on an executor, andDeferred(executor)submits it, all with the same arguments. - Deferred calls use the language’s own idioms.
Statuspolls throughusearch_outcome_status,Wait()blocks throughusearch_outcome_wait, andawaithands that wait to the thread pool. - No life of its own. The binding starts no threads and keeps no queues; releasing a
DeferredridesDisposeandusing, which waits for the call first. - Arenas are lent and reused, never shared. Every output whose length scales with the call lands in an arena the caller passes; a view the arena’s next call outdated throws rather than reading rewound memory.
- Nothing costs anything until it is used. Submitting a call allocates nothing, and an arena allocates only when a call writes into it.
- Blocking calls stay on the stack. A blocking call keeps its outputs on the stack, fixes its buffers in place without a pinning handle, and answers a view by value; a deferred one allocates its handle and one pinned outputs struct.
- Exceptions never cross the native boundary. A predicate or
rowOfdelegate that throws is captured on the worker thread and rethrown with its own type once the call is final.