Files
go-astikit/binary_test.go
2024-02-07 19:01:24 +01:00

319 lines
7.5 KiB
Go

package astikit
import (
"bytes"
"fmt"
"io"
"reflect"
"testing"
)
func TestBitsWriter(t *testing.T) {
// TODO Need to test LittleEndian
bw := &bytes.Buffer{}
cbBuf := bytes.Buffer{}
w := NewBitsWriter(BitsWriterOptions{
Writer: bw,
WriteCallback: func(i []byte) {
cbBuf.Write(i)
},
})
err := w.Write("000000")
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if e, g := 0, bw.Len(); e != g {
t.Fatalf("expected %d, got %d", e, g)
}
err = w.Write(false)
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
err = w.Write(true)
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if e, g := []byte{1}, bw.Bytes(); !reflect.DeepEqual(e, g) {
t.Fatalf("expected %+v, got %+v", e, g)
}
err = w.Write([]byte{2, 3})
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if e, g := []byte{1, 2, 3}, bw.Bytes(); !reflect.DeepEqual(e, g) {
t.Fatalf("expected %+v, got %+v", e, g)
}
err = w.Write(uint8(4))
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if e, g := []byte{1, 2, 3, 4}, bw.Bytes(); !reflect.DeepEqual(e, g) {
t.Fatalf("expected %+v, got %+v", e, g)
}
err = w.Write(uint16(5))
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if e, g := []byte{1, 2, 3, 4, 0, 5}, bw.Bytes(); !reflect.DeepEqual(e, g) {
t.Fatalf("expected %+v, got %+v", e, g)
}
err = w.Write(uint32(6))
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if e, g := []byte{1, 2, 3, 4, 0, 5, 0, 0, 0, 6}, bw.Bytes(); !reflect.DeepEqual(e, g) {
t.Fatalf("expected %+v, got %+v", e, g)
}
err = w.Write(uint64(7))
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if e, g := []byte{1, 2, 3, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7}, bw.Bytes(); !reflect.DeepEqual(e, g) {
t.Fatalf("expected %+v, got %+v", e, g)
}
if e, g := []byte{1, 2, 3, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7}, cbBuf.Bytes(); !reflect.DeepEqual(e, g) {
t.Fatalf("callback buffer: expected %+v, got %+v", e, g)
}
err = w.Write(1)
if err == nil {
t.Fatal("expected error")
}
bw.Reset()
err = w.WriteN(uint8(4), 3)
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
err = w.WriteN(uint16(4096), 13)
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if e, g := []byte{144, 0}, bw.Bytes(); !reflect.DeepEqual(e, g) {
t.Fatalf("expected %+v, got %+v", e, g)
}
}
var bitsWriter_WriteBytesN_testCases = []struct {
bs []byte
n int
expected []byte
}{
{nil, 0, nil},
{[]byte{0x00}, 0, nil},
{nil, 3, []byte{0xff, 0xff, 0xff}},
{[]byte("en"), 3, []byte{'e', 'n', 0xff}},
{[]byte("eng"), 3, []byte{'e', 'n', 'g'}},
{[]byte("english"), 3, []byte{'e', 'n', 'g'}},
}
func TestBitsWriter_WriteBytesN(t *testing.T) {
padByte := uint8(0xff)
for _, tt := range bitsWriter_WriteBytesN_testCases {
t.Run(fmt.Sprintf("%v/%d", tt.bs, tt.n), func(t *testing.T) {
buf := bytes.Buffer{}
w := NewBitsWriter(BitsWriterOptions{Writer: &buf})
err := w.WriteBytesN(tt.bs, tt.n, padByte)
if err != nil {
t.Fatalf("expected no error, got %+v", err)
}
if !reflect.DeepEqual(tt.expected, buf.Bytes()) {
t.Fatalf("expected %+v, got %+v", tt.expected, buf.Bytes())
}
})
}
}
// testLimitedWriter is an implementation of io.Writer with max write size limit to test error handling
type testLimitedWriter struct {
BytesLimit int
}
func (t *testLimitedWriter) Write(p []byte) (n int, err error) {
t.BytesLimit -= len(p)
if t.BytesLimit >= 0 {
return len(p), nil
}
return len(p) + t.BytesLimit, io.EOF
}
func TestNewBitsWriterBatch(t *testing.T) {
wr := &testLimitedWriter{BytesLimit: 1}
w := NewBitsWriter(BitsWriterOptions{Writer: wr})
b := NewBitsWriterBatch(w)
b.Write(uint8(0))
if err := b.Err(); err != nil {
t.Fatalf("expected no error, got %+v", err)
}
b.Write(uint8(1))
if err := b.Err(); err == nil {
t.Fatalf("expected error, got %+v", err)
}
// let's check if the error is persisted
b.Write(uint8(2))
if err := b.Err(); err == nil {
t.Fatalf("expected error, got %+v", err)
}
}
func BenchmarkBitsWriter_Write(b *testing.B) {
benchmarks := []struct {
input interface{}
}{
{"000000"},
{false},
{true},
{[]byte{2, 3}},
{uint8(4)},
{uint16(5)},
{uint32(6)},
{uint64(7)},
}
bw := &bytes.Buffer{}
bw.Grow(1024)
w := NewBitsWriter(BitsWriterOptions{Writer: bw})
for _, bm := range benchmarks {
b.Run(fmt.Sprintf("%#v", bm.input), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bw.Reset()
w.Write(bm.input) //nolint:errcheck
}
})
}
}
func BenchmarkBitsWriter_WriteN(b *testing.B) {
type benchData struct {
i interface{}
n int
}
benchmarks := make([]benchData, 0, 128)
for i := 1; i <= 8; i++ {
benchmarks = append(benchmarks, benchData{uint8(0xff), i})
}
for i := 1; i <= 16; i++ {
benchmarks = append(benchmarks, benchData{uint16(0xffff), i})
}
for i := 1; i <= 32; i++ {
benchmarks = append(benchmarks, benchData{uint32(0xffffffff), i})
}
for i := 1; i <= 64; i++ {
benchmarks = append(benchmarks, benchData{uint64(0xffffffffffffffff), i})
}
bw := &bytes.Buffer{}
bw.Grow(1024)
w := NewBitsWriter(BitsWriterOptions{Writer: bw})
for _, bm := range benchmarks {
b.Run(fmt.Sprintf("%#v/%d", bm.i, bm.n), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bw.Reset()
w.WriteN(bm.i, bm.n) //nolint:errcheck
}
})
}
}
func BenchmarkBitsWriter_WriteBytesN(b *testing.B) {
bw := &bytes.Buffer{}
bw.Grow(1024)
w := NewBitsWriter(BitsWriterOptions{Writer: bw})
for _, bm := range bitsWriter_WriteBytesN_testCases {
b.Run(fmt.Sprintf("%v/%d", bm.bs, bm.n), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bw.Reset()
w.WriteBytesN(bm.bs, bm.n, 0xff) //nolint:errcheck
}
})
}
}
func testByteHamming84Decode(i uint8) (o uint8, ok bool) {
p1, d1, p2, d2, p3, d3, p4, d4 := i>>7&0x1, i>>6&0x1, i>>5&0x1, i>>4&0x1, i>>3&0x1, i>>2&0x1, i>>1&0x1, i&0x1
testA := p1^d1^d3^d4 > 0
testB := d1^p2^d2^d4 > 0
testC := d1^d2^p3^d3 > 0
testD := p1^d1^p2^d2^p3^d3^p4^d4 > 0
if testA && testB && testC {
// p4 may be incorrect
} else if testD && (!testA || !testB || !testC) {
return
} else {
if !testA && testB && testC {
// p1 is incorrect
} else if testA && !testB && testC {
// p2 is incorrect
} else if testA && testB && !testC {
// p3 is incorrect
} else if !testA && !testB && testC {
// d4 is incorrect
d4 ^= 1
} else if testA && !testB && !testC {
// d2 is incorrect
d2 ^= 1
} else if !testA && testB && !testC {
// d3 is incorrect
d3 ^= 1
} else {
// d1 is incorrect
d1 ^= 1
}
}
o = uint8(d4<<3 | d3<<2 | d2<<1 | d1)
ok = true
return
}
func TestByteHamming84Decode(t *testing.T) {
for i := 0; i < 256; i++ {
v, okV := ByteHamming84Decode(uint8(i))
e, okE := testByteHamming84Decode(uint8(i))
if !okE {
if okV {
t.Fatal("expected false, got true")
}
} else {
if !okV {
t.Fatal("expected true, got false")
}
if !reflect.DeepEqual(e, v) {
t.Fatalf("expected %+v, got %+v", e, v)
}
}
}
}
func testByteParity(i uint8) bool {
return (i&0x1)^(i>>1&0x1)^(i>>2&0x1)^(i>>3&0x1)^(i>>4&0x1)^(i>>5&0x1)^(i>>6&0x1)^(i>>7&0x1) > 0
}
func TestByteParity(t *testing.T) {
for i := 0; i < 256; i++ {
v, okV := ByteParity(uint8(i))
okE := testByteParity(uint8(i))
if !okE {
if okV {
t.Fatal("expected false, got true")
}
} else {
if !okV {
t.Fatal("expected true, got false")
}
if e := uint8(i) & 0x7f; e != v {
t.Fatalf("expected %+v, got %+v", e, v)
}
}
}
}