blob: 3aa74a713139a65693b128abb13a6e7270d90969 [file] [edit]
//go:build arm64 && !appengine && !noasm && gc
package zstd
import (
"errors"
"fmt"
"reflect"
"testing"
)
// buildDtableRef is a copy of the pure-Go reference algorithm
// (fse_decoder_generic.go), kept here so the arm64 asm implementation can be
// differentially tested against it even though the generic file is not
// compiled on arm64.
func buildDtableRef(s *fseDecoder) error {
tableSize := uint32(1 << s.actualTableLog)
highThreshold := tableSize - 1
symbolNext := s.stateTable[:256]
{
for i, v := range s.norm[:s.symbolLen] {
if v == -1 {
s.dt[highThreshold].setAddBits(uint8(i))
highThreshold--
v = 1
}
symbolNext[i] = uint16(v)
}
}
{
tableMask := tableSize - 1
step := tableStep(tableSize)
position := uint32(0)
for ss, v := range s.norm[:s.symbolLen] {
for i := 0; i < int(v); i++ {
s.dt[position].setAddBits(uint8(ss))
for {
position = (position + step) & tableMask
if position <= highThreshold {
break
}
}
}
}
if position != 0 {
return errors.New("corrupted input (position != 0)")
}
}
{
tableSize := uint16(1 << s.actualTableLog)
for u, v := range s.dt[:tableSize] {
symbol := v.addBits()
nextState := symbolNext[symbol]
symbolNext[symbol] = nextState + 1
nBits := s.actualTableLog - byte(highBits(uint32(nextState)))
s.dt[u&maxTableMask].setNBits(nBits)
newState := (nextState << nBits) - tableSize
if newState > tableSize {
return fmt.Errorf("newState (%d) outside table size (%d)", newState, tableSize)
}
if newState == uint16(u) && nBits == 0 {
return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, u)
}
s.dt[u&maxTableMask].setNewState(newState)
}
}
return nil
}
func TestBuildDtableARM64MatchesReference(t *testing.T) {
for name, mk := range predefinedFSEInputs() {
t.Run(name, func(t *testing.T) {
got := mk()
if err := got.buildDtable(); err != nil {
t.Fatalf("asm buildDtable: %v", err)
}
want := mk()
if err := buildDtableRef(want); err != nil {
t.Fatalf("reference buildDtable: %v", err)
}
tableSize := 1 << got.actualTableLog
if !reflect.DeepEqual(got.dt[:tableSize], want.dt[:tableSize]) {
for i := range tableSize {
if got.dt[i] != want.dt[i] {
t.Errorf("dt[%d]: asm %#016x, ref %#016x", i, uint64(got.dt[i]), uint64(want.dt[i]))
}
}
t.FailNow()
}
if got.stateTable != want.stateTable {
t.Errorf("stateTable mismatch:\nasm %v\nref %v", got.stateTable, want.stateTable)
}
})
}
}