initial commit
This commit is contained in:
131
internal/chunk/palette.go
Normal file
131
internal/chunk/palette.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package chunk
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// paletteSize is the size of a palette. It indicates the amount of bits occupied per value stored.
|
||||
type paletteSize byte
|
||||
|
||||
// Palette is a palette of values that every PalettedStorage has. Storages hold 'pointers' to indices
|
||||
// in this palette.
|
||||
type Palette struct {
|
||||
last uint32
|
||||
lastIndex int16
|
||||
size paletteSize
|
||||
|
||||
// values is a map of values. A PalettedStorage points to the index to this value.
|
||||
values []uint32
|
||||
}
|
||||
|
||||
// newPalette returns a new Palette with size and a slice of added values.
|
||||
func newPalette(size paletteSize, values []uint32) *Palette {
|
||||
return &Palette{size: size, values: values, last: math.MaxUint32}
|
||||
}
|
||||
|
||||
// Len returns the amount of unique values in the Palette.
|
||||
func (palette *Palette) Len() int {
|
||||
return len(palette.values)
|
||||
}
|
||||
|
||||
// Add adds a values to the Palette. It does not first check if the value was already set in the Palette.
|
||||
// The index at which the value was added is returned. Another bool is returned indicating if the Palette
|
||||
// was resized as a result of adding the value.
|
||||
func (palette *Palette) Add(v uint32) (index int16, resize bool) {
|
||||
i := int16(len(palette.values))
|
||||
palette.values = append(palette.values, v)
|
||||
|
||||
if palette.needsResize() {
|
||||
palette.increaseSize()
|
||||
return i, true
|
||||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
// Replace calls the function passed for each value present in the Palette. The value returned by the
|
||||
// function replaces the value present at the index of the value passed.
|
||||
func (palette *Palette) Replace(f func(v uint32) uint32) {
|
||||
// Reset last runtime ID as it now has a different offset.
|
||||
palette.last = math.MaxUint32
|
||||
for index, v := range palette.values {
|
||||
palette.values[index] = f(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Index loops through the values of the Palette and looks for the index of the given value. If the value could
|
||||
// not be found, -1 is returned.
|
||||
func (palette *Palette) Index(runtimeID uint32) int16 {
|
||||
if runtimeID == palette.last {
|
||||
// Fast path out.
|
||||
return palette.lastIndex
|
||||
}
|
||||
// Slow path in a separate function allows for inlining the fast path.
|
||||
return palette.indexSlow(runtimeID)
|
||||
}
|
||||
|
||||
// indexSlow searches the index of a value in the Palette's values by iterating through the Palette's values.
|
||||
func (palette *Palette) indexSlow(runtimeID uint32) int16 {
|
||||
l := len(palette.values)
|
||||
for i := 0; i < l; i++ {
|
||||
if palette.values[i] == runtimeID {
|
||||
palette.last = runtimeID
|
||||
v := int16(i)
|
||||
palette.lastIndex = v
|
||||
return v
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Value returns the value in the Palette at a specific index.
|
||||
func (palette *Palette) Value(i uint16) uint32 {
|
||||
return palette.values[i]
|
||||
}
|
||||
|
||||
// needsResize checks if the Palette, and with it the holding PalettedStorage, needs to be resized to a bigger
|
||||
// size.
|
||||
func (palette *Palette) needsResize() bool {
|
||||
return len(palette.values) > (1 << palette.size)
|
||||
}
|
||||
|
||||
var sizes = [...]paletteSize{0, 1, 2, 3, 4, 5, 6, 8, 16}
|
||||
var offsets = [...]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 8: 7, 16: 8}
|
||||
|
||||
// increaseSize increases the size of the Palette to the next palette size.
|
||||
func (palette *Palette) increaseSize() {
|
||||
palette.size = sizes[offsets[palette.size]+1]
|
||||
}
|
||||
|
||||
// padded returns true if the Palette size is 3, 5 or 6.
|
||||
func (p paletteSize) padded() bool {
|
||||
return p == 3 || p == 5 || p == 6
|
||||
}
|
||||
|
||||
// paletteSizeFor finds a suitable paletteSize for the amount of values passed n.
|
||||
func paletteSizeFor(n int) paletteSize {
|
||||
for _, size := range sizes {
|
||||
if n <= (1 << size) {
|
||||
return size
|
||||
}
|
||||
}
|
||||
// Should never happen.
|
||||
return 0
|
||||
}
|
||||
|
||||
// uint32s returns the amount of uint32s needed to represent a storage with this palette size.
|
||||
func (p paletteSize) uint32s() (n int) {
|
||||
uint32Count := 0
|
||||
if p != 0 {
|
||||
// indicesPerUint32 is the amount of indices that may be stored in a single uint32.
|
||||
indicesPerUint32 := 32 / int(p)
|
||||
// uint32Count is the amount of uint32s required to store all indices: 4096 indices need to be stored in
|
||||
// total.
|
||||
uint32Count = 4096 / indicesPerUint32
|
||||
}
|
||||
if p.padded() {
|
||||
// We've got one of the padded sizes, so the storage has another uint32 to be able to store
|
||||
// every index.
|
||||
uint32Count++
|
||||
}
|
||||
return uint32Count
|
||||
}
|
||||
Reference in New Issue
Block a user