Uh oh!
There was an error while loading. Please reload this page.
fix: honor allocation limits at boundary cases - #2
Conversation
b94b8c3 to
1e28061Compare
kolkov
left a comment
There was a problem hiding this comment.
@besmpl — thorough fix. Validated locally:
Bug confirmed:New(1024, 1).Allocate(1) panics on main — insertNodeIntoBin crashes because the initial free region consumed the only allocation node.
Reference checked: The original C++ OffsetAllocator has the same bug — allocates exactly maxAllocs nodes (line 184), and the initial free region at line 195 consumes one, leaving zero usable slots when maxAllocs=1. Your fix addresses a bug in the upstream reference implementation itself.
Validation:
| Check | Result |
|---|---|
go build ./... | ✅ |
go test ./... -count=1 | ✅ all pass |
go vet ./... | ✅ |
golangci-lint run --timeout=5m | ✅ 0 issues |
| Math proof (2K nodes for K allocs + K+1 free regions) | ✅ sound |
Overflow guard size > NoSpace-(alignment-1) | ✅ prevents uint32 wrap |
| No API break | ✅ |
Approving. Will merge after CI passes.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Uh oh!
There was an error while loading. Please reload this page.
Summary
maxAllocscount even when allocations split free regionsuint32Why
The initial free region used one of the advertised allocation nodes. As a result,
New(1024, 1).Allocate(1)attempted to create a remainder through the freelist sentinel and panicked instead of returning the single supported allocation. Fragmented layouts could similarly exhaust structural nodes before reachingmaxAllocs.Free regions are never adjacent after coalescing, so with
Kactive allocations there are at mostK+1free regions. A preallocated2*maxAllocsnode pool (or one initial node for a zero-capacity allocator) therefore covers every reachable layout while the new active count enforces the public limit.Separately,
AllocateAlignedpreviously allowedsize + alignment - 1to wrap, potentially returning a tiny successful allocation for a near-MaxUint32request.Verification
go test ./...go test -race ./...go build ./...go vet ./...golangci-lint run --timeout=5mgo test -covermode=atomic -coverprofile=coverage.out ./...— 99.5% total statement coverage and 100% of changed production statements (42/42; no uncovered changed blocks)0 B/op,0 allocs/opgofmtandgit diff --check