Liblevenshtein 4.0.0-rc.5
dotnet add package Liblevenshtein --version 4.0.0-rc.5
NuGet\Install-Package Liblevenshtein -Version 4.0.0-rc.5
<PackageReference Include="Liblevenshtein" Version="4.0.0-rc.5" />
<PackageVersion Include="Liblevenshtein" Version="4.0.0-rc.5" />
<PackageReference Include="Liblevenshtein" />
paket add Liblevenshtein --version 4.0.0-rc.5
#r "nuget: Liblevenshtein, 4.0.0-rc.5"
#:package Liblevenshtein@4.0.0-rc.5
#addin nuget:?package=Liblevenshtein&version=4.0.0-rc.5&prerelease
#tool nuget:?package=Liblevenshtein&version=4.0.0-rc.5&prerelease
Vinary Tree .NET bindings
Liblevenshtein is the NuGet distribution for the idiomatic, streaming
VinaryTree.Liblevenshtein .NET namespace over the
stable native ABI. VinaryTree.Interop contains the shared two-word retained
resource contract used by independently packaged dictionary producers.
The package targets .NET 8 (the oldest supported LTS) and uses the latest C#
language standard. Query enumerators retain the query-start dictionary revision
and lease only one native result batch at a time. Dispose transducers and
enumerators deterministically; SafeHandle supplies the leak-safe fallback.
Build and test with dotnet run --project tests/VinaryTree.Liblevenshtein.Tests.
NuGet packaging is produced with dotnet pack -c Release.
Dictionary producers implementing IDictionaryResource gain immutable native
.NET collection views from VinaryTree.Interop:
DictionarySnapshot snapshot = dictionary.SnapshotEntries();
DictionaryKey key = DictionaryKey.FromString("café");
bool present = snapshot.Entries.TryGetValue(key, out ulong? value);
using DictionaryEntryStream entries = dictionary.StreamEntries();
foreach (DictionaryEntry entry in entries)
{
Console.WriteLine(entry);
if (ShouldStop(entry)) break;
}
DictionarySnapshot implements IReadOnlyCollection<DictionaryEntry> and
exposes ordered IReadOnlySet<DictionaryKey> and
IReadOnlyDictionary<DictionaryKey, ulong?> views. TryGetValue distinguishes
an absent key from a present key with no value. Keys preserve arbitrary bytes,
Unicode scalars, and the complete ulong domain with value equality. Snapshot
metadata carries exact length and revision identity. All three views share one
ordered entry array and use binary-search lookup; they do not build duplicate
output-sized hash tables or take a lazy-initialization lock. Set-relation calls
accept every ordinary IEnumerable<DictionaryKey> and allocate temporary
state only when the requested relation requires de-duplicating that external
sequence. OpenEntryEnumerator and StreamEntries own a native cursor;
dispose them after early termination. Enumeration copies and releases each
bounded batch before yielding managed entries, and cleanup cancels unread work.
Support and package contract
| Property | Contract |
|---|---|
| Binding | .NET |
| Languages/runtime | .NET 8+ and current C# |
| Support tier | Tier 2 |
| Distribution | NuGet package Liblevenshtein |
| Native boundary | Source-generated P/Invoke reaches the stable C ABI; VinaryTree.Interop carries retained resource handles between packages. |
| Canonical facade source | bindings/dotnet/src/VinaryTree.Liblevenshtein |
The support tier controls release gating, not semantic quality: every tier has the same snapshot, ownership, status, and ABI compatibility laws. Consult the binding architecture before implementing a custom provider and the family hub when combining independently packaged projects.
Executable example and verification
The repository's canonical executable example is
bindings/dotnet/tests/VinaryTree.Liblevenshtein.Tests/Program.cs. It exercises the same public package a user
installs and is run by the binding CI with:
dotnet run --project bindings/dotnet/tests/VinaryTree.Liblevenshtein.Tests
Examples deliberately construct or receive resources through public project packages. They never import private Rust modules, depend on object layout, or reach behind the stable C/resource ABIs.
Public API and data model
The idiomatic facade groups the stable surface into these concepts:
| Concept | Semantics |
|---|---|
| Dictionary resource | A retained vt.dictionary.v1 capability. Construction and mutation belong to a producer such as libdictenstein. |
| Transducer | Immutable query configuration plus a retained dictionary provider; construction is constant-time with respect to dictionary size. |
| Query cursor | A one-shot traversal over the immutable dictionary revision captured at query start. |
| Match/batch | Owned matches are stable host values; a borrowed batch is valid only inside its documented callback or lease interval. |
string, ReadOnlySpan<byte>, and ReadOnlySpan<ulong> select Unicode, byte, and token domains. Empty terms, embedded zero bytes, non-ASCII text, and the full
unsigned 64-bit identifier range are represented explicitly; no facade may use
a sentinel value that removes a valid input from the domain.
Facade symbol index
This table is generated from the same exhaustive model as the binding conformance gate. A public symbol may implement several ABI operations when the host language expresses domain or lifecycle choices with overloads, variants, protocols, or methods.
| Public symbol | Backing native operation(s) | Capability |
|---|---|---|
Distance.Damerau |
llev_damerau_distance, llev_damerau_distance_threshold |
standalone exact or thresholded distance |
Distance.Levenshtein |
llev_distance, llev_distance_threshold |
standalone exact or thresholded distance |
Distance.TrueDamerau |
llev_true_damerau_distance, llev_true_damerau_distance_threshold |
standalone true-Damerau distance |
LiblevenshteinException |
llev_last_error_message |
typed failure diagnostics |
PhoneticPattern.CompileLlre |
llev_phonetic_pattern_compile_llre |
compiled phonetic-pattern lifecycle and matching |
PhoneticPattern.CompileRegex |
llev_phonetic_pattern_compile_regex |
compiled phonetic-pattern lifecycle and matching |
PhoneticPattern.Dispose |
llev_phonetic_pattern_free |
compiled phonetic-pattern lifecycle and matching |
PhoneticPattern.Matches |
llev_phonetic_pattern_matches |
compiled phonetic-pattern lifecycle and matching |
PhoneticPattern.Size |
llev_phonetic_pattern_size |
compiled phonetic-pattern lifecycle and matching |
PhoneticRuleSet.Apply |
llev_owned_string_free, llev_phonetic_rules_apply |
owned result-string release; phonetic rule-set lifecycle and rewriting |
PhoneticRuleSet.Builtin |
llev_phonetic_rules_builtin |
phonetic rule-set lifecycle and rewriting |
PhoneticRuleSet.Count |
llev_phonetic_rules_len |
phonetic rule-set lifecycle and rewriting |
PhoneticRuleSet.Dispose |
llev_phonetic_rules_free |
phonetic rule-set lifecycle and rewriting |
PhoneticRuleSet.Parse |
llev_phonetic_rules_parse |
phonetic rule-set lifecycle and rewriting |
Query.Dispose |
llev_query_cursor_free |
streaming result traversal and batch leases |
Query.GetEnumerator |
llev_query_cursor_next_batch, llev_query_cursor_release_batch |
streaming result traversal and batch leases |
Transducer |
llev_transducer_new |
transducer lifecycle, snapshot, or domain metadata |
Transducer.Dispose |
llev_transducer_free |
transducer lifecycle, snapshot, or domain metadata |
Transducer.Query |
llev_transducer_query_utf8, llev_transducer_query_bytes, llev_transducer_query_u64, llev_transducer_query_pattern |
domain-preserving dictionary query; phonetic-pattern dictionary query |
Public types and traversal protocols
| Facade type or protocol | Purpose | Exposure note |
|---|---|---|
LiblevenshteinException.StatusCode |
Typed native status or error carrier | Public facade type |
Algorithm |
Edit-distance algorithm selection | Public facade type |
QueryOrder |
Result traversal ordering | Public facade type |
PhoneticRuleSetKind |
Built-in phonetic rule-set selection | Public facade type |
Query.GetEnumerator |
One-shot owned-result iteration | Public facade protocol |
Facade-encapsulated model values
| Model value | Idiomatic treatment |
|---|---|
reducer |
no public batch-reduction entry point; the safe iterator leases and materializes one bounded native batch at a time internally |
Native operations omitted from the public-symbol table are deliberately encapsulated by the facade. The generated completeness matrix records every such operation with its reviewed rationale; an unreasoned absence fails CI.
Intended usage paths
| Need | Use | Rationale |
|---|---|---|
| Repeated fuzzy queries | Reuse one transducer and create a fresh cursor per query | Construction retains a provider in constant time; each cursor captures its own immutable revision. |
| Ordinary streaming | The facade iterator protocol | It materializes bounded owned values and supports early termination with deterministic close. |
| Maximum result throughput | The facade batch/reducer protocol | It amortizes the foreign boundary and keeps borrowed views inside one lexical lease. |
| Repeated phonetic matching | Compile a phonetic pattern once, then query or match repeatedly | Compilation is separated from traversal and the compiled handle is immutable. |
| Repeated phonetic rewriting | Parse or select a rule set once, then apply it repeatedly | Rule validation and allocation are amortized while each returned string remains independently owned. |
| Cross-project dictionaries | Pass the retained dictionary resource directly | The versioned resource preserves snapshot identity without serialization or shared Rust layout. |
For the exhaustive native function contract—including exact preconditions,
returnable statuses, complexity, and thread-safety—use the
llev_* C ABI reference. The facade
source linked above is the authoritative idiomatic symbol inventory; its
exhaustive coverage is governed by bindings/api-surface-map.json and the generated completeness matrix.
Ownership, snapshots, and resource handoff
Use using/await using-style lexical ownership for disposable handles. SafeHandle protects exceptional paths but does not replace disposal.
A transducer retains the provider resource, and a query retains the revision visible at query start. Closing the original dictionary or publishing later mutations cannot invalidate that query. Acquisition either completes with one owned retain or fails with no ownership transfer. Teardown order is therefore free across dictionary, transducer, and completed query handles.
Borrowed results are intentionally lexical. Copy data that must outlive the callback; retaining a raw address, slice, memory segment, or foreign pointer is an API violation even when the next operation happens to reuse the same arena.
Errors and failure containment
Non-OK statuses become typed .NET exceptions with status and native diagnostic properties.
Malformed utf-8, unsupported unit domains, incompatible resource versions, closed handles, invalid bounds, allocation failures, provider faults, and contained rust panics are distinct failures. Never parse diagnostic prose to branch on an error: inspect the typed status/exception first and treat the message as human context. Diagnostics must be copied before another native call on the same thread.
Concurrency and reentrancy
Independent handles are safe across tasks. One enumerator is single-consumer; cancellation or early exit must dispose it to release the batch lease.
Snapshot capture is a linearization point, not a dictionary-wide query lock. First-party immutable snapshots can be walked concurrently. A foreign provider that does not advertise parallel callbacks is serialized at its callback gate; the host language must not add a weaker promise.
Performance and marshalling
- Reuse transducers for repeated queries against the same resource.
- Prefer streaming cursors to whole-result materialization.
- Prefer batch/reducer APIs when per-match boundary crossings dominate.
- Keep Unicode, byte, and token domains explicit to avoid transcoding.
- Measure native, WASM, and WASI paths independently; they have different startup and marshalling costs but identical query semantics.
No host wrapper should cache unbounded query results. Applications that add a memo use a revision key and a hard entry/weight bound; eviction may be approximate because all values remain derivable from the retained snapshot.
Security model
Treat a foreign resource provider and all user-controlled queries as untrusted inputs. Validate lengths before allocation, preserve paging bounds, reject unknown enum values, contain callbacks/panics at the boundary, and never trust capability flags until interface negotiation succeeds. The normative duties are in the binding trust model.
Compatibility and troubleshooting
The project ABI revision, family ABI version, interface identity/version, package version, and umbrella-runtime version are independent counters. Follow the ABI evolution policy; never infer compatibility from a package version alone.
When loading fails, check—in order—the documented runtime/toolchain version, CPU/OS artifact, native-access permission, loader search path, dependent interop package pin, and process-wide JavaScript runtime identity. When a query fails after construction, report the typed status and copied diagnostic before reducing the case to the smallest dictionary/query pair.
Maintainer checklist
- Update the machine-readable binding model before changing a public symbol.
- Regenerate headers/constants and the API coverage matrix.
- Extend the canonical executable example and negative-path tests.
- Run the language package, snapshot, leak, property, and cross-project suites.
- Verify package staging contains this guide and uses coherent sibling pins.
- Render diagrams headlessly and run the documentation/link/math gates.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- VinaryTree.Interop (>= 4.0.0-rc.5)
-
net8.0
- VinaryTree.Interop (>= 4.0.0-rc.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.0-rc.5 | 62 | 8/28/2026 |
| 4.0.0-rc.4 | 61 | 8/26/2026 |