Skip to content

Commit db753f3

Browse files
committed
repo: an initial implementation of a circular buffer (ringbuf) for Go
0 parents  commit db753f3

12 files changed

Lines changed: 366 additions & 0 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Run CI Tests
2+
on:
3+
pull_request:
4+
paths-ignore:
5+
- 'README.md'
6+
- 'LICENSE'
7+
push:
8+
branches:
9+
- 'main'
10+
jobs:
11+
run-copywrite:
12+
timeout-minutes: 5
13+
runs-on: ubuntu-24.04
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: hashicorp/setup-copywrite@v1.1.3
17+
- name: verify copyright
18+
run: |
19+
copywrite --config .github/workflows/scripts/copywrite.hcl \
20+
headers --spdx "BSD-3-Clause" --plan
21+
run-tests:
22+
timeout-minutes: 5
23+
runs-on: ubuntu-24.04
24+
steps:
25+
- uses: actions/checkout@v4
26+
- uses: extractions/setup-just@v2
27+
- uses: actions/setup-go@v5
28+
with:
29+
go-version-file: go.mod
30+
cache-dependency-path: '**/go.sum'
31+
- name: Show System
32+
run: |
33+
uname -a
34+
just sysinfo
35+
- name: Run Go Test
36+
run: |
37+
just init tidy lint test
38+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
schema_version = 1
2+
3+
project {
4+
license = "BSD-3-Clause"
5+
copyright_holder = "CattleCloud LLC"
6+
copyright_year = 2025
7+
header_ignore = [
8+
"**/*.sh",
9+
".src/**",
10+
".bin/**",
11+
".github/**",
12+
".golangci.yaml",
13+
]
14+
}
15+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
run:
2+
timeout: 5m
3+
linters:
4+
enable:
5+
- asasalint
6+
- asciicheck
7+
- bidichk
8+
- bodyclose
9+
- copyloopvar
10+
- dogsled
11+
- dupword
12+
- durationcheck
13+
- errcheck
14+
- errname
15+
- errorlint
16+
- exhaustive
17+
- gochecknoinits
18+
- gocritic
19+
- gofmt
20+
- gosimple
21+
- govet
22+
- ineffassign
23+
- makezero
24+
- misspell
25+
- musttag
26+
- nilnil
27+
- noctx
28+
- paralleltest
29+
- perfsprint
30+
- prealloc
31+
- predeclared
32+
- reassign
33+
- revive
34+
- rowserrcheck
35+
- staticcheck
36+
- sqlclosecheck
37+
- tagalign
38+
- tenv
39+
- unused
40+
- whitespace
41+
42+
linters-settings:
43+
paralleltest:
44+
ignore-missing-subtests: true
45+
exhaustive:
46+
default-signifies-exhaustive: true

.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
*~
2+
~*
3+
4+
# OS files
5+
**/.DS_Store
6+
7+
# Build artifacts
8+
site
9+
10+
# Go workspace file
11+
go.work
12+
go.work.sum
13+
14+
# Local source and binary files
15+
.src/
16+
.bin/
17+

Justfile

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
set shell := ["bash", "-u", "-c"]
2+
3+
export scripts := ".github/workflows/scripts"
4+
export GOBIN := `echo $PWD/.bin`
5+
6+
# show available commands
7+
[private]
8+
default:
9+
@just --list
10+
11+
# tidy up Go modules
12+
[group('build')]
13+
tidy:
14+
go mod tidy
15+
16+
# run tests across source tree
17+
[group('build')]
18+
test:
19+
go test -v -race -count=1 ./...
20+
21+
# ensure copywrite headers present on source files
22+
[group('lint')]
23+
copywrite:
24+
copywrite \
25+
--config {{scripts}}/copywrite.hcl headers \
26+
--spdx "BSD-3-Clause"
27+
28+
# apply go vet command on source tree
29+
[group('lint')]
30+
vet:
31+
go vet ./...
32+
33+
# apply golangci-lint linters on source tree
34+
[group('lint')]
35+
lint: vet
36+
$GOBIN/golangci-lint run --config {{scripts}}/golangci.yaml
37+
38+
# show host system information
39+
[group('build')]
40+
@sysinfo:
41+
echo "{{os()/arch()}} {{num_cpus()}}c"
42+
43+
# locally install build dependencies
44+
[group('build')]
45+
init:
46+
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
47+

