diff options
Diffstat (limited to 'pkg/binary/binary.go')
-rw-r--r-- | pkg/binary/binary.go | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/pkg/binary/binary.go b/pkg/binary/binary.go new file mode 100644 index 000000000..25065aef9 --- /dev/null +++ b/pkg/binary/binary.go @@ -0,0 +1,266 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package binary translates between select fixed-sized types and a binary +// representation. +package binary + +import ( + "encoding/binary" + "fmt" + "io" + "reflect" +) + +// LittleEndian is the same as encoding/binary.LittleEndian. +// +// It is included here as a convenience. +var LittleEndian = binary.LittleEndian + +// BigEndian is the same as encoding/binary.BigEndian. +// +// It is included here as a convenience. +var BigEndian = binary.BigEndian + +// AppendUint16 appends the binary representation of a uint16 to buf. +func AppendUint16(buf []byte, order binary.ByteOrder, num uint16) []byte { + buf = append(buf, make([]byte, 2)...) + order.PutUint16(buf[len(buf)-2:], num) + return buf +} + +// AppendUint32 appends the binary representation of a uint32 to buf. +func AppendUint32(buf []byte, order binary.ByteOrder, num uint32) []byte { + buf = append(buf, make([]byte, 4)...) + order.PutUint32(buf[len(buf)-4:], num) + return buf +} + +// AppendUint64 appends the binary representation of a uint64 to buf. +func AppendUint64(buf []byte, order binary.ByteOrder, num uint64) []byte { + buf = append(buf, make([]byte, 8)...) + order.PutUint64(buf[len(buf)-8:], num) + return buf +} + +// Marshal appends a binary representation of data to buf. +// +// data must only contain fixed-length signed and unsigned ints, arrays, +// slices, structs and compositions of said types. data may be a pointer, +// but cannot contain pointers. +func Marshal(buf []byte, order binary.ByteOrder, data interface{}) []byte { + return marshal(buf, order, reflect.Indirect(reflect.ValueOf(data))) +} + +func marshal(buf []byte, order binary.ByteOrder, data reflect.Value) []byte { + switch data.Kind() { + case reflect.Int8: + buf = append(buf, byte(int8(data.Int()))) + case reflect.Int16: + buf = AppendUint16(buf, order, uint16(int16(data.Int()))) + case reflect.Int32: + buf = AppendUint32(buf, order, uint32(int32(data.Int()))) + case reflect.Int64: + buf = AppendUint64(buf, order, uint64(data.Int())) + + case reflect.Uint8: + buf = append(buf, byte(data.Uint())) + case reflect.Uint16: + buf = AppendUint16(buf, order, uint16(data.Uint())) + case reflect.Uint32: + buf = AppendUint32(buf, order, uint32(data.Uint())) + case reflect.Uint64: + buf = AppendUint64(buf, order, data.Uint()) + + case reflect.Array, reflect.Slice: + for i, l := 0, data.Len(); i < l; i++ { + buf = marshal(buf, order, data.Index(i)) + } + + case reflect.Struct: + for i, l := 0, data.NumField(); i < l; i++ { + buf = marshal(buf, order, data.Field(i)) + } + + default: + panic("invalid type: " + data.Type().String()) + } + return buf +} + +// Unmarshal unpacks buf into data. +// +// data must be a slice or a pointer and buf must have a length of exactly +// Size(data). data must only contain fixed-length signed and unsigned ints, +// arrays, slices, structs and compositions of said types. +func Unmarshal(buf []byte, order binary.ByteOrder, data interface{}) { + value := reflect.ValueOf(data) + switch value.Kind() { + case reflect.Ptr: + value = value.Elem() + case reflect.Slice: + default: + panic("invalid type: " + value.Type().String()) + } + buf = unmarshal(buf, order, value) + if len(buf) != 0 { + panic(fmt.Sprintf("buffer too long by %d bytes", len(buf))) + } +} + +func unmarshal(buf []byte, order binary.ByteOrder, data reflect.Value) []byte { + switch data.Kind() { + case reflect.Int8: + data.SetInt(int64(int8(buf[0]))) + buf = buf[1:] + case reflect.Int16: + data.SetInt(int64(int16(order.Uint16(buf)))) + buf = buf[2:] + case reflect.Int32: + data.SetInt(int64(int32(order.Uint32(buf)))) + buf = buf[4:] + case reflect.Int64: + data.SetInt(int64(order.Uint64(buf))) + buf = buf[8:] + + case reflect.Uint8: + data.SetUint(uint64(buf[0])) + buf = buf[1:] + case reflect.Uint16: + data.SetUint(uint64(order.Uint16(buf))) + buf = buf[2:] + case reflect.Uint32: + data.SetUint(uint64(order.Uint32(buf))) + buf = buf[4:] + case reflect.Uint64: + data.SetUint(order.Uint64(buf)) + buf = buf[8:] + + case reflect.Array, reflect.Slice: + for i, l := 0, data.Len(); i < l; i++ { + buf = unmarshal(buf, order, data.Index(i)) + } + + case reflect.Struct: + for i, l := 0, data.NumField(); i < l; i++ { + if field := data.Field(i); field.CanSet() { + buf = unmarshal(buf, order, field) + } else { + buf = buf[sizeof(field):] + } + } + + default: + panic("invalid type: " + data.Type().String()) + } + return buf +} + +// Size calculates the buffer sized needed by Marshal or Unmarshal. +// +// Size only support the types supported by Marshal. +func Size(v interface{}) uintptr { + return sizeof(reflect.Indirect(reflect.ValueOf(v))) +} + +func sizeof(data reflect.Value) uintptr { + switch data.Kind() { + case reflect.Int8, reflect.Uint8: + return 1 + case reflect.Int16, reflect.Uint16: + return 2 + case reflect.Int32, reflect.Uint32: + return 4 + case reflect.Int64, reflect.Uint64: + return 8 + + case reflect.Array, reflect.Slice: + var size uintptr + for i, l := 0, data.Len(); i < l; i++ { + size += sizeof(data.Index(i)) + } + return size + + case reflect.Struct: + var size uintptr + for i, l := 0, data.NumField(); i < l; i++ { + size += sizeof(data.Field(i)) + } + return size + + default: + panic("invalid type: " + data.Type().String()) + } +} + +// ReadUint16 reads a uint16 from r. +func ReadUint16(r io.Reader, order binary.ByteOrder) (uint16, error) { + buf := make([]byte, 2) + if _, err := io.ReadFull(r, buf); err != nil { + return 0, err + } + return order.Uint16(buf), nil +} + +// ReadUint32 reads a uint32 from r. +func ReadUint32(r io.Reader, order binary.ByteOrder) (uint32, error) { + buf := make([]byte, 4) + if _, err := io.ReadFull(r, buf); err != nil { + return 0, err + } + return order.Uint32(buf), nil +} + +// ReadUint64 reads a uint64 from r. +func ReadUint64(r io.Reader, order binary.ByteOrder) (uint64, error) { + buf := make([]byte, 8) + if _, err := io.ReadFull(r, buf); err != nil { + return 0, err + } + return order.Uint64(buf), nil +} + +// WriteUint16 writes a uint16 to w. +func WriteUint16(w io.Writer, order binary.ByteOrder, num uint16) error { + buf := make([]byte, 2) + order.PutUint16(buf, num) + _, err := w.Write(buf) + return err +} + +// WriteUint32 writes a uint32 to w. +func WriteUint32(w io.Writer, order binary.ByteOrder, num uint32) error { + buf := make([]byte, 4) + order.PutUint32(buf, num) + _, err := w.Write(buf) + return err +} + +// WriteUint64 writes a uint64 to w. +func WriteUint64(w io.Writer, order binary.ByteOrder, num uint64) error { + buf := make([]byte, 8) + order.PutUint64(buf, num) + _, err := w.Write(buf) + return err +} + +// AlignUp rounds a length up to an alignment. align must be a power of 2. +func AlignUp(length int, align uint) int { + return (length + int(align) - 1) & ^(int(align) - 1) +} + +// AlignDown rounds a length down to an alignment. align must be a power of 2. +func AlignDown(length int, align uint) int { + return length & ^(int(align) - 1) +} |