11 Commits

Author SHA1 Message Date
572e59f33d Merge remote-tracking branch 'origin' into feat/get-no-error
All checks were successful
CI / Check PR Title (pull_request) Successful in 29s
CI / Go Lint (pull_request) Successful in 41s
CI / Makefile Lint (pull_request) Successful in 37s
CI / Markdown Lint (pull_request) Successful in 32s
CI / Unit Tests (pull_request) Successful in 39s
CI / Fuzz Tests (pull_request) Successful in 1m37s
CI / Mutation Tests (pull_request) Successful in 1m19s
2026-04-13 21:36:59 -04:00
867a1d49df feat: sentinel error ErrBadHash (#19)
All checks were successful
CI / Check PR Title (push) Has been skipped
CI / Makefile Lint (push) Successful in 1m4s
CI / Markdown Lint (push) Successful in 32s
CI / Go Lint (push) Successful in 1m15s
CI / Unit Tests (push) Successful in 38s
CI / Fuzz Tests (push) Successful in 1m34s
CI / Mutation Tests (push) Successful in 1m31s
## Description

Currently, the errors are not sentinel, and so are hard to test for. This PR makes sure hash collision errors are accounted for.

## Changes

- Add `ErrBadHash`. Happens when there are too many collisions for an item to be added.

### Design Decisions

- Chose to name `ErrBadHash` over `ErrCycle` because the feedbach that the user should be given is to evaluate their hash functions. Cycle collision is a bit esoteric.

## Checklist

- [x] Tests pass
- [x] Docs updated

Reviewed-on: #19
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-04-14 00:38:11 +00:00
95687acade feat!: update contract for 'Table.Get()'
All checks were successful
CI / Check PR Title (pull_request) Successful in 19s
CI / Go Lint (pull_request) Successful in 42s
CI / Markdown Lint (pull_request) Successful in 22s
CI / Makefile Lint (pull_request) Successful in 40s
CI / Unit Tests (pull_request) Successful in 37s
CI / Fuzz Tests (pull_request) Successful in 1m12s
CI / Mutation Tests (pull_request) Successful in 1m18s
2026-04-03 21:27:48 +02:00
322d71f0be refactor!: remove MinimumLoad() option (#17)
All checks were successful
CI / Check PR Title (push) Has been skipped
CI / Go Lint (push) Successful in 38s
CI / Makefile Lint (push) Successful in 36s
CI / Markdown Lint (push) Successful in 21s
CI / Unit Tests (push) Successful in 37s
CI / Fuzz Tests (push) Successful in 1m9s
CI / Mutation Tests (push) Successful in 1m18s
## Description

The `cuckoo.MinimumLoad()` option was not a very useful option, and prone to error. By removing the ability to modify it, and setting it to something reasonable (like 5%), we can remove a whole set of errors that the user may stumble into.

## Changes

- Remove `MinimumLoad()` option.
- Privated `DefaultMinimumLoad`.

### Design Decisions

- `DefaultMinimumLoad` should be privated because it is no longer an option. The user should not need to interact with it.

## Checklist

- [x] Tests pass
- [x] Docs updated

Reviewed-on: #17
2026-04-03 14:51:41 +00:00
ed30a4fc7c fix: check-pr-title job has prompt injection (#18)
All checks were successful
CI / Check PR Title (push) Has been skipped
CI / Go Lint (push) Successful in 36s
CI / Makefile Lint (push) Successful in 35s
CI / Markdown Lint (push) Successful in 22s
CI / Unit Tests (push) Successful in 35s
CI / Fuzz Tests (push) Successful in 1m6s
CI / Mutation Tests (push) Successful in 1m10s
## Description

Currently, the `check-pr-title` job has a security vulnerability. If you give the PR a bad title, the job can run arbitrary code.

## Changes

- Fix prompt injection by pulling the PR title as an environment variable.
- Also, restricted the job to only `pull_request` trigger.

### Design Decisions

- It is better to pull out this job into a separate workflow with a unique trigger, but I chose not to because it is currently only one job.

## Checklist

- [x] Tests pass
- [x] Docs updated

Reviewed-on: #18
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-04-03 14:47:01 +00:00
c834f004a2 chore: no free form issues, 'description'-s to 'placeholders'-s (#16)
Some checks failed
CI / Check PR Title (push) Failing after 19s
CI / Makefile Lint (push) Successful in 34s
CI / Go Lint (push) Successful in 37s
CI / Markdown Lint (push) Successful in 35s
CI / Unit Tests (push) Successful in 33s
CI / Fuzz Tests (push) Successful in 1m4s
CI / Mutation Tests (push) Successful in 2m55s
## Description

There were some problems with the roll-out of the issue templates. This PR addresses them.

## Changes

- Disable `blank-issue-enabled`.
- Move all `description` types in the issue templates to `placeholder`.

### Design Decisions

- The `description` fields take up too much space.

## Checklist

- [x] Tests pass
- [x] Docs updated

Reviewed-on: #16
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-04-03 13:57:03 +00:00
581f26f562 ci: support semantic versioning, conventional commits (#15)
Some checks failed
CI / Check PR Title (push) Failing after 18s
CI / Makefile Lint (push) Successful in 33s
CI / Markdown Lint (push) Successful in 22s
CI / Unit Tests (push) Successful in 30s
CI / Go Lint (push) Successful in 45s
CI / Mutation Tests (push) Successful in 1m18s
CI / Fuzz Tests (push) Successful in 1m48s
## Description

Currently, the repository is not well suited for semantic versioning, and hasn't much to support it. This PR adds templates, CI jobs, and configs to simplify its adoption.

## Changes

- Added `FEATURE` and `BUG` issue templates. Also, forbids free-form issues.
- Adds a PR template.
- Adds a CI job to ensure the commit title follows conventional commits.

### Design Decisions

N/A.

## Checklist

- [x] Tests pass
- [x] Docs updated

Reviewed-on: #15
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-04-03 13:46:05 +00:00
b1ab3a6326 chore: remove ./.claude/settings.json (#14)
All checks were successful
CI / Go Lint (push) Successful in 29s
CI / Makefile Lint (push) Successful in 28s
CI / Markdown Lint (push) Successful in 16s
CI / Unit Tests (push) Successful in 28s
CI / Fuzz Tests (push) Successful in 59s
CI / Mutation Tests (push) Successful in 1m7s
There shouldn't be a global settings folder for Claude in the repository. Also, add final newline to `.gitignore`.

Reviewed-on: #14
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-03-28 19:52:06 +00:00
36d76d4929 feat: wider fuzz tests, stronger options. (#9)
All checks were successful
CI / Go Lint (push) Successful in 38s
CI / Markdown Lint (push) Successful in 12s
CI / Makefile Lint (push) Successful in 30s
CI / Unit Tests (push) Successful in 28s
CI / Fuzz Tests (push) Successful in 1m1s
CI / Mutation Tests (push) Successful in 57s
- Added all `Option`-s to the fuzz tests.
  - Minimum load is always <=20%.
- Tested all options, and force a panic on all invalid options.
  - Capacity must now be non-negative.
  - Minimum load should be <=20%, but just put it as a recommendation.

Reviewed-on: #9
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-03-25 01:26:56 +00:00
41b09ae680 fix: remove redundant test TestRemove (#8)
All checks were successful
CI / Makefile Lint (push) Successful in 24s
CI / Go Lint (push) Successful in 28s
CI / Markdown Lint (push) Successful in 20s
CI / Unit Tests (push) Successful in 23s
CI / Fuzz Tests (push) Successful in 54s
CI / Mutation Tests (push) Successful in 50s
This test offers the same functionality as `TestAddItem`.

Reviewed-on: #8
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-03-21 16:24:52 +00:00
e00e6fcb1b docs: add logo for project, expand readme (#7)
All checks were successful
CI / Makefile Lint (push) Successful in 22s
CI / Go Lint (push) Successful in 26s
CI / Markdown Lint (push) Successful in 9s
CI / Unit Tests (push) Successful in 21s
CI / Fuzz Tests (push) Successful in 53s
CI / Mutation Tests (push) Successful in 1m23s
Reviewed-on: #7
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-03-21 01:14:17 +00:00
17 changed files with 193 additions and 55 deletions

View File

@@ -0,0 +1,26 @@
# yaml-language-server: $schema=https://www.schemastore.org/gitea-issue-forms.json
name: 🐛 Bug Report
about: Report a bug in this project
title: "[BUG]: "
body:
- type: textarea
id: context
attributes:
label: Context
placeholder: What circumstances led to the bug?
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
placeholder: What did you expect would happen?
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: Actual Behavior
placeholder: What happened, and why was it unexpected?
validations:
required: true

View File

@@ -0,0 +1,37 @@
# 'Feature Request Template' By @cheehwatang
# https://github.com/cheehwatang/.github/blob/master/.github/ISSUE_TEMPLATE/feature_request.yml
#
# yaml-language-server: $schema=https://www.schemastore.org/gitea-issue-forms.json
name: ✨ Feature Request
about: Suggest an idea for this project
title: "[FEATURE]: "
body:
- type: dropdown
attributes:
multiple: false
label: Feature Type
options:
- "✨ New Feature"
- "📝 Documentation"
- "🎨 Style and UI"
- "🔨 Code Refactor"
- "⚡ Performance Improvements"
- "✅ New Test"
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
placeholder: |
Give us a brief description of the feature or enhancement you would
like!
validations:
required: true
- type: textarea
id: additional-information
attributes:
label: Additional Information
placeholder: |
Give us some additional information on the feature request like proposed
solutions, links, screenshots, etc.

View File

@@ -0,0 +1,2 @@
# yaml-language-server: $schema=https://www.schemastore.org/gitea-issue-config.json
blank_issues_enabled: false

View File

@@ -0,0 +1,17 @@
---
name: "New Pull Request"
about: "Standard PR template"
title: ""
ref: "main"
---
## Description
## Changes
### Design Decisions
## Checklist
- [ ] Tests pass
- [ ] Docs updated

View File

@@ -6,6 +6,19 @@ on:
pull_request:
jobs:
check-pr-title:
name: Check PR Title
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
env:
TITLE: ${{ gitea.event.pull_request.title }}
steps:
- run: |
if ! echo "$TITLE" | grep -qE '^(WIP: )?(feat|fix|docs|chore|ci|test|refactor|perf|build|style|revert)(\(.+\))?(!)?: .+'; then
echo "::error::Pull Request title must follow conventional commits"
exit 1
fi
lint-go:
name: Go Lint
runs-on: ubuntu-latest
@@ -39,6 +52,14 @@ jobs:
- name: Run mutation tests
run: make lint-makefile
lint-markdown:
name: Markdown Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v19
test-unit:
name: Unit Tests
runs-on: ubuntu-latest

1
.gitignore vendored
View File

@@ -24,4 +24,3 @@ go.work.sum
# env file
.env

17
.markdownlint.yml Normal file
View File

@@ -0,0 +1,17 @@
default: true
heading-style:
style: atx
ul-indent:
indent: 2
line-length: false
no-duplicate-heading:
siblings_only: true
no-inline-html:
allowed_elements:
- br
- details
- summary
- img
- picture
- source
first-line-heading: true

View File

@@ -1,4 +1,4 @@
.PHONY: all help install clean test-unit test-mutation test-fuzz test docs lint-go lint-makefile lint
.PHONY: all help install clean test-unit test-mutation test-fuzz test docs lint-go lint-makefile lint-markdown lint
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*##' $(MAKEFILE_LIST) | awk -F ':.*## ' '{printf " %-15s %s\n", $$1, $$2}'
@@ -32,7 +32,10 @@ lint-go: ## Lint Go code
lint-makefile: ## Lint the Makefile
checkmake Makefile
lint: lint-go lint-makefile ## Lint all code
lint-markdown: ## Lint Markdown files
docker run --rm -v $(CURDIR):/workdir davidanson/markdownlint-cli2 "**/*.md"
lint: lint-go lint-makefile lint-markdown ## Lint all code
docs: ## Serve godoc locally
@echo ">>> Visit: http://localhost:6060/pkg/$$(go list -m)"

View File

@@ -1,3 +1,3 @@
# go-cuckoo
# <img height="30" src="assets/logo.svg" alt="Go Cuckoo, by `mvhutz`."> Go Cuckoo
A hash table that uses cuckoo hashing to achieve a worst-case O(1) lookup time.
A hash table that uses cuckoo hashing to achieve a worst-case O(1) lookup time. Read more about it in [the package documentation](https://pkg.go.dev/git.maximhutz.com/tools/go-cuckoo).

1
assets/logo.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges"><path fill="#2a1512" d="M3,6h1v1h-1v-1M2,7h1v1h-1v-1M12,7h1v1h-1v-1M1,8h1v1h-1v-1M4,8h1v2h-1v-2M14,8h1v1h-1v-1M11,9h1v2h-2v-1h1v-1M5,10h1v1h-1v-1M1,13h2v1h1v1h-2v-1h-1v-1M13,13h2v1h-1v1h-1v1h-1v-2h1v-1M4,15h1v1h-1v-1"/><path fill="#38231f" d="M4,6h1v1h-1v-1M11,6h1v1h-1v-1M13,7h1v1h-1v-1M1,9h1v2h-1v-2M14,9h1v1h-1v-1M14,11h1v2h-1v-2M1,12h1v1h-1v-1M5,15h3v1h-3v-1M10,15h2v1h-2v-1"/><path fill="#3f3f74" d="M7,3h2v1h-2v-1M6,4h1v1h-1v-1M9,4h1v1h-1v-1M5,5h1v5h-1v-5M10,5h1v5h-1v-5M6,10h4v1h-4v-1"/><path fill="#6262ab" d="M9,5h1v1h-1v-1M6,9h1v1h-1v-1M9,9h1v1h-1v-1"/><path fill="#663931" d="M11,8h1v1h-1v-1M2,9h1v1h-1v-1M14,10h1v1h-1v-1M1,11h1v1h-1v-1M3,11h2v1h-2v-1M13,11h1v1h-1v-1M2,12h1v1h-1v-1M5,12h1v1h-1v-1M10,12h3v2h-1v-1h-2v-1M3,13h1v1h-1v-1M8,13h2v1h-2v-1M4,14h3v1h-3v-1M10,14h2v1h-2v-1M8,15h2v1h-2v-1"/><path fill="#8d8dcb" d="M9,6h1v3h-1v1h-2v-1h1v-1h1v-2M6,8h1v1h-1v-1"/><path fill="#8f563b" d="M2,8h1v1h-1v-1M12,9h1v1h-1v-1M2,10h1v1h-1v-1M13,10h1v1h-1v-1M7,11h1v1h-1v-1M11,11h2v1h-2v-1M4,12h1v1h1v-1h1v2h-3v-2M13,12h1v1h-1v-1M10,13h2v1h-2v-1M7,14h3v1h-3v-1"/><path fill="#a4a4d5" d="M8,4h1v4h-1v1h-1v-1h-1v-2h1v-1h1v-1"/><path fill="#ab764a" d="M4,7h1v1h-1v-1M3,8h1v2h-1v-2M12,10h1v1h-1v-1M2,11h1v1h-1v-1M8,11h1v1h-1v-1M3,12h1v1h-1v-1M7,12h1v2h-1v-2"/><path fill="#cacaea" d="M7,4h1v1h-1v-1M6,5h1v1h-1v-1"/><path fill="#d9a066" d="M3,7h1v1h-1v-1M11,7h1v1h-1v-1M12,8h2v2h-1v-1h-1v-1M3,10h2v1h-2v-1M5,11h2v1h-2v-1M9,11h2v1h-1v1h-2v-1h1v-1"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/logo128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

BIN
assets/logo16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

View File

@@ -1,7 +1,9 @@
package cuckoo_test
import (
"fmt"
"maps"
"os"
"testing"
"github.com/stretchr/testify/assert"
@@ -25,8 +27,9 @@ type fuzzStep struct {
}
type fuzzScenario struct {
seedA, seedB uint32
steps []fuzzStep
seedA, seedB uint32
capacity, growthFactor uint8
steps []fuzzStep
}
func FuzzInsertLookup(f *testing.F) {
@@ -40,14 +43,25 @@ func FuzzInsertLookup(f *testing.F) {
return
}
if scenario.seedA == scenario.seedB {
return
seedA, seedB := scenario.seedA, scenario.seedB
growthFactor := max(2, int(scenario.growthFactor))
capacity := int(scenario.capacity)
// If they are the same number, the hashes will clash, always causing an
// error.
if seedA == seedB {
t.Skip()
}
fmt.Fprintf(os.Stderr, "seedA=%d seedB=%d capacity=%d growthFactor=%d\n",
seedA, seedB, capacity, growthFactor)
actual := cuckoo.NewCustomTable[uint32, uint32](
offsetHash(scenario.seedA),
offsetHash(scenario.seedB),
offsetHash(seedA),
offsetHash(seedB),
func(a, b uint32) bool { return a == b },
cuckoo.Capacity(capacity),
cuckoo.GrowthFactor(growthFactor),
)
expected := map[uint32]uint32{}
@@ -59,16 +73,16 @@ func FuzzInsertLookup(f *testing.F) {
delete(expected, step.key)
_, err = actual.Get(step.key)
assert.Error(err)
_, ok := actual.Get(step.key)
assert.False(ok)
} else {
err := actual.Put(step.key, step.value)
assert.NoError(err)
expected[step.key] = step.value
found, err := actual.Get(step.key)
assert.NoError(err)
found, ok := actual.Get(step.key)
assert.True(ok)
assert.Equal(step.value, found)
}

View File

@@ -108,28 +108,16 @@ func TestGetMany(t *testing.T) {
}
for i := range 2_000 {
value, err := table.Get(i)
value, ok := table.Get(i)
if i < 1_000 {
assert.NoError(err)
assert.True(ok)
assert.Equal(value, true)
} else {
assert.Error(err)
assert.False(ok)
}
}
}
func TestRemove(t *testing.T) {
assert := assert.New(t)
table := cuckoo.NewTable[int, bool]()
assert.False(table.Has(0))
err := table.Put(0, true)
assert.NoError(err)
assert.True(table.Has(0))
}
func TestDropExistingItem(t *testing.T) {
assert := assert.New(t)
key, value := 0, true

View File

@@ -14,19 +14,19 @@ func Example_basic() {
fmt.Println("Put error:", err)
}
if item, err := table.Get(1); err != nil {
fmt.Println("Error:", err)
if item, ok := table.Get(1); !ok {
fmt.Println("Not Found 1!")
} else {
fmt.Println("Found 1:", item)
}
if item, err := table.Get(0); err != nil {
fmt.Println("Error:", err)
if item, ok := table.Get(0); !ok {
fmt.Println("Not Found 0!")
} else {
fmt.Println("Found 0:", item)
}
// Output:
// Found 1: Hello, World!
// Error: key '0' not found
// Not Found 0!
}

View File

@@ -1,5 +1,7 @@
package cuckoo
import "fmt"
// DefaultCapacity is the initial capacity of a [Table]. It is inspired from
// Java's [HashMap] implementation, which also uses 16.
//
@@ -10,11 +12,12 @@ const DefaultCapacity uint64 = 16
// hash table implementations use 2.
const DefaultGrowthFactor uint64 = 2
// DefaultMinimumLoad is the default lowest acceptable occupancy of a [Table].
// The value of 5% is taken from [libcuckoo].
// defaultMinimumLoad is the default lowest acceptable occupancy of a [Table].
// The higher the minimum load, the more likely that a [Table.Put] will not
// succeed. The value of 5% is taken from [libcuckoo].
//
// [libcuckoo]: https://github.com/efficient/libcuckoo/blob/656714705a055df2b7a605eb3c71586d9da1e119/libcuckoo/cuckoohash_config.hh#L21
const DefaultMinimumLoad float64 = 0.05
const defaultMinimumLoad float64 = 0.05
type settings struct {
growthFactor uint64
@@ -27,19 +30,21 @@ type settings struct {
type Option func(*settings)
// Capacity modifies the starting capacity of each bucket of the [Table]. The
// value must be greater than 0.
// value must be non-negative.
func Capacity(value int) Option {
return func(s *settings) { s.bucketSize = uint64(value) }
}
if value < 0 {
panic(fmt.Sprintf("go-cuckoo: Capacity must be non-negative, got %d", value))
}
// MinimumLoad modifies the [DefaultMinimumLoad] of the [Table]. The value must
// be between 0.00 and 1.00.
func MinimumLoad(value float64) Option {
return func(s *settings) { s.minLoadFactor = value }
return func(s *settings) { s.bucketSize = uint64(value) }
}
// GrowthFactor controls how much the capacity of the [Table] multiplies when
// it must resize. The value must be greater than 1.
func GrowthFactor(value int) Option {
if value < 2 {
panic(fmt.Sprintf("go-cuckoo: GrowthFactor must be greater than 1, got %d", value))
}
return func(s *settings) { s.growthFactor = uint64(value) }
}

View File

@@ -1,12 +1,21 @@
package cuckoo
import (
"errors"
"fmt"
"iter"
"math/bits"
"strings"
)
// ErrBadHash occurs when the hashes given to a [Table] cause too many key
// collisions. Try rebuilding the table using:
//
// 1. Different hash seeds. Equal seeds produce equal hash functions, which
// always cycle.
// 2. A different [Hash] algorithm.
var ErrBadHash = errors.New("bad hash")
// A Table is hash table that uses cuckoo hashing to resolve collision. Create
// one with [NewTable]. Or if you want more granularity, use [NewTableBy] or
// [NewCustomTable].
@@ -86,24 +95,23 @@ func (t *Table[K, V]) shrink() error {
return t.resize(t.bucketA.capacity / t.growthFactor)
}
// Get fetches the value for a key in the [Table]. Returns an error if no value
// is found.
func (t Table[K, V]) Get(key K) (value V, err error) {
// Get fetches the value for a key in the [Table].
func (t Table[K, V]) Get(key K) (value V, ok bool) {
if item, ok := t.bucketA.get(key); ok {
return item, nil
return item, true
}
if item, ok := t.bucketB.get(key); ok {
return item, nil
return item, true
}
return value, fmt.Errorf("key '%v' not found", key)
return
}
// Has returns true if a key has a value in the table.
func (t Table[K, V]) Has(key K) (exists bool) {
_, err := t.Get(key)
return err == nil
_, exists = t.Get(key)
return
}
// Put sets the value for a key. Returns error if its value cannot be set.
@@ -128,7 +136,7 @@ func (t *Table[K, V]) Put(key K, value V) (err error) {
}
if t.load() < t.minLoadFactor {
return fmt.Errorf("bad hash: resize on load %d/%d = %f", t.Size(), t.TotalCapacity(), t.load())
return fmt.Errorf("hash functions produced a cycle at load %d/%d: %w", t.Size(), t.TotalCapacity(), ErrBadHash)
}
if err := t.grow(); err != nil {
@@ -198,7 +206,7 @@ func NewCustomTable[K, V any](hashA, hashB Hash[K], compare EqualFunc[K], option
settings := &settings{
growthFactor: DefaultGrowthFactor,
bucketSize: DefaultCapacity,
minLoadFactor: DefaultMinimumLoad,
minLoadFactor: defaultMinimumLoad,
}
for _, option := range options {