LICENSE

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2025, CattleCloud LLC
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# circlebuffer
2+
3+
[![Go Reference](https://pkg.go.dev/badge/cattlecloud.net/go/circlebuffer.svg)](https://pkg.go.dev/cattlecloud.net/go/circlebuffer)
4+
[![License](https://img.shields.io/github/license/cattlecloud/circlebuffer?color=7C00D8&style=flat-square&label=License)](https://github.com/cattlecloud/circlebuffer/blob/main/LICENSE)
5+
[![Build](https://img.shields.io/github/actions/workflow/status/cattlecloud/circlebuffer/ci.yaml?style=flat-square&color=0FAA07&label=Tests)](https://github.com/cattlecloud/circlebuffer/actions/workflows/ci.yaml)
6+
7+
`circlebuffer` provides a modern [circular buffer](https://en.wikipedia.org/wiki/Circular_buffer) Go library with support for generics.
8+
9+
### Getting Started
10+
11+
The `circlebuffer` package can be added to a project with `go-get`.
12+
13+
```shell
14+
go get cattlecloud.net/go/circlebuffer@latest
15+
```
16+
17+
```go
18+
import "cattlecloud.net/go/circlebuffer"
19+
```
20+
21+
### Examples
22+
23+
##### Inserting elements
24+
25+
```go
26+
buf := circlebuffer.New[string](1024)
27+
buf.Insert("alice")
28+
buf.Insert("bob")
29+
```
30+
31+
##### Iterating elements
32+
33+
```go
34+
for item := range buf.All() {
35+
// ...
36+
}
37+
```
38+
39+
### License
40+
41+
The `cattlecloud.net/go/circlebuffer` module is open source under the [BSD](LICENSE) license.

buffer.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) CattleCloud LLC
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
package circlebuffer
5+
6+
import "iter"
7+
8+
type Array[T any] interface {
9+
All() iter.Seq[T]
10+
Insert(T)
11+
Size() int
12+
Empty() bool
13+
}
14+
15+
func New[T any](capacity int) Array[T] {
16+
return &array[T]{
17+
data: make([]T, capacity),
18+
}
19+
}
20+
21+
type array[T any] struct {
22+
data []T
23+
head int
24+
size int
25+
}
26+
27+
func (b *array[T]) Size() int {
28+
return b.size
29+
}
30+
31+
func (b *array[T]) Empty() bool {
32+
return b.Size() == 0
33+
}
34+
35+
func (b *array[T]) Insert(item T) {
36+
b.data[b.head] = item
37+
if b.size < len(b.data) {
38+
b.size++
39+
}
40+
b.head = (b.head + 1) % len(b.data)
41+
}
42+
43+
func (b *array[T]) All() iter.Seq[T] {
44+
return func(yield func(T) bool) {
45+
if b.size == 0 {
46+
return
47+
}
48+
49+
start := (b.head - b.size + len(b.data)) % len(b.data)
50+
for i := 0; i < b.size; i++ {
51+
index := (start + i) % len(b.data)
52+
if !yield(b.data[index]) {
53+
return
54+
}
55+
}
56+
}
57+
}

buffer_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) CattleCloud LLC
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
package circlebuffer
5+
6+
import (
7+
"testing"
8+
9+
"github.com/shoenig/test/must"
10+
)
11+
12+
func testSequence[T any](t *testing.T, a Array[T], exp []T) {
13+
t.Helper()
14+
15+
result := make([]T, 0, a.Size())
16+
for item := range a.All() {
17+
result = append(result, item)
18+
}
19+
must.Eq(t, exp, result)
20+
}
21+
22+
func TestArray(t *testing.T) {
23+
t.Parallel()
24+
25+
buf := New[string](3)
26+
must.Empty(t, buf)
27+
testSequence(t, buf, []string{})
28+
29+
buf.Insert("one")
30+
must.Size(t, 1, buf)
31+
testSequence(t, buf, []string{"one"})
32+
33+
buf.Insert("two")
34+
must.Size(t, 2, buf)
35+
testSequence(t, buf, []string{"one", "two"})
36+
37+
buf.Insert("three")
38+
must.Size(t, 3, buf)
39+
testSequence(t, buf, []string{"one", "two", "three"})
40+
41+
buf.Insert("four")
42+
must.Size(t, 3, buf)
43+
testSequence(t, buf, []string{"two", "three", "four"})
44+
45+
buf.Insert("five")
46+
must.Size(t, 3, buf)
47+
testSequence(t, buf, []string{"three", "four", "five"})
48+
}

dependabot.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directories:
5+
- "/"
6+
schedule:
7+
interval: "monthly"
8+
labels:
9+
- "dependencies"
10+
- package-ecosystem: "gomod"
11+
directories:
12+
- "/"
13+
schedule:
14+
interval: "monthly"
15+
labels:
16+
- "dependencies"
17+

0 commit comments

Comments
 (0)