diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..1ec3d80 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,235 @@ +--- +# golangci-lint configuration file made by @ccoVeille +# Source: https://github.com/ccoVeille/golangci-lint-config-examples/ +# Author: @ccoVeille +# License: MIT +# Variant: 03-safe +# Version: v2.0.0 +# +version: "2" + +formatters: + enable: + # format the code + - gofmt + # format the block of imports + - gci + + settings: + # format the code with Go standard library + gofmt: + # simplify the code + # https://pkg.go.dev/cmd/gofmt#hdr-The_simplify_command + simplify: true + rewrite-rules: + # replace `interface{}` with `any` in the code on format + - pattern: 'interface{}' + replacement: 'any' + + # make sure imports are always in a deterministic order + # https://github.com/daixiang0/gci/ + gci: # define the section orders for imports + sections: + # Standard section: captures all standard packages. + - standard + # Default section: catchall that is not standard or custom + - default + # linters that related to local tool, so they should be separated + - localmodule + +linters: + exclusions: + # these presets where present in the v1 version of golangci-lint + # it's interesting to keep them when migrating, but removing them should be the goal + presets: + # exclude check on comments format in godoc + # These are common false positives in poor code + # you should not use this on recent code you write from scratch + # More information: https://golangci-lint.run/usage/false-positives/#comments + # + # Please uncomment the following line if your code is not using the godoc format + # - comments + + # Common false positives + # feel free to remove this if you don't have any false positives + # More information: https://golangci-lint.run/usage/false-positives/#common-false-positives + - common-false-positives + + # Legacy preset is not recommended anymore + # More information: https://golangci-lint.run/usage/false-positives/#legacy + - legacy + + # std-error-handling is a set of rules that avoid reporting unhandled errors on common functions/methods + # More information: https://golangci-lint.run/usage/false-positives/#std-error-handling + - std-error-handling + + # some linters are enabled by default + # https://golangci-lint.run/usage/linters/ + # + # enable some extra linters + enable: + # Errcheck is a program for checking for unchecked errors in Go code. + - errcheck + + # Vet examines Go source code and reports suspicious constructs. + - govet + + # Detects when assignments to existing variables are not used. + - ineffassign + + # It's a set of rules from staticcheck. See https://staticcheck.io/ + - staticcheck + + # Checks Go code for unused constants, variables, functions and types. + - unused + + # Fast, configurable, extensible, flexible, and beautiful linter for Go. + # Drop-in replacement of golint. + - revive + + # make sure to use t.Helper() when needed + - thelper + + # mirror suggests rewrites to avoid unnecessary []byte/string conversion + - mirror + + # detect the possibility to use variables/constants from the Go standard library. + - usestdlibvars + + # Finds commonly misspelled English words. + - misspell + + # Checks for duplicate words in the source code. + - dupword + + # linter to detect errors invalid key values count + - loggercheck + + # detect when a package or method could be replaced by one from the standard library + - exptostd + + # detects nested contexts in loops or function literals + - fatcontext + + # Reports uses of functions with replacement inside the testing package. + - usetesting + + settings: + revive: + rules: + # these are the default revive rules + # you can remove the whole "rules" node if you want + # BUT + # ! /!\ they all need to be present when you want to add more rules than the default ones + # otherwise, you won't have the default rules, but only the ones you define in the "rules" node + + # Blank import should be only in a main or test package, or have a comment justifying it. + - name: blank-imports + + # Packages should have comments of the form "Package x ...". + - name: package-comments + + # context.Context() should be the first parameter of a function when provided as argument. + - name: context-as-argument + arguments: + - allowTypesBefore: "*testing.T" + + # Basic types should not be used as a key in `context.WithValue` + - name: context-keys-type + + # Importing with `.` makes the programs much harder to understand + - name: dot-imports + + # Empty blocks make code less readable and could be a symptom of a bug or unfinished refactoring. + - name: empty-block + + # for better readability, variables of type `error` must be named with the prefix `err`. + - name: error-naming + + # for better readability, the errors should be last in the list of returned values by a function. + - name: error-return + + # for better readability, error messages should not be capitalized or end with punctuation or a newline. + - name: error-strings + + # report when replacing `errors.New(fmt.Sprintf())` with `fmt.Errorf()` is possible + - name: errorf + + # check naming and commenting conventions on exported symbols. + - name: exported + arguments: + # make error messages clearer + - "sayRepetitiveInsteadOfStutters" + # require comments on public interface methods + - "checkPublicInterface" + + # incrementing an integer variable by 1 is recommended to be done using the `++` operator + - name: increment-decrement + + # highlights redundant else-blocks that can be eliminated from the code + # - name: indent-error-flow + + # This rule suggests a shorter way of writing ranges that do not use the second value. + - name: range + + # receiver names in a method should reflect the struct name (p for Person, for example) + - name: receiver-naming + + # redefining built in names (true, false, append, make) can lead to bugs very difficult to detect. + - name: redefines-builtin-id + + # redundant else-blocks that can be eliminated from the code. + # - name: superfluous-else + + # prevent confusing name for variables when using `time` package + - name: time-naming + + # warns when an exported function or method returns a value of an un-exported type. + - name: unexported-return + + # spots and proposes to remove unreachable code. also helps to spot errors + - name: unreachable-code + + # Functions or methods with unused parameters can be a symptom of an unfinished refactoring or a bug. + - name: unused-parameter + + # report when a variable declaration can be simplified + - name: var-declaration + + # warns when initialism, variable or package naming conventions are not followed. + - name: var-naming + + misspell: + # Correct spellings using locale preferences for US or UK. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + # Default ("") is to use a neutral variety of English. + locale: US + + # List of words to ignore + # among the one defined in https://github.com/golangci/misspell/blob/master/words.go + ignore-rules: [] + # - valor + # - and + + # Extra word corrections. + extra-words: [] + # - typo: "whattever" + # correction: "whatever" + +output: + # Order to use when sorting results. + # Possible values: `file`, `linter`, and `severity`. + # + # If the severity values are inside the following list, they are ordered in this order: + # 1. error + # 2. warning + # 3. high + # 4. medium + # 5. low + # Either they are sorted alphabetically. + # + # Default: ["file"] + sort-order: + - linter + - severity + - file # filepath, line, and column. \ No newline at end of file diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..491c3ad --- /dev/null +++ b/doc.go @@ -0,0 +1,2 @@ +// Package ring implements a generic deque using a circular buffer. +package ring diff --git a/options.go b/options.go index b795dc1..ce0b0f8 100644 --- a/options.go +++ b/options.go @@ -2,17 +2,26 @@ package ring import "fmt" +// DefaultGrowthFactor is the default multiplier a [Ring] uses to increase its +// capacity. Most implementations use 2. const DefaultGrowthFactor = 2 +// DefaultCapacity is the default starting capacity of a new [Ring]. Sixteen was +// chosen as a reasonable default. const DefaultCapacity = 16 +// A config holds all values that modify the behavior of a [Ring]. type config struct { capacity uint64 growthFactor uint64 } +// An Option can be used with [New] to modify the behavior of a [Ring]. type Option func(*config) +// GrowthFactor modifies the multiplier used to increase (and decrease) the +// capacity of a [Ring]. +// Its value must be greater than 1. func GrowthFactor(value int) Option { if value <= 1 { panic(fmt.Errorf("go-ring: growth factor must be greater than 1, got %d", value)) @@ -23,6 +32,8 @@ func GrowthFactor(value int) Option { } } +// Capacity modifies the starting capacity of a [Ring]. +// Its value must be non-negative. func Capacity(value int) Option { if value < 0 { panic(fmt.Errorf("go-ring: starting capacity must be non-negative, got %d", value)) diff --git a/ring.go b/ring.go index 33962d4..b0437d2 100644 --- a/ring.go +++ b/ring.go @@ -12,16 +12,18 @@ type Ring[T any] struct { offset uint64 // Is not greater than 'size'. } +// Cap returns the capacity of 'r'. func (r Ring[T]) Cap() int { return len(r.data) } +// Len returns the number of values in 'r'. func (r Ring[T]) Len() int { return int(r.size) } -// fetch returns the value of a certain index. The index MUST NOT be out of -// range. +// fetch returns the value of a certain index. +// The index MUST NOT be out of range. func (r Ring[T]) fetch(index uint64) T { if index >= r.size { panic("Out of range!") @@ -30,20 +32,26 @@ func (r Ring[T]) fetch(index uint64) T { return r.data[(r.offset+index)%r.size] } +// Get returns the value at a certain 'index' in 'r'. +// The index must be within the range of the ring. func (r Ring[T]) Get(index int) T { return r.fetch(uint64(index)) } +// Front returns the first value in 'r'. +// The ring must not be empty. func (r Ring[T]) Front() T { return r.fetch(0) } +// Back returns the last value in 'r'. +// The ring must not be empty. func (r Ring[T]) Back() T { return r.fetch(r.size - 1) } -// resize replaces the current data table with a new table a new 'capacity'. The -// new capacity MUST NOT be less than [Ring.Len]. +// resize replaces the current data table with a new table a new 'capacity'. +// The new capacity must not be less than [Ring.Len]. func (r *Ring[T]) resize(capacity uint64) { if capacity < r.size { panic("Resize too small!") @@ -58,8 +66,8 @@ func (r *Ring[T]) resize(capacity uint64) { r.data = newData } -// grow increases the size of the array by its growth factor. It is guaranteed -// to not be [Ring.full]. +// grow increases the size of the array by its growth factor. +// It is guaranteed to not be [Ring.full]. func (r *Ring[T]) grow() { if r.size == 0 { r.resize(1) @@ -73,8 +81,8 @@ func (r Ring[T]) full() bool { return r.size == uint64(len(r.data)) } -// shrink decreases the size of the data table by its growth factor. Do not call -// this when the data table is not [Ring.sparse]. +// shrink decreases the capacity of 'r' by its growth factor. +// The ring must be [Ring.sparse]. func (r *Ring[T]) shrink() { r.resize(r.size / r.growthFactor) } @@ -84,7 +92,8 @@ func (r *Ring[T]) sparse() bool { return r.size*r.growthFactor < uint64(len(r.data)) } -// set updates the value of a certain index. The index MUST be in range. +// set updates the value of a certain index. +// The index must be in range. func (r Ring[T]) set(index uint64, value T) { if index >= r.size { panic("Out of range!")