diff --git a/CHANGELOG.md b/CHANGELOG.md index efa3452..a15e7db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to Celerity are documented here. This project follows [Keep ## [Unreleased] +### Added + +- `README.md` — new "Quick start" section with concrete, runnable usage examples for `IntDictionary`, `CelerityDictionary` (with `GuidHasher`, `StringFnV1AHasher`, `DefaultHasher`), the sets (`IntSet`, `CeleritySet`), and the `IEnumerable>` constructor. Covers indexer get/set, `TryAdd` / `Add` semantics, `TryGetValue`, removal, and bulk-load from a BCL `Dictionary<,>`. Closes the "Add usage examples to README" item from issue #15. + ## [1.1.2] - 2026-05-01 First successful 1.1.x publish. Tags `v1.1.0` and `v1.1.1` exist on the repository but never published to nuget.org: `v1.1.0` failed at deploy with HTTP 403 (NuGet API key had expired), and the follow-up `v1.1.1` failed with HTTP 401 because the trusted-publishing migration used the wrong NuGet account name (`marius-bughiu` instead of `marius.bughiu`). 1.1.2 is the same library code as the 1.1.0 tag plus the trusted-publishing migration with the correct user, shipped under a fresh version because the failed tags couldn't be cleanly recycled. diff --git a/README.md b/README.md index 906f576..1c29d8f 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,101 @@ Celerity is a .NET library that provides specialized high-performance collection All dictionaries implement `IReadOnlyDictionary` and ship allocation-free struct enumerators, `Keys` / `Values` views, and an `IEnumerable>` constructor. All collections handle `default(TKey)` (or zero for `int` / `long` keys, `null` for reference-type keys) out-of-band so it never collides with the empty-slot sentinel. +## Quick start + +Install from NuGet: + +```bash +dotnet add package Celerity.Collections +``` + +### `IntDictionary` — the int-keyed fast path + +`IntDictionary` defaults to `Int32WangNaiveHasher`, so most callers don't need to pick a hasher. + +```csharp +using Celerity.Collections; + +var counts = new IntDictionary(); +counts[42] = 1; +counts[42]++; // indexer get/set +counts.TryAdd(7, 100); // returns false if key already present, no overwrite +counts.Add(8, 200); // throws ArgumentException if key already present + +if (counts.TryGetValue(42, out var hits)) + Console.WriteLine(hits); // 2 + +counts.Remove(7); +Console.WriteLine(counts.Count); // 2 + +// foreach is allocation-free — Enumerator is a struct. +foreach (var kvp in counts) + Console.WriteLine($"{kvp.Key} -> {kvp.Value}"); +``` + +The zero key is a legitimate value, not the empty-slot sentinel — `counts[0] = 99` round-trips correctly. `LongDictionary` follows the exact same surface for `long` keys (defaulting to `Int64WangHasher`). + +### `CelerityDictionary` — generic keys with a struct hasher + +For non-`int`/`long` keys, pick a hasher from `Celerity.Hashing` (or supply your own). `DefaultHasher` falls back to `EqualityComparer.Default.GetHashCode()` for arbitrary types. + +```csharp +using Celerity.Collections; +using Celerity.Hashing; + +var byId = new CelerityDictionary(); +byId[Guid.NewGuid()] = "alice"; + +var byName = new CelerityDictionary(); +byName["bob"] = 1; + +// DefaultHasher works for any type but pays the EqualityComparer dispatch. +var byKey = new CelerityDictionary>(); +byKey[DateOnly.FromDateTime(DateTime.UtcNow)] = "today"; +``` + +The hasher is a `struct` and is supplied as a generic constraint, so the JIT devirtualizes and inlines the `Hash()` call on the probe path. + +### Sets + +`IntSet` and `CeleritySet` mirror the dictionary types for membership-only workloads. + +```csharp +using Celerity.Collections; +using Celerity.Hashing; + +var seen = new IntSet(); +seen.Add(1); +seen.Add(2); +Console.WriteLine(seen.Contains(1)); // true +seen.Remove(2); + +var visitedIds = new CeleritySet(); +visitedIds.TryAdd(Guid.NewGuid()); // returns true on first add, false on duplicate +``` + +### Construct from an existing collection + +The dictionaries accept any `IEnumerable>`. When the source implements `ICollection`, its `Count` is used to pre-size the backing storage so the bulk fill avoids resize work. + +```csharp +var bcl = new Dictionary { [1] = "a", [2] = "b", [3] = "c" }; +var fast = new IntDictionary(bcl); + +var fromKvps = new CelerityDictionary( + new[] + { + new KeyValuePair("alice", 1), + new KeyValuePair("bob", 2), + }); +``` + +Duplicate keys (including duplicate `default(TKey)` / zero-key entries) throw `ArgumentException`, matching BCL `Dictionary<,>` semantics. + +### Custom hasher + +Implement `IHashProvider` as a `struct` to plug in your own hash function. See [Custom hashing](#custom-hashing) below for the contract and a worked example. + ## Benchmarks #### CelerityDictionary