Documentation
¶
Overview ¶
Package containers holds small generic in-memory containers:
- BinarySearchGrowingKV — a sorted-iteration key-value container over parallel slices with deferred-batch writes, range reads and a read-free BinarySearchGrowingKVBuilder. Preferred over map[K]V when iteration must be deterministic and sorted, when K is not comparable, or when a custom comparator is needed.
- HashSet — a map-backed set with in-place set algebra (UnionMod, DifferenceMod, IntersectMod) and Clone for the non-destructive forms.
- Stack — a slice-backed LIFO stack.
None of the types is safe for concurrent use; callers serialise externally.
Subpackage co operates on co-indexed parallel slices and panics on length mismatches; subpackage ragged zips length-mismatched inputs by stopping at the shorter side.
Index ¶
- Variables
- func IterateMergedBinarySearchGrowingKVKeys[K any, V any, W any](a *BinarySearchGrowingKV[K, V], b *BinarySearchGrowingKV[K, W]) iter.Seq[K]
- func IterateSortedUniqueFuncUnique[T any](s1 []T, s2 []T, compare func(a, b T) int) iter.Seq[T]
- func IterateSortedUniqueOrderedUnique[T cmp.Ordered](s1 []T, s2 []T) iter.Seq[T]
- type BinarySearchGrowingKV
- func NewBinarySearchGrowingKV[K any, V any](estSize int, cmpKey func(a K, b K) int) (inst *BinarySearchGrowingKV[K, V])
- func NewBinarySearchGrowingKVFromAnyMap(m map[string]any) (kv *BinarySearchGrowingKV[string, any])
- func NewBinarySearchGrowingKVOrdered[K cmp.Ordered, V any](estSize int) (inst *BinarySearchGrowingKV[K, V])
- func (inst *BinarySearchGrowingKV[K, V]) Delete(key K) (existed bool)
- func (inst *BinarySearchGrowingKV[K, V]) Get(key K) (val V, has bool)
- func (inst *BinarySearchGrowingKV[K, V]) GetDefault(key K, defaultV V) (val V)
- func (inst *BinarySearchGrowingKV[K, V]) Grow(n int)
- func (inst *BinarySearchGrowingKV[K, V]) Has(key K) (has bool)
- func (inst *BinarySearchGrowingKV[K, V]) IsEmpty() bool
- func (inst *BinarySearchGrowingKV[K, V]) IterateFrom(lo K) iter.Seq2[K, V]
- func (inst *BinarySearchGrowingKV[K, V]) IterateKeys() iter.Seq[K]
- func (inst *BinarySearchGrowingKV[K, V]) IteratePairs() iter.Seq2[K, V]
- func (inst *BinarySearchGrowingKV[K, V]) IterateRange(lo K, hi K) iter.Seq2[K, V]
- func (inst *BinarySearchGrowingKV[K, V]) IterateValues() iter.Seq[V]
- func (inst *BinarySearchGrowingKV[K, V]) Len() int
- func (inst *BinarySearchGrowingKV[K, V]) MergeValue(key K, val V, merge func(old V, new V) V) (existed bool)
- func (inst *BinarySearchGrowingKV[K, V]) Reset()
- func (inst *BinarySearchGrowingKV[K, V]) UpsertBatch(key K, val V)
- func (inst *BinarySearchGrowingKV[K, V]) UpsertSingle(key K, val V) (existed bool)
- type BinarySearchGrowingKVBuilder
- type HashSet
- func (inst *HashSet[T]) Add(val T)
- func (inst *HashSet[T]) AddEx(val T) (existed bool)
- func (inst *HashSet[T]) AddExMany(vals iter.Seq[T]) (existing int, nonExisting int)
- func (inst *HashSet[T]) AddMany(vals iter.Seq[T]) (added int)
- func (inst *HashSet[T]) Clear()
- func (inst *HashSet[T]) Clone() *HashSet[T]
- func (inst *HashSet[T]) DifferenceMod(other *HashSet[T])
- func (inst *HashSet[T]) Equal(other *HashSet[T]) bool
- func (inst *HashSet[T]) Has(val T) bool
- func (inst *HashSet[T]) IntersectMod(other *HashSet[T])
- func (inst *HashSet[T]) IsEmpty() bool
- func (inst *HashSet[T]) IterateAll() iter.Seq[T]
- func (inst *HashSet[T]) Remove(val T)
- func (inst *HashSet[T]) RemoveEx(val T) (had bool)
- func (inst *HashSet[T]) Size() int
- func (inst *HashSet[T]) Slice() []T
- func (inst *HashSet[T]) SliceEx(in []T) (out []T)
- func (inst *HashSet[T]) UnionMod(other *HashSet[T])
- type Stack
- func (inst *Stack[T]) Depth() int
- func (inst *Stack[T]) Items() []T
- func (inst *Stack[T]) Peek() (retr T, err error)
- func (inst *Stack[T]) PeekDefault(emptyValue T) (retr T)
- func (inst *Stack[T]) Pop() (retr T, err error)
- func (inst *Stack[T]) PopDefault(emptyValue T) (retr T)
- func (inst *Stack[T]) Push(value T)
- func (inst *Stack[T]) Reset()
- func (inst *Stack[T]) Swap(newValue T) (oldValue T, err error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var PackageProps = packageprops.Props{ WASMWASI: packageprops.WASMCompiles, WASMJS: packageprops.WASMCompiles, WASMFreestanding: packageprops.WASMCompiles, }
PackageProps records this package's curated properties (ADR-0080). Seeded by `boxer code analysis golang wasmsurvey props generate`; curate by hand. The same group's `props verify` reconciles it.
Functions ¶
func IterateMergedBinarySearchGrowingKVKeys ¶
func IterateMergedBinarySearchGrowingKVKeys[K any, V any, W any](a *BinarySearchGrowingKV[K, V], b *BinarySearchGrowingKV[K, W]) iter.Seq[K]
IterateMergedBinarySearchGrowingKVKeys yields the union of both containers' keys in ascending order, each key once (a's spelling wins on ties). Both containers must be non-nil and must sort equivalently under a's comparator — the merge walks both key slices with a.cmpKey, so containers built with incompatible comparators produce meaningless output. Flush semantics as in BinarySearchGrowingKV.IterateKeys.
func IterateSortedUniqueFuncUnique ¶
IterateSortedUniqueFuncUnique merges two slices into one ascending sequence with cross-slice duplicates collapsed (s1's element wins on ties). Precondition — the "SortedUnique" in the name: each input must already be sorted under compare and free of internal duplicates; duplicates within one slice pass through undeduplicated.
func IterateSortedUniqueOrderedUnique ¶
IterateSortedUniqueOrderedUnique is IterateSortedUniqueFuncUnique with the natural cmp.Compare ordering.
Types ¶
type BinarySearchGrowingKV ¶
BinarySearchGrowingKV is a sorted-iteration key-value container backed by two parallel slices ([]K, []V). Reads are O(log N) binary search; iteration is in cmpKey-ascending order with cache-friendly sequential access. Writes come in two flavours with very different cost profiles — see UpsertSingle and UpsertBatch.
The container internally tracks a flushed flag (sorted + compacted). Every read entry point (Has, Get, GetDefault, Len, IterateKeys, IterateValues, IteratePairs, IterateFrom, IterateRange, MergeValue, UpsertSingle, Delete) transparently invokes ensureSorted before operating — the Iterate* methods at the start of each range — so callers never need to flush manually. UpsertBatch is the only writer that defers — see its docstring for the full cost model, invariants, and antipatterns.
Iteration order is deterministic across runs and across the choice of UpsertSingle vs UpsertBatch: cmpKey-ascending. Among equal-cmpKey entries the newest value wins while the first-inserted key spelling is retained (relevant only for comparators that treat distinguishable keys as equal).
Not safe for concurrent use. Even pure reads mutate the flushed flag via ensureSorted, so readers and writers must serialise externally.
A nil *BinarySearchGrowingKV is a valid empty container for reads: IsEmpty, Len, Has, Get, GetDefault and the Iterate* methods return zero values or empty sequences on a nil receiver. Write methods (UpsertSingle, UpsertBatch, MergeValue, Delete, Grow, Reset) panic on nil. This makes the nil early-out of NewBinarySearchGrowingKVFromAnyMap safe to hand to read-only consumers.
Compared to map[K]V: this container is preferred when iteration must be deterministic and sorted, when K is not comparable (so map[K]V is not an option), or when a custom cmpKey (case-insensitive, locale, byte-slice) is needed. For point lookups it is typically several times slower than map; for full iteration it is typically an order of magnitude faster. See binarysearchkv_bench_test.go for measured break-evens on string keys.
The point-lookup methods (Has, Get, GetDefault, Delete) dispatch through the bsearch field, set at construction time. NewBinarySearchGrowingKVOrdered stores a closure that calls slices.BinarySearch — cmp.Compare is then inlined into the search loop and the per-comparison indirect-call cost disappears. NewBinarySearchGrowingKV (general path) stores a closure that calls slices.BinarySearchFunc with the supplied cmpKey, paying the indirect call per comparison. Construction-time dispatch keeps the public API identical across both flavours.
func NewBinarySearchGrowingKV ¶
func NewBinarySearchGrowingKV[K any, V any](estSize int, cmpKey func(a K, b K) int) (inst *BinarySearchGrowingKV[K, V])
NewBinarySearchGrowingKV constructs a container with a caller-supplied comparator. Lookup methods (Has, Get, GetDefault, Delete) go through a closure that calls slices.BinarySearchFunc with the supplied cmpKey — the per-comparison cost is one indirect call. Prefer NewBinarySearchGrowingKVOrdered when K satisfies cmp.Ordered, which inlines the comparator and is measurably faster on the lookup hot path.
func NewBinarySearchGrowingKVFromAnyMap ¶
func NewBinarySearchGrowingKVFromAnyMap(m map[string]any) (kv *BinarySearchGrowingKV[string, any])
NewBinarySearchGrowingKVFromAnyMap converts a dynamically-typed map (typically produced by YAML or JSON decoding) into a key-sorted BinarySearchGrowingKV.
The conversion is recursive: nested map[string]any and map[any]any values are themselves converted into nested BinarySearchGrowingKV[string, any] values; []any sequences are walked and any maps inside them are converted too. Other values (string, int, bool, nil, …) pass through unchanged.
Returns nil if m is nil or empty so callers can early-out on `if kv == nil`; the nil result is itself safe for all read methods, which treat a nil receiver as an empty container (relevant for nested empty maps, whose converted value is a typed-nil *BinarySearchGrowingKV inside an interface). The bulk path uses BinarySearchGrowingKV.UpsertBatch + a single flush on first read, which is O(N log N) instead of UpsertSingle's O(N²).
yaml.v2 sometimes produces map[any]any for nested maps; non-string keys are stringified with fmt.Sprintf("%v", k) to match the renderer behaviour in boxer/public/semistructured/markdown/obsidian/frontmatter.go. Distinct keys can stringify to the same string (42 and "42" both become "42"); exactly one entry survives such a collision, and which one is unspecified — it follows Go's randomised map iteration order.
func NewBinarySearchGrowingKVOrdered ¶
func NewBinarySearchGrowingKVOrdered[K cmp.Ordered, V any](estSize int) (inst *BinarySearchGrowingKV[K, V])
NewBinarySearchGrowingKVOrdered constructs a container for keys satisfying cmp.Ordered. Lookup methods (Has, Get, GetDefault, Delete) dispatch through a closure that calls slices.BinarySearch — cmp.Compare is then inlined into the search loop, saving one indirect call per comparison. Typically 1.4×–3.5× faster than NewBinarySearchGrowingKV for Get on string keys; see binarysearchkv_bench_test.go for measured numbers across N.
func (*BinarySearchGrowingKV[K, V]) Delete ¶
func (inst *BinarySearchGrowingKV[K, V]) Delete(key K) (existed bool)
Delete removes the entry for key. Returns true when an entry was present (and removed), false when key was not in the container. O(log N) lookup + O(N) shift; sorted/compacted invariants are preserved. slices.Delete zeros the trailing slot before truncating so pointer values don't leak past their entry's lifetime.
func (*BinarySearchGrowingKV[K, V]) Get ¶
func (inst *BinarySearchGrowingKV[K, V]) Get(key K) (val V, has bool)
func (*BinarySearchGrowingKV[K, V]) GetDefault ¶
func (inst *BinarySearchGrowingKV[K, V]) GetDefault(key K, defaultV V) (val V)
func (*BinarySearchGrowingKV[K, V]) Grow ¶
func (inst *BinarySearchGrowingKV[K, V]) Grow(n int)
func (*BinarySearchGrowingKV[K, V]) Has ¶
func (inst *BinarySearchGrowingKV[K, V]) Has(key K) (has bool)
func (*BinarySearchGrowingKV[K, V]) IsEmpty ¶
func (inst *BinarySearchGrowingKV[K, V]) IsEmpty() bool
IsEmpty reports whether the container holds zero entries. It does not flush deferred UpsertBatch state because compaction can only remove duplicates, never reduce a non-empty slice to empty: if len(inst.keys) is zero the container is genuinely empty; if non-zero, at least one entry survives any pending compaction.
func (*BinarySearchGrowingKV[K, V]) IterateFrom ¶ added in v0.0.12
func (inst *BinarySearchGrowingKV[K, V]) IterateFrom(lo K) iter.Seq2[K, V]
IterateFrom yields (key, value) pairs in cmpKey-ascending order, starting at the first key not less than lo (under cmpKey). Flush semantics as in BinarySearchGrowingKV.IterateKeys: the deferred state is flushed and the start position located when ranging begins.
func (*BinarySearchGrowingKV[K, V]) IterateKeys ¶
func (inst *BinarySearchGrowingKV[K, V]) IterateKeys() iter.Seq[K]
IterateKeys yields the keys in cmpKey-ascending order. The container's deferred UpsertBatch state is flushed when ranging begins, not when IterateKeys is called, so a Seq obtained earlier always iterates the current (sorted, compacted) view. Mutating the container while a range is in progress remains undefined behaviour.
func (*BinarySearchGrowingKV[K, V]) IteratePairs ¶
func (inst *BinarySearchGrowingKV[K, V]) IteratePairs() iter.Seq2[K, V]
IteratePairs yields (key, value) pairs in cmpKey-ascending order. Flush semantics as in BinarySearchGrowingKV.IterateKeys.
func (*BinarySearchGrowingKV[K, V]) IterateRange ¶ added in v0.0.12
func (inst *BinarySearchGrowingKV[K, V]) IterateRange(lo K, hi K) iter.Seq2[K, V]
IterateRange yields (key, value) pairs in cmpKey-ascending order over the half-open interval [lo, hi): keys k with cmpKey(k, lo) >= 0 and cmpKey(k, hi) < 0. When cmpKey(lo, hi) >= 0 the range is empty. Flush semantics as in BinarySearchGrowingKV.IterateKeys.
func (*BinarySearchGrowingKV[K, V]) IterateValues ¶
func (inst *BinarySearchGrowingKV[K, V]) IterateValues() iter.Seq[V]
IterateValues yields the values in cmpKey-ascending key order. Flush semantics as in BinarySearchGrowingKV.IterateKeys.
func (*BinarySearchGrowingKV[K, V]) Len ¶
func (inst *BinarySearchGrowingKV[K, V]) Len() int
Len returns the number of unique entries. It forces ensureSorted so that pending UpsertBatch state is flushed and the count reflects the post-compaction unique-entry count, not the raw appended-item count. Without this flush, an UpsertBatch sequence with duplicate keys would over-report by the number of shadowed duplicates.
func (*BinarySearchGrowingKV[K, V]) MergeValue ¶
func (inst *BinarySearchGrowingKV[K, V]) MergeValue(key K, val V, merge func(old V, new V) V) (existed bool)
MergeValue combines an incoming value with the resident one: when key is present, the stored value becomes merge(old, val) — old is the resident value, val the incoming one — and the resident key spelling is kept; when absent, (key, val) is inserted without calling merge. Returns whether the key was already present. Any deferred UpsertBatch state is flushed first, so merge sees the compacted (newest) resident value. O(log N) lookup + O(N) shift on insert.
func (*BinarySearchGrowingKV[K, V]) Reset ¶
func (inst *BinarySearchGrowingKV[K, V]) Reset()
func (*BinarySearchGrowingKV[K, V]) UpsertBatch ¶
func (inst *BinarySearchGrowingKV[K, V]) UpsertBatch(key K, val V)
UpsertBatch stages a (key, value) pair on a deferred append buffer. The container is *not* sorted or deduplicated at this point — both are postponed until the next read (Has, Get, GetDefault, Len, IterateKeys, IterateValues, IteratePairs, MergeValue, UpsertSingle, Delete), which triggers ensureSorted transparently.
Invariants ¶
- Per call: appends one entry to each backing slice and flips the sorted / compacted flags to false. No comparison, no shift, no binary search.
- On flush: sort.Stable orders entries by cmpKey-ascending, then compactNewestWins collapses each equal-key run to a single surviving entry whose value is the most recent UpsertBatch call and whose key is the run's first-inserted spelling. "Newest value wins" relies on sort.Stable preserving insertion order among equal keys; keeping the first key spelling matches UpsertSingle's replace-in-place behaviour.
- After flush, the container is in the same observable state as if the equivalent UpsertSingle sequence had been issued: same final entries, same iteration order, same Get results.
Cost model ¶
- Per call: O(1) plus the occasional growslice when the append exceeds capacity. No comparison, no allocation in the steady state.
- First read after N batched calls: O(N log N) sort.Stable + O(N) compaction pass, where N is the *total* number of UpsertBatch calls since the last flush, not the final unique-entry count.
- Subsequent reads are free until the next write.
When UpsertBatch wins ¶
- Bulk load into a large container (typically N ≳ 3000 unique keys on string-keyed workloads) with reads happening once at the end. The deferred sort amortises the O(N²) cumulative shift cost that a UpsertSingle loop would pay.
- Adversarial insert order — e.g. reverse-sorted or repeatedly at position 0 — where every UpsertSingle would pay an O(N) shift. UpsertBatch's append is O(1) regardless of insertion position.
- Per-call latency smoothing in hot writer loops, where the worst- case O(N) shift of UpsertSingle is unacceptable jitter and the reader can tolerate a deferred sort.
When UpsertBatch loses (counter to the name) ¶
- Heavy-duplicate workloads (the same key reinserted many times). UpsertSingle replaces in place after the first occurrence and so never grows past the unique count; UpsertBatch sorts and discards duplicates only at flush time. Measured 2–3× slower and up to 50× more memory on duplicate-heavy batches in this package's bench.
- Mid-size N (~10 to ~2000 unique keys with random insert order). The per-call shift cost of UpsertSingle is small enough that the deferred sort+compact has worse constants in absolute terms.
- Workloads that interleave reads with writes. Every read calls ensureSorted, so a Has-gated UpsertBatch loop pays N sort costs instead of one. See the antipatterns section below.
Antipatterns ¶
Has/Get/IteratePairs/Len inside an UpsertBatch loop. Each read forces a sort+compact, defeating the deferred-sort optimisation and producing the worst-of-both-worlds cost profile. If duplicate suppression is needed during build, use UpsertSingle (which idempotently replaces) or maintain an external seen-set.
Free mixing of UpsertSingle and UpsertBatch. Each transition pays the flush cost. Choose one strategy per build phase.
Calling UpsertBatch during iteration of the same container. The iterator reads the slice headers when ranging begins; the append may grow-and-relocate the underlying array mid-iteration, leaving the loop walking stale storage. (Obtaining an iterator, mutating, and only then ranging is safe — the deferred state is flushed when ranging begins, not when the Iterate method is called.)
Sizing ¶
The estSize hint passed to NewBinarySearchGrowingKV / Ordered should be the expected *total* number of UpsertBatch calls, not the final unique count. Under-sizing causes growslice to reallocate the deferred buffer multiple times — measured at this package's bench as roughly 4× memory blowup and a 10–15% slowdown at N=4096 when sized for the unique count vs the total. After flush, the compacted slices may shrink below the high-water mark, but the hint controls the peak working set.
func (*BinarySearchGrowingKV[K, V]) UpsertSingle ¶
func (inst *BinarySearchGrowingKV[K, V]) UpsertSingle(key K, val V) (existed bool)
UpsertSingle inserts or replaces the entry for key, keeping the container sorted and compacted on the write path. Returns true if the key was already present (in which case the value is replaced in place with no shift), false if a fresh slot was opened. Cost: O(log N) binary search + O(N) shift on insert; O(log N) on in-place replace.
See UpsertBatch for the cost-model comparison and guidance on which write path to use for which workload.
type BinarySearchGrowingKVBuilder ¶
type BinarySearchGrowingKVBuilder[K any, V any] struct { // contains filtered or unexported fields }
BinarySearchGrowingKVBuilder accumulates (key, value) pairs in a write-only staging area, then produces a sorted-and-compacted BinarySearchGrowingKV via BinarySearchGrowingKVBuilder.Freeze.
The builder has no read methods (no Get, Has, IteratePairs). That is the point — the Has-gated-UpsertBatch antipattern documented on BinarySearchGrowingKV.UpsertBatch becomes a compile-time error rather than a silent N-fold perf regression. A build phase that genuinely needs to read the in-progress state is a build phase that should use BinarySearchGrowingKV.UpsertSingle instead.
Single-use: after Freeze, subsequent Stage / StageSeq / Freeze calls panic. Allocate a fresh builder for the next build phase.
Not safe for concurrent use.
Example ¶
ExampleBinarySearchGrowingKVBuilder shows the canonical build → freeze → read flow.
b := NewBinarySearchGrowingKVBuilderOrdered[string, int](4)
b.Stage("charlie", 3)
b.Stage("alpha", 1)
b.Stage("bravo", 2)
kv := b.Freeze()
for k, v := range kv.IteratePairs() {
fmt.Printf("%s=%d\n", k, v)
}
Output: alpha=1 bravo=2 charlie=3
Example (NewestWins) ¶
ExampleBinarySearchGrowingKVBuilder_newestWins shows that duplicate keys in the staging buffer collapse to the most recent value at Freeze time.
b := NewBinarySearchGrowingKVBuilderOrdered[string, string](4)
b.Stage("config", "v1")
b.Stage("config", "v2")
b.Stage("config", "v3")
kv := b.Freeze()
v, _ := kv.Get("config")
fmt.Println(v)
Output: v3
Example (StageSeq) ¶
ExampleBinarySearchGrowingKVBuilder_stageSeq shows bulk-staging from an existing map (or any iter.Seq2 source).
src := map[string]int{"z": 26, "a": 1, "m": 13}
b := NewBinarySearchGrowingKVBuilderOrdered[string, int](len(src))
b.StageSeq(maps.All(src))
kv := b.Freeze()
for k, v := range kv.IteratePairs() {
fmt.Printf("%s=%d\n", k, v)
}
Output: a=1 m=13 z=26
func NewBinarySearchGrowingKVBuilder ¶
func NewBinarySearchGrowingKVBuilder[K any, V any](estSize int, cmpKey func(a K, b K) int) *BinarySearchGrowingKVBuilder[K, V]
NewBinarySearchGrowingKVBuilder allocates a builder backed by slices of the given estimated capacity. The estimate should be the expected *total* number of Stage / StageSeq inserts (not the unique-key count), to avoid growslice reallocations during build. See BinarySearchGrowingKV.UpsertBatch's docstring for the rationale.
Prefer NewBinarySearchGrowingKVBuilderOrdered when K satisfies cmp.Ordered — the produced container's point-lookup methods will use an inlined comparator on the hot path. See NewBinarySearchGrowingKV for the cost comparison.
func NewBinarySearchGrowingKVBuilderOrdered ¶
func NewBinarySearchGrowingKVBuilderOrdered[K cmp.Ordered, V any](estSize int) *BinarySearchGrowingKVBuilder[K, V]
NewBinarySearchGrowingKVBuilderOrdered is the cmp.Ordered convenience variant. The produced container dispatches point-lookups (Has, Get, GetDefault, Delete) through an inlined-comparator binary search — see NewBinarySearchGrowingKVOrdered for the rationale.
func (*BinarySearchGrowingKVBuilder[K, V]) Freeze ¶
func (b *BinarySearchGrowingKVBuilder[K, V]) Freeze() *BinarySearchGrowingKV[K, V]
Freeze produces a sorted-and-compacted BinarySearchGrowingKV from the staged pairs and marks the builder as consumed. Subsequent Stage / StageSeq / Freeze calls panic.
Among duplicate keys, the value of the latest Stage call survives — same newest-wins semantics as BinarySearchGrowingKV.UpsertBatch.
The returned container owns the builder's backing slices (no copy). Freezing eagerly runs sort + compact so the returned container is already in the ready-for-read state; freeze cost is therefore O(N log N) on N = total staged inserts.
func (*BinarySearchGrowingKVBuilder[K, V]) Len ¶
func (b *BinarySearchGrowingKVBuilder[K, V]) Len() int
Len returns the raw count of staged inserts. Duplicate keys are not collapsed at this point, so the value can exceed Freeze().Len(). Useful for sizing decisions before Freeze.
After Freeze the value is meaningless: it still reports the raw staged count, but the backing slices belong to the frozen container, which has compacted them.
func (*BinarySearchGrowingKVBuilder[K, V]) Stage ¶
func (b *BinarySearchGrowingKVBuilder[K, V]) Stage(key K, val V)
Stage appends one (key, value) pair to the staging buffer. O(1) per call. Panics if Freeze has already been called.
func (*BinarySearchGrowingKVBuilder[K, V]) StageSeq ¶
func (b *BinarySearchGrowingKVBuilder[K, V]) StageSeq(pairs iter.Seq2[K, V])
StageSeq appends every (key, value) pair from the iterator. Useful when the source is already an iter.Seq2 (e.g. maps.All on a map, a zip of two slices, a filter pipeline). Panics if Freeze has already been called.
type HashSet ¶
type HashSet[T comparable] struct { // contains filtered or unexported fields }
func NewHashSet ¶
func NewHashSet[T comparable](estimatedCard int) *HashSet[T]
func (*HashSet[T]) AddMany ¶
AddMany inserts every value from the iterator and returns the number of values that were not already present. Duplicate values within vals count once.
func (*HashSet[T]) Clone ¶ added in v0.0.12
Clone returns an independent copy of the set: mutations of the clone do not affect the original and vice versa. Together with the *Mod set operations it enables non-destructive set algebra (a.Clone().IntersectMod(b)).
func (*HashSet[T]) DifferenceMod ¶
func (*HashSet[T]) Equal ¶
Equal reports whether both sets hold exactly the same elements. other must be non-nil.
func (*HashSet[T]) IntersectMod ¶
func (*HashSet[T]) IterateAll ¶
type Stack ¶
type Stack[T any] struct { // contains filtered or unexported fields }
Stack is a slice-backed LIFO stack. The zero value is an empty, usable stack; NewStack / NewStackSized pre-size the backing storage. Vacated slots are cleared on Pop/PopDefault/Reset so pointer-valued elements don't keep their referents reachable past their stack lifetime. Not safe for concurrent use.
func NewStackSized ¶
func (*Stack[T]) Items ¶
func (inst *Stack[T]) Items() []T
Items returns the backing slice, bottom of the stack first, as a read-only view for diagnostics and logging. It stays valid only until the next mutating call, and writing through it bypasses the stack's slot-clearing — use Push/Pop/Swap to mutate.
func (*Stack[T]) Peek ¶
Peek returns the top element without removing it. Errors on an empty stack; PeekDefault is the non-error variant.
func (*Stack[T]) PeekDefault ¶
func (inst *Stack[T]) PeekDefault(emptyValue T) (retr T)
PeekDefault returns the top element without removing it, or emptyValue when the stack is empty.
func (*Stack[T]) Pop ¶
Pop removes and returns the top element. Errors on an empty stack; PopDefault is the non-error variant.
func (*Stack[T]) PopDefault ¶
func (inst *Stack[T]) PopDefault(emptyValue T) (retr T)
PopDefault removes and returns the top element, or emptyValue when the stack is empty.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package co provides operations over co-indexed parallel slices — separate slices whose elements correspond by position (a struct-of-arrays layout): sorting a lead slice while keeping companions aligned (CoSortSlices), sorted insertion and merge over a key slice plus a value slice, grouped iteration over key runs, and co-filtered iteration.
|
Package co provides operations over co-indexed parallel slices — separate slices whose elements correspond by position (a struct-of-arrays layout): sorting a lead slice while keeping companions aligned (CoSortSlices), sorted insertion and merge over a key slice plus a value slice, grouped iteration over key runs, and co-filtered iteration. |
|
Package ragged provides zip iterators that tolerate length-mismatched ("ragged") inputs by stopping at the shorter side: Zip2 for two slices, and Zip2L, Zip2R, Zip2LR where the suffix letters mark which operand positions are lazy (iter.Seq-valued).
|
Package ragged provides zip iterators that tolerate length-mismatched ("ragged") inputs by stopping at the shorter side: Zip2 for two slices, and Zip2L, Zip2R, Zip2LR where the suffix letters mark which operand positions are lazy (iter.Seq-valued). |