Files
strom/level/storage.go
T
2026-04-23 22:36:34 +02:00

153 lines
3.6 KiB
Go

package level
import (
"errors"
"math"
"slices"
)
type Storage[T comparable] struct {
Palette []T
Data []uint64
PossibleBitsPerEntryValues []uint8 // These should be sorted
BitsPerEntry uint8
Len int
MaxAtCurrentBpe int
ElementsPerLong int
Mask uint64
}
var CannotGrowPalletErr = errors.New("cannot grow pallet")
var CannotGrowPalletNoPossibleBpeErr = errors.New("cannot grow pallet, no possible bpe provided")
var Bpe0ButNot1LenPaletteErr = errors.New("bpe 0 but not 1, len palette")
var InvalidPaletteIndexErr = errors.New("invalid palette index")
func NewStorage[T comparable](len int, possibleBpe []uint8) (s *Storage[T]) {
s = new(Storage[T])
s.Len = len
s.PossibleBitsPerEntryValues = possibleBpe
s.Resize(possibleBpe[0])
return
}
func ImportStorage[T comparable](data []uint64, bpe uint8, palette []T, len int, possibleBpe []uint8) (s *Storage[T], err error) {
s = new(Storage[T])
s.Resize(bpe)
s.Data = data
s.Palette = palette
s.Len = len
s.PossibleBitsPerEntryValues = possibleBpe
err = s.CheckData()
return
}
func (s *Storage[T]) CheckData() (err error) {
if s.BitsPerEntry == 0 && len(s.Palette) != 1 {
err = Bpe0ButNot1LenPaletteErr
return
}
for i := range s.Len {
if s.GetLow(i) >= uint32(len(s.Palette)) {
err = InvalidPaletteIndexErr
return
}
}
return
}
func (s *Storage[T]) Resize(bpe uint8) {
if bpe == 0 {
s.Data = nil
s.BitsPerEntry = 0
s.ElementsPerLong = 0
s.Mask = 0
s.MaxAtCurrentBpe = 1
return
}
elementsPerLong := int(64 / int(bpe))
numberOfLongs := int(math.Ceil(float64(s.Len) / float64(elementsPerLong)))
newData := make([]uint64, numberOfLongs)
if s.BitsPerEntry == 0 {
s.Data = newData
s.BitsPerEntry = bpe
s.ElementsPerLong = elementsPerLong
s.Mask = (uint64(1) << uint32(bpe)) - 1
s.MaxAtCurrentBpe = 1 << s.BitsPerEntry
return
}
for i := range s.Len {
v := s.GetLow(i)
newData[i/elementsPerLong] |= uint64(v) << ((i % elementsPerLong) * int(bpe))
}
s.Data = newData
s.BitsPerEntry = bpe
s.ElementsPerLong = elementsPerLong
s.Mask = (uint64(1) << uint32(bpe)) - 1
s.MaxAtCurrentBpe = 1 << s.BitsPerEntry
return
}
func (s *Storage[T]) GrowPallet(v T) (index int, err error) {
index = len(s.Palette)
if index+1 > s.MaxAtCurrentBpe {
if s.PossibleBitsPerEntryValues == nil {
err = CannotGrowPalletNoPossibleBpeErr
return
}
bpei := slices.Index(s.PossibleBitsPerEntryValues, s.BitsPerEntry)
if bpei == len(s.PossibleBitsPerEntryValues)-1 {
err = CannotGrowPalletErr
return
}
newBpe := s.PossibleBitsPerEntryValues[bpei+1]
s.Resize(newBpe)
}
s.Palette = append(s.Palette, v)
return
}
func (s *Storage[T]) Set(i int, v T) (err error) {
pi := slices.Index(s.Palette, v)
if pi != -1 {
if s.BitsPerEntry == 0 {
return
}
s.SetLow(i, uint32(pi))
return
}
pi, err = s.GrowPallet(v)
if err != nil {
return
}
if s.BitsPerEntry == 0 {
return
}
s.SetLow(i, uint32(pi))
return
}
func (s *Storage[T]) Get(i int) (v T) {
if s.BitsPerEntry == 0 {
return s.Palette[0]
}
return s.Palette[s.GetLow(i)]
}
func (s *Storage[T]) GetLow(i int) (j uint32) {
j = uint32((s.Data[i/s.ElementsPerLong] >> ((i % s.ElementsPerLong) * int(s.BitsPerEntry))) & s.Mask)
return
}
func (s *Storage[T]) SetLow(i int, v uint32) {
s.Data[i/s.ElementsPerLong] &= ^(s.Mask << ((i % s.ElementsPerLong) * int(s.BitsPerEntry)))
s.Data[i/s.ElementsPerLong] |= uint64(v) << ((i % s.ElementsPerLong) * int(s.BitsPerEntry))
}
func (s *Storage[T]) RealValues() []T {
ret := make([]T, s.Len)
for i := range s.Len {
ret[i] = s.Get(i)
}
return ret
}