mirror of
https://github.com/taigrr/arc
synced 2025-01-18 04:33:13 -08:00
initial import
This commit is contained in:
140
binary/binary.go
Normal file
140
binary/binary.go
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright (C) 2016 - Will Glozer. All rights reserved.
|
||||
|
||||
package binary
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type ByteOrder binary.ByteOrder
|
||||
|
||||
var (
|
||||
BE ByteOrder = binary.BigEndian
|
||||
LE ByteOrder = binary.LittleEndian
|
||||
)
|
||||
|
||||
func Write(w io.Writer, order ByteOrder, data interface{}) error {
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
t := v.Type()
|
||||
|
||||
out := make([]byte, size(v, t))
|
||||
buf := out
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
v := v.Field(i)
|
||||
t := v.Type()
|
||||
|
||||
if skip(v, t) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Int8:
|
||||
buf[0] = byte(v.Int())
|
||||
case reflect.Uint8:
|
||||
buf[0] = byte(v.Uint())
|
||||
case reflect.Int16:
|
||||
order.PutUint16(buf, uint16(v.Int()))
|
||||
case reflect.Uint16:
|
||||
order.PutUint16(buf, uint16(v.Uint()))
|
||||
case reflect.Int32:
|
||||
order.PutUint32(buf, uint32(v.Int()))
|
||||
case reflect.Uint32:
|
||||
order.PutUint32(buf, uint32(v.Uint()))
|
||||
case reflect.Int64:
|
||||
order.PutUint64(buf, uint64(v.Int()))
|
||||
case reflect.Uint64:
|
||||
order.PutUint64(buf, uint64(v.Uint()))
|
||||
case reflect.Array:
|
||||
copy(buf, v.Slice(0, v.Len()).Bytes())
|
||||
}
|
||||
|
||||
buf = buf[t.Size():]
|
||||
}
|
||||
|
||||
_, err := w.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func Read(r io.Reader, order ByteOrder, data interface{}) error {
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
t := v.Type()
|
||||
|
||||
buf := make([]byte, size(v, t))
|
||||
|
||||
_, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
v := v.Field(i)
|
||||
t := v.Type()
|
||||
|
||||
if skip(v, t) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Int8:
|
||||
v.SetInt(int64(buf[0]))
|
||||
case reflect.Uint8:
|
||||
v.SetUint(uint64(buf[0]))
|
||||
case reflect.Int16:
|
||||
v.SetInt(int64(order.Uint16(buf)))
|
||||
case reflect.Uint16:
|
||||
v.SetUint(uint64(order.Uint16(buf)))
|
||||
case reflect.Int32:
|
||||
v.SetInt(int64(order.Uint32(buf)))
|
||||
case reflect.Uint32:
|
||||
v.SetUint(uint64(order.Uint32(buf)))
|
||||
case reflect.Int64:
|
||||
v.SetInt(int64(order.Uint64(buf)))
|
||||
case reflect.Uint64:
|
||||
v.SetUint(uint64(order.Uint64(buf)))
|
||||
case reflect.Array:
|
||||
reflect.Copy(v, reflect.ValueOf(buf))
|
||||
}
|
||||
|
||||
buf = buf[t.Size():]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func size(v reflect.Value, t reflect.Type) uintptr {
|
||||
size := uintptr(0)
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
v := v.Field(i)
|
||||
t := v.Type()
|
||||
if !skip(v, t) {
|
||||
size += t.Size()
|
||||
}
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
func skip(v reflect.Value, t reflect.Type) bool {
|
||||
if !v.CanSet() {
|
||||
return true
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Int8, reflect.Uint8:
|
||||
return false
|
||||
case reflect.Int16, reflect.Uint16:
|
||||
return false
|
||||
case reflect.Int32, reflect.Uint32:
|
||||
return false
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
return false
|
||||
case reflect.Array:
|
||||
return t.Elem().Size() != 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
168
binary/binary_test.go
Normal file
168
binary/binary_test.go
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright (C) 2016 - Will Glozer. All rights reserved.
|
||||
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBinaryArray(t *testing.T) {
|
||||
type Values struct {
|
||||
Byte byte
|
||||
Array [3]byte
|
||||
Int64 int64
|
||||
}
|
||||
|
||||
in := &Values{1, [3]byte{2, 3, 4}, 0xAC00BD00}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
out := &Values{}
|
||||
|
||||
if err := Write(buf, binary.LittleEndian, in); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := Read(buf, binary.LittleEndian, out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(in, out) {
|
||||
t.Fatalf("round trip serialization failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBinaryMinMax(t *testing.T) {
|
||||
type Values struct {
|
||||
MinInt8 int8
|
||||
MaxInt8 int8
|
||||
MaxUint8 uint8
|
||||
MinInt16 int16
|
||||
MaxInt16 int16
|
||||
MaxUint16 uint16
|
||||
MinInt32 int32
|
||||
MaxInt32 int32
|
||||
MaxUint32 uint32
|
||||
MinInt64 int64
|
||||
MaxInt64 int64
|
||||
MaxUint64 uint64
|
||||
}
|
||||
|
||||
in := &Values{
|
||||
MinInt8: math.MinInt8,
|
||||
MaxInt8: math.MaxInt8,
|
||||
MaxUint8: 1<<8 - 1,
|
||||
MinInt16: math.MinInt16,
|
||||
MaxInt16: math.MaxInt16,
|
||||
MaxUint16: 1<<16 - 1,
|
||||
MinInt32: math.MinInt32,
|
||||
MaxInt32: math.MaxInt32,
|
||||
MaxUint32: 1<<32 - 1,
|
||||
MinInt64: math.MinInt64,
|
||||
MaxInt64: math.MaxInt64,
|
||||
MaxUint64: 1<<64 - 1,
|
||||
}
|
||||
|
||||
out := &Values{}
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := Write(buf, binary.LittleEndian, in); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := Read(buf, binary.LittleEndian, out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(in, out) {
|
||||
t.Fatalf("round trip serialization failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBinaryByteOrder(t *testing.T) {
|
||||
type Values struct {
|
||||
Uint16 uint16
|
||||
Uint32 uint32
|
||||
Uint64 uint64
|
||||
}
|
||||
|
||||
in := &Values{
|
||||
Uint16: 0x1234,
|
||||
Uint32: 0x12345678,
|
||||
Uint64: 0x1234567890ABCDEF,
|
||||
}
|
||||
|
||||
little := []byte{0xEF, 0xCD, 0xAB, 0x90, 0x78, 0x56, 0x34, 0x12}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := Write(buf, binary.LittleEndian, in); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf.Bytes()[0:2], little[6:8]) {
|
||||
t.Fatalf("uint16 little endian incorrect")
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf.Bytes()[2:6], little[4:8]) {
|
||||
t.Fatalf("uint32 little endian incorrect")
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf.Bytes()[6:14], little[0:8]) {
|
||||
t.Fatalf("uint64 little endian incorrect")
|
||||
}
|
||||
buf.Reset()
|
||||
|
||||
big := []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF}
|
||||
|
||||
if err := Write(buf, binary.BigEndian, in); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf.Bytes()[0:2], big[0:2]) {
|
||||
t.Fatalf("uint16 big endian incorrect")
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf.Bytes()[2:6], big[0:4]) {
|
||||
t.Fatalf("uint32 big endian incorrect")
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf.Bytes()[6:14], big[0:8]) {
|
||||
t.Fatalf("uint64 big endian incorrect")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBinarySkip(t *testing.T) {
|
||||
type Values struct {
|
||||
A byte
|
||||
B string
|
||||
C int32
|
||||
D []byte
|
||||
e byte
|
||||
}
|
||||
|
||||
in := &Values{1, "foo", 0xABCDEF, []byte("bar"), 2}
|
||||
|
||||
out := &Values{}
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := Write(buf, binary.LittleEndian, in); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := Read(buf, binary.LittleEndian, out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
in.B = ""
|
||||
in.D = nil
|
||||
in.e = 0
|
||||
|
||||
if !reflect.DeepEqual(in, out) {
|
||||
t.Fatal("round trip serialization failed")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user