mirror of
https://github.com/asticode/go-astikit.git
synced 2025-12-24 11:50:53 +08:00
319 lines
7.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|
|
}
|