error-pages/internal/pick/picker.go

89 lines
1.5 KiB
Go

package pick
import (
"math/rand"
"sync"
"time"
)
type pickMode = byte
const (
First pickMode = 1 + iota // Always pick the first element (index = 0)
RandomOnce // Pick random element once (any future Pick calls will return the same element)
RandomEveryTime // Always Pick the random element
)
type picker struct {
mode pickMode
rand *rand.Rand // will be nil for the First pick mode
maxIdx uint32
mu sync.Mutex
lastIdx uint32
}
const unsetIdx uint32 = 4294967295
func NewPicker(maxIdx uint32, mode pickMode) *picker {
var p = &picker{
maxIdx: maxIdx,
mode: mode,
lastIdx: unsetIdx,
}
if mode != First {
p.rand = rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec
}
return p
}
// NextIndex returns an index for the next element (based on pickMode).
func (p *picker) NextIndex() uint32 {
if p.maxIdx == 0 {
return 0
}
switch p.mode {
case First:
return 0
case RandomOnce:
if p.lastIdx == unsetIdx {
return p.randomizeNext()
}
return p.lastIdx
case RandomEveryTime:
return p.randomizeNext()
default:
panic("picker.NextIndex(): unsupported mode")
}
}
func (p *picker) randomizeNext() uint32 {
var idx = uint32(p.rand.Intn(int(p.maxIdx + 1)))
p.mu.Lock()
defer p.mu.Unlock()
if idx == p.lastIdx {
p.lastIdx++
} else {
p.lastIdx = idx
}
if p.lastIdx > p.maxIdx { // overflow?
p.lastIdx = 0
}
if p.lastIdx == unsetIdx {
p.lastIdx--
}
return p.lastIdx
}