2026-07-22 09:28:11 +02:00
|
|
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
package verify
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"os"
|
2026-08-05 23:09:54 +02:00
|
|
|
"runtime"
|
2026-07-22 09:28:11 +02:00
|
|
|
"testing"
|
|
|
|
|
"unsafe"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// lz4KernelPath is the sibling repository's AVX2 kernel, used for
|
|
|
|
|
// integration testing. The test is skipped when the file is absent
|
|
|
|
|
// (e.g. in CI without the sibling checkout).
|
|
|
|
|
const lz4KernelPath = "../../go-libraries/go-lz4/avx2_amd64.s"
|
|
|
|
|
|
|
|
|
|
func loadLZ4Kernel(t *testing.T) *Kernel {
|
|
|
|
|
t.Helper()
|
|
|
|
|
if _, err := os.Stat(lz4KernelPath); err != nil {
|
|
|
|
|
t.Skipf("sibling kernel not available: %v", err)
|
|
|
|
|
}
|
|
|
|
|
k, err := Load(lz4KernelPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Load(%s): %v", lz4KernelPath, err)
|
|
|
|
|
}
|
|
|
|
|
t.Cleanup(k.Close)
|
|
|
|
|
return k
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// callDecodeBlockAVX2 invokes the JIT-assembled decodeBlockAVX2 with the
|
|
|
|
|
// given src and dst buffers, returning (n, code).
|
|
|
|
|
func callDecodeBlockAVX2(t *testing.T, k *Kernel, src, dst []byte) (int, int) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
args := make([]byte, 64)
|
|
|
|
|
if len(src) > 0 {
|
|
|
|
|
PutPtr(args, 0, unsafe.Pointer(&src[0]))
|
|
|
|
|
}
|
|
|
|
|
PutUint64(args, 8, uint64(len(src)))
|
|
|
|
|
PutUint64(args, 16, uint64(cap(src)))
|
|
|
|
|
if len(dst) > 0 {
|
|
|
|
|
PutPtr(args, 24, unsafe.Pointer(&dst[0]))
|
|
|
|
|
}
|
|
|
|
|
PutUint64(args, 32, uint64(len(dst)))
|
|
|
|
|
PutUint64(args, 40, uint64(cap(dst)))
|
|
|
|
|
|
|
|
|
|
out, err := k.CallFunc("decodeBlockAVX2", args)
|
2026-08-05 23:09:54 +02:00
|
|
|
runtime.KeepAlive(src)
|
|
|
|
|
runtime.KeepAlive(dst)
|
2026-07-22 09:28:11 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("CallFunc(decodeBlockAVX2): %v", err)
|
|
|
|
|
}
|
|
|
|
|
return int(GetUint64(out, 48)), int(GetUint64(out, 56))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLZ4DecodeKnownAnswers(t *testing.T) {
|
|
|
|
|
k := loadLZ4Kernel(t)
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
src []byte
|
|
|
|
|
dstSize int
|
|
|
|
|
wantDst []byte
|
|
|
|
|
wantN int
|
|
|
|
|
wantCode int
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "literals_only",
|
|
|
|
|
src: []byte{0x50, 'H', 'e', 'l', 'l', 'o'},
|
|
|
|
|
dstSize: 16,
|
|
|
|
|
wantDst: []byte("Hello"),
|
|
|
|
|
wantN: 5,
|
|
|
|
|
wantCode: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "literals_and_match",
|
|
|
|
|
src: []byte{0x54, 'A', 'A', 'A', 'A', 'A', 0x05, 0x00, 0x30, 'B', 'B', 'B'},
|
|
|
|
|
dstSize: 32,
|
|
|
|
|
wantDst: []byte("AAAAAAAAAAAAABBB"),
|
|
|
|
|
wantN: 16,
|
|
|
|
|
wantCode: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "overlapping_match",
|
|
|
|
|
// 1 literal 'X', then match offset=1 length=4+4=8 → "XXXXXXXXX",
|
|
|
|
|
// then final 1 literal 'Y'.
|
|
|
|
|
src: []byte{0x14, 'X', 0x01, 0x00, 0x10, 'Y'},
|
|
|
|
|
dstSize: 16,
|
|
|
|
|
wantDst: []byte("XXXXXXXXXY"),
|
|
|
|
|
wantN: 10,
|
|
|
|
|
wantCode: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "malformed_truncated",
|
|
|
|
|
src: []byte{0x50, 'H', 'e'}, // claims 5 literals, has 2
|
|
|
|
|
dstSize: 16,
|
|
|
|
|
wantN: 0,
|
|
|
|
|
wantCode: 1,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "zero_offset",
|
|
|
|
|
src: []byte{0x14, 'X', 0x00, 0x00},
|
|
|
|
|
dstSize: 16,
|
|
|
|
|
wantN: 0,
|
|
|
|
|
wantCode: 2,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "empty_token",
|
|
|
|
|
src: []byte{0x00}, // 0 literals, end of block
|
|
|
|
|
dstSize: 16,
|
|
|
|
|
wantDst: nil,
|
|
|
|
|
wantN: 0,
|
|
|
|
|
wantCode: 0,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
dst := make([]byte, tt.dstSize)
|
|
|
|
|
n, code := callDecodeBlockAVX2(t, k, tt.src, dst)
|
|
|
|
|
if n != tt.wantN || code != tt.wantCode {
|
|
|
|
|
t.Fatalf("decodeBlockAVX2: got (n=%d, code=%d), want (n=%d, code=%d)",
|
|
|
|
|
n, code, tt.wantN, tt.wantCode)
|
|
|
|
|
}
|
|
|
|
|
if tt.wantCode == 0 && tt.wantDst != nil {
|
|
|
|
|
if !bytes.Equal(dst[:n], tt.wantDst) {
|
|
|
|
|
t.Errorf("output mismatch:\n got %q\n want %q", dst[:n], tt.wantDst)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLZ4WideCopyAVX2(t *testing.T) {
|
|
|
|
|
k := loadLZ4Kernel(t)
|
|
|
|
|
|
|
|
|
|
sizes := []int{0, 1, 15, 16, 31, 32, 33, 63, 64, 100, 256, 1024}
|
|
|
|
|
for _, n := range sizes {
|
|
|
|
|
src := make([]byte, n)
|
|
|
|
|
for i := range src {
|
|
|
|
|
src[i] = byte(i*13 + 7)
|
|
|
|
|
}
|
|
|
|
|
dst := make([]byte, n)
|
|
|
|
|
|
|
|
|
|
args := make([]byte, 48)
|
|
|
|
|
if n > 0 {
|
|
|
|
|
PutPtr(args, 0, unsafe.Pointer(&dst[0]))
|
|
|
|
|
PutPtr(args, 24, unsafe.Pointer(&src[0]))
|
|
|
|
|
}
|
|
|
|
|
PutUint64(args, 8, uint64(n))
|
|
|
|
|
PutUint64(args, 16, uint64(n))
|
|
|
|
|
PutUint64(args, 32, uint64(n))
|
|
|
|
|
PutUint64(args, 40, uint64(n))
|
|
|
|
|
|
|
|
|
|
_, err := k.CallFunc("wideCopyAVX2", args)
|
2026-08-05 23:09:54 +02:00
|
|
|
runtime.KeepAlive(dst)
|
|
|
|
|
runtime.KeepAlive(src)
|
2026-07-22 09:28:11 +02:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("wideCopyAVX2(n=%d): %v", n, err)
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(dst, src) {
|
|
|
|
|
t.Errorf("wideCopyAVX2(n=%d): output mismatch", n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|