package binary
import (
"errors"
"io"
"math"
"reflect"
"slices"
"sync"
)
var errBufferTooSmall = errors .New ("buffer too small" )
type ByteOrder interface {
Uint16 ([]byte ) uint16
Uint32 ([]byte ) uint32
Uint64 ([]byte ) uint64
PutUint16 ([]byte , uint16 )
PutUint32 ([]byte , uint32 )
PutUint64 ([]byte , uint64 )
String () string
}
type AppendByteOrder interface {
AppendUint16 ([]byte , uint16 ) []byte
AppendUint32 ([]byte , uint32 ) []byte
AppendUint64 ([]byte , uint64 ) []byte
String () string
}
var LittleEndian littleEndian
var BigEndian bigEndian
type littleEndian struct {}
func (littleEndian ) Uint16 (b []byte ) uint16 {
_ = b [1 ]
return uint16 (b [0 ]) | uint16 (b [1 ])<<8
}
func (littleEndian ) PutUint16 (b []byte , v uint16 ) {
_ = b [1 ]
b [0 ] = byte (v )
b [1 ] = byte (v >> 8 )
}
func (littleEndian ) AppendUint16 (b []byte , v uint16 ) []byte {
return append (b ,
byte (v ),
byte (v >>8 ),
)
}
func (littleEndian ) Uint32 (b []byte ) uint32 {
_ = b [3 ]
return uint32 (b [0 ]) | uint32 (b [1 ])<<8 | uint32 (b [2 ])<<16 | uint32 (b [3 ])<<24
}
func (littleEndian ) PutUint32 (b []byte , v uint32 ) {
_ = b [3 ]
b [0 ] = byte (v )
b [1 ] = byte (v >> 8 )
b [2 ] = byte (v >> 16 )
b [3 ] = byte (v >> 24 )
}
func (littleEndian ) AppendUint32 (b []byte , v uint32 ) []byte {
return append (b ,
byte (v ),
byte (v >>8 ),
byte (v >>16 ),
byte (v >>24 ),
)
}
func (littleEndian ) Uint64 (b []byte ) uint64 {
_ = b [7 ]
return uint64 (b [0 ]) | uint64 (b [1 ])<<8 | uint64 (b [2 ])<<16 | uint64 (b [3 ])<<24 |
uint64 (b [4 ])<<32 | uint64 (b [5 ])<<40 | uint64 (b [6 ])<<48 | uint64 (b [7 ])<<56
}
func (littleEndian ) PutUint64 (b []byte , v uint64 ) {
_ = b [7 ]
b [0 ] = byte (v )
b [1 ] = byte (v >> 8 )
b [2 ] = byte (v >> 16 )
b [3 ] = byte (v >> 24 )
b [4 ] = byte (v >> 32 )
b [5 ] = byte (v >> 40 )
b [6 ] = byte (v >> 48 )
b [7 ] = byte (v >> 56 )
}
func (littleEndian ) AppendUint64 (b []byte , v uint64 ) []byte {
return append (b ,
byte (v ),
byte (v >>8 ),
byte (v >>16 ),
byte (v >>24 ),
byte (v >>32 ),
byte (v >>40 ),
byte (v >>48 ),
byte (v >>56 ),
)
}
func (littleEndian ) String () string { return "LittleEndian" }
func (littleEndian ) GoString () string { return "binary.LittleEndian" }
type bigEndian struct {}
func (bigEndian ) Uint16 (b []byte ) uint16 {
_ = b [1 ]
return uint16 (b [1 ]) | uint16 (b [0 ])<<8
}
func (bigEndian ) PutUint16 (b []byte , v uint16 ) {
_ = b [1 ]
b [0 ] = byte (v >> 8 )
b [1 ] = byte (v )
}
func (bigEndian ) AppendUint16 (b []byte , v uint16 ) []byte {
return append (b ,
byte (v >>8 ),
byte (v ),
)
}
func (bigEndian ) Uint32 (b []byte ) uint32 {
_ = b [3 ]
return uint32 (b [3 ]) | uint32 (b [2 ])<<8 | uint32 (b [1 ])<<16 | uint32 (b [0 ])<<24
}
func (bigEndian ) PutUint32 (b []byte , v uint32 ) {
_ = b [3 ]
b [0 ] = byte (v >> 24 )
b [1 ] = byte (v >> 16 )
b [2 ] = byte (v >> 8 )
b [3 ] = byte (v )
}
func (bigEndian ) AppendUint32 (b []byte , v uint32 ) []byte {
return append (b ,
byte (v >>24 ),
byte (v >>16 ),
byte (v >>8 ),
byte (v ),
)
}
func (bigEndian ) Uint64 (b []byte ) uint64 {
_ = b [7 ]
return uint64 (b [7 ]) | uint64 (b [6 ])<<8 | uint64 (b [5 ])<<16 | uint64 (b [4 ])<<24 |
uint64 (b [3 ])<<32 | uint64 (b [2 ])<<40 | uint64 (b [1 ])<<48 | uint64 (b [0 ])<<56
}
func (bigEndian ) PutUint64 (b []byte , v uint64 ) {
_ = b [7 ]
b [0 ] = byte (v >> 56 )
b [1 ] = byte (v >> 48 )
b [2 ] = byte (v >> 40 )
b [3 ] = byte (v >> 32 )
b [4 ] = byte (v >> 24 )
b [5 ] = byte (v >> 16 )
b [6 ] = byte (v >> 8 )
b [7 ] = byte (v )
}
func (bigEndian ) AppendUint64 (b []byte , v uint64 ) []byte {
return append (b ,
byte (v >>56 ),
byte (v >>48 ),
byte (v >>40 ),
byte (v >>32 ),
byte (v >>24 ),
byte (v >>16 ),
byte (v >>8 ),
byte (v ),
)
}
func (bigEndian ) String () string { return "BigEndian" }
func (bigEndian ) GoString () string { return "binary.BigEndian" }
func (nativeEndian ) String () string { return "NativeEndian" }
func (nativeEndian ) GoString () string { return "binary.NativeEndian" }
func Read (r io .Reader , order ByteOrder , data any ) error {
if n , _ := intDataSize (data ); n != 0 {
bs := make ([]byte , n )
if _ , err := io .ReadFull (r , bs ); err != nil {
return err
}
if decodeFast (bs , order , data ) {
return nil
}
}
v := reflect .ValueOf (data )
size := -1
switch v .Kind () {
case reflect .Pointer :
v = v .Elem ()
size = dataSize (v )
case reflect .Slice :
size = dataSize (v )
}
if size < 0 {
return errors .New ("binary.Read: invalid type " + reflect .TypeOf (data ).String ())
}
d := &decoder {order : order , buf : make ([]byte , size )}
if _ , err := io .ReadFull (r , d .buf ); err != nil {
return err
}
d .value (v )
return nil
}
func Decode (buf []byte , order ByteOrder , data any ) (int , error ) {
if n , _ := intDataSize (data ); n != 0 {
if len (buf ) < n {
return 0 , errBufferTooSmall
}
if decodeFast (buf , order , data ) {
return n , nil
}
}
v := reflect .ValueOf (data )
size := -1
switch v .Kind () {
case reflect .Pointer :
v = v .Elem ()
size = dataSize (v )
case reflect .Slice :
size = dataSize (v )
}
if size < 0 {
return 0 , errors .New ("binary.Decode: invalid type " + reflect .TypeOf (data ).String ())
}
if len (buf ) < size {
return 0 , errBufferTooSmall
}
d := &decoder {order : order , buf : buf [:size ]}
d .value (v )
return size , nil
}
func decodeFast(bs []byte , order ByteOrder , data any ) bool {
switch data := data .(type ) {
case *bool :
*data = bs [0 ] != 0
case *int8 :
*data = int8 (bs [0 ])
case *uint8 :
*data = bs [0 ]
case *int16 :
*data = int16 (order .Uint16 (bs ))
case *uint16 :
*data = order .Uint16 (bs )
case *int32 :
*data = int32 (order .Uint32 (bs ))
case *uint32 :
*data = order .Uint32 (bs )
case *int64 :
*data = int64 (order .Uint64 (bs ))
case *uint64 :
*data = order .Uint64 (bs )
case *float32 :
*data = math .Float32frombits (order .Uint32 (bs ))
case *float64 :
*data = math .Float64frombits (order .Uint64 (bs ))
case []bool :
for i , x := range bs {
data [i ] = x != 0
}
case []int8 :
for i , x := range bs {
data [i ] = int8 (x )
}
case []uint8 :
copy (data , bs )
case []int16 :
for i := range data {
data [i ] = int16 (order .Uint16 (bs [2 *i :]))
}
case []uint16 :
for i := range data {
data [i ] = order .Uint16 (bs [2 *i :])
}
case []int32 :
for i := range data {
data [i ] = int32 (order .Uint32 (bs [4 *i :]))
}
case []uint32 :
for i := range data {
data [i ] = order .Uint32 (bs [4 *i :])
}
case []int64 :
for i := range data {
data [i ] = int64 (order .Uint64 (bs [8 *i :]))
}
case []uint64 :
for i := range data {
data [i ] = order .Uint64 (bs [8 *i :])
}
case []float32 :
for i := range data {
data [i ] = math .Float32frombits (order .Uint32 (bs [4 *i :]))
}
case []float64 :
for i := range data {
data [i ] = math .Float64frombits (order .Uint64 (bs [8 *i :]))
}
default :
return false
}
return true
}
func Write (w io .Writer , order ByteOrder , data any ) error {
if n , bs := intDataSize (data ); n != 0 {
if bs == nil {
bs = make ([]byte , n )
encodeFast (bs , order , data )
}
_ , err := w .Write (bs )
return err
}
v := reflect .Indirect (reflect .ValueOf (data ))
size := dataSize (v )
if size < 0 {
return errors .New ("binary.Write: some values are not fixed-sized in type " + reflect .TypeOf (data ).String ())
}
buf := make ([]byte , size )
e := &encoder {order : order , buf : buf }
e .value (v )
_ , err := w .Write (buf )
return err
}
func Encode (buf []byte , order ByteOrder , data any ) (int , error ) {
if n , _ := intDataSize (data ); n != 0 {
if len (buf ) < n {
return 0 , errBufferTooSmall
}
encodeFast (buf , order , data )
return n , nil
}
v := reflect .Indirect (reflect .ValueOf (data ))
size := dataSize (v )
if size < 0 {
return 0 , errors .New ("binary.Encode: some values are not fixed-sized in type " + reflect .TypeOf (data ).String ())
}
if len (buf ) < size {
return 0 , errBufferTooSmall
}
e := &encoder {order : order , buf : buf }
e .value (v )
return size , nil
}
func Append (buf []byte , order ByteOrder , data any ) ([]byte , error ) {
if n , _ := intDataSize (data ); n != 0 {
buf , pos := ensure (buf , n )
encodeFast (pos , order , data )
return buf , nil
}
v := reflect .Indirect (reflect .ValueOf (data ))
size := dataSize (v )
if size < 0 {
return nil , errors .New ("binary.Append: some values are not fixed-sized in type " + reflect .TypeOf (data ).String ())
}
buf , pos := ensure (buf , size )
e := &encoder {order : order , buf : pos }
e .value (v )
return buf , nil
}
func encodeFast(bs []byte , order ByteOrder , data any ) {
switch v := data .(type ) {
case *bool :
if *v {
bs [0 ] = 1
} else {
bs [0 ] = 0
}
case bool :
if v {
bs [0 ] = 1
} else {
bs [0 ] = 0
}
case []bool :
for i , x := range v {
if x {
bs [i ] = 1
} else {
bs [i ] = 0
}
}
case *int8 :
bs [0 ] = byte (*v )
case int8 :
bs [0 ] = byte (v )
case []int8 :
for i , x := range v {
bs [i ] = byte (x )
}
case *uint8 :
bs [0 ] = *v
case uint8 :
bs [0 ] = v
case []uint8 :
copy (bs , v )
case *int16 :
order .PutUint16 (bs , uint16 (*v ))
case int16 :
order .PutUint16 (bs , uint16 (v ))
case []int16 :
for i , x := range v {
order .PutUint16 (bs [2 *i :], uint16 (x ))
}
case *uint16 :
order .PutUint16 (bs , *v )
case uint16 :
order .PutUint16 (bs , v )
case []uint16 :
for i , x := range v {
order .PutUint16 (bs [2 *i :], x )
}
case *int32 :
order .PutUint32 (bs , uint32 (*v ))
case int32 :
order .PutUint32 (bs , uint32 (v ))
case []int32 :
for i , x := range v {
order .PutUint32 (bs [4 *i :], uint32 (x ))
}
case *uint32 :
order .PutUint32 (bs , *v )
case uint32 :
order .PutUint32 (bs , v )
case []uint32 :
for i , x := range v {
order .PutUint32 (bs [4 *i :], x )
}
case *int64 :
order .PutUint64 (bs , uint64 (*v ))
case int64 :
order .PutUint64 (bs , uint64 (v ))
case []int64 :
for i , x := range v {
order .PutUint64 (bs [8 *i :], uint64 (x ))
}
case *uint64 :
order .PutUint64 (bs , *v )
case uint64 :
order .PutUint64 (bs , v )
case []uint64 :
for i , x := range v {
order .PutUint64 (bs [8 *i :], x )
}
case *float32 :
order .PutUint32 (bs , math .Float32bits (*v ))
case float32 :
order .PutUint32 (bs , math .Float32bits (v ))
case []float32 :
for i , x := range v {
order .PutUint32 (bs [4 *i :], math .Float32bits (x ))
}
case *float64 :
order .PutUint64 (bs , math .Float64bits (*v ))
case float64 :
order .PutUint64 (bs , math .Float64bits (v ))
case []float64 :
for i , x := range v {
order .PutUint64 (bs [8 *i :], math .Float64bits (x ))
}
}
}
func Size (v any ) int {
switch data := v .(type ) {
case bool , int8 , uint8 :
return 1
case *bool :
if data == nil {
return -1
}
return 1
case *int8 :
if data == nil {
return -1
}
return 1
case *uint8 :
if data == nil {
return -1
}
return 1
case []bool :
return len (data )
case []int8 :
return len (data )
case []uint8 :
return len (data )
case int16 , uint16 :
return 2
case *int16 :
if data == nil {
return -1
}
return 2
case *uint16 :
if data == nil {
return -1
}
return 2
case []int16 :
return 2 * len (data )
case []uint16 :
return 2 * len (data )
case int32 , uint32 :
return 4
case *int32 :
if data == nil {
return -1
}
return 4
case *uint32 :
if data == nil {
return -1
}
return 4
case []int32 :
return 4 * len (data )
case []uint32 :
return 4 * len (data )
case int64 , uint64 :
return 8
case *int64 :
if data == nil {
return -1
}
return 8
case *uint64 :
if data == nil {
return -1
}
return 8
case []int64 :
return 8 * len (data )
case []uint64 :
return 8 * len (data )
case float32 :
return 4
case *float32 :
if data == nil {
return -1
}
return 4
case float64 :
return 8
case *float64 :
if data == nil {
return -1
}
return 8
case []float32 :
return 4 * len (data )
case []float64 :
return 8 * len (data )
}
return dataSize (reflect .Indirect (reflect .ValueOf (v )))
}
var structSize sync .Map
func dataSize(v reflect .Value ) int {
switch v .Kind () {
case reflect .Slice , reflect .Array :
t := v .Type ().Elem ()
if size , ok := structSize .Load (t ); ok {
return size .(int ) * v .Len ()
}
size := sizeof (t )
if size >= 0 {
if t .Kind () == reflect .Struct {
structSize .Store (t , size )
}
return size * v .Len ()
}
case reflect .Struct :
t := v .Type ()
if size , ok := structSize .Load (t ); ok {
return size .(int )
}
size := sizeof (t )
structSize .Store (t , size )
return size
default :
if v .IsValid () {
return sizeof (v .Type ())
}
}
return -1
}
func sizeof(t reflect .Type ) int {
switch t .Kind () {
case reflect .Array :
if s := sizeof (t .Elem ()); s >= 0 {
return s * t .Len ()
}
case reflect .Struct :
sum := 0
for i , n := 0 , t .NumField (); i < n ; i ++ {
s := sizeof (t .Field (i ).Type )
if s < 0 {
return -1
}
sum += s
}
return sum
case reflect .Bool ,
reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 ,
reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
reflect .Float32 , reflect .Float64 , reflect .Complex64 , reflect .Complex128 :
return int (t .Size ())
}
return -1
}
type coder struct {
order ByteOrder
buf []byte
offset int
}
type decoder coder
type encoder coder
func (d *decoder ) bool () bool {
x := d .buf [d .offset ]
d .offset ++
return x != 0
}
func (e *encoder ) bool (x bool ) {
if x {
e .buf [e .offset ] = 1
} else {
e .buf [e .offset ] = 0
}
e .offset ++
}
func (d *decoder ) uint8 () uint8 {
x := d .buf [d .offset ]
d .offset ++
return x
}
func (e *encoder ) uint8 (x uint8 ) {
e .buf [e .offset ] = x
e .offset ++
}
func (d *decoder ) uint16 () uint16 {
x := d .order .Uint16 (d .buf [d .offset : d .offset +2 ])
d .offset += 2
return x
}
func (e *encoder ) uint16 (x uint16 ) {
e .order .PutUint16 (e .buf [e .offset :e .offset +2 ], x )
e .offset += 2
}
func (d *decoder ) uint32 () uint32 {
x := d .order .Uint32 (d .buf [d .offset : d .offset +4 ])
d .offset += 4
return x
}
func (e *encoder ) uint32 (x uint32 ) {
e .order .PutUint32 (e .buf [e .offset :e .offset +4 ], x )
e .offset += 4
}
func (d *decoder ) uint64 () uint64 {
x := d .order .Uint64 (d .buf [d .offset : d .offset +8 ])
d .offset += 8
return x
}
func (e *encoder ) uint64 (x uint64 ) {
e .order .PutUint64 (e .buf [e .offset :e .offset +8 ], x )
e .offset += 8
}
func (d *decoder ) int8 () int8 { return int8 (d .uint8 ()) }
func (e *encoder ) int8 (x int8 ) { e .uint8 (uint8 (x )) }
func (d *decoder ) int16 () int16 { return int16 (d .uint16 ()) }
func (e *encoder ) int16 (x int16 ) { e .uint16 (uint16 (x )) }
func (d *decoder ) int32 () int32 { return int32 (d .uint32 ()) }
func (e *encoder ) int32 (x int32 ) { e .uint32 (uint32 (x )) }
func (d *decoder ) int64 () int64 { return int64 (d .uint64 ()) }
func (e *encoder ) int64 (x int64 ) { e .uint64 (uint64 (x )) }
func (d *decoder ) value (v reflect .Value ) {
switch v .Kind () {
case reflect .Array :
l := v .Len ()
for i := 0 ; i < l ; i ++ {
d .value (v .Index (i ))
}
case reflect .Struct :
t := v .Type ()
l := v .NumField ()
for i := 0 ; i < l ; i ++ {
if v := v .Field (i ); v .CanSet () || t .Field (i ).Name != "_" {
d .value (v )
} else {
d .skip (v )
}
}
case reflect .Slice :
l := v .Len ()
for i := 0 ; i < l ; i ++ {
d .value (v .Index (i ))
}
case reflect .Bool :
v .SetBool (d .bool ())
case reflect .Int8 :
v .SetInt (int64 (d .int8 ()))
case reflect .Int16 :
v .SetInt (int64 (d .int16 ()))
case reflect .Int32 :
v .SetInt (int64 (d .int32 ()))
case reflect .Int64 :
v .SetInt (d .int64 ())
case reflect .Uint8 :
v .SetUint (uint64 (d .uint8 ()))
case reflect .Uint16 :
v .SetUint (uint64 (d .uint16 ()))
case reflect .Uint32 :
v .SetUint (uint64 (d .uint32 ()))
case reflect .Uint64 :
v .SetUint (d .uint64 ())
case reflect .Float32 :
v .SetFloat (float64 (math .Float32frombits (d .uint32 ())))
case reflect .Float64 :
v .SetFloat (math .Float64frombits (d .uint64 ()))
case reflect .Complex64 :
v .SetComplex (complex (
float64 (math .Float32frombits (d .uint32 ())),
float64 (math .Float32frombits (d .uint32 ())),
))
case reflect .Complex128 :
v .SetComplex (complex (
math .Float64frombits (d .uint64 ()),
math .Float64frombits (d .uint64 ()),
))
}
}
func (e *encoder ) value (v reflect .Value ) {
switch v .Kind () {
case reflect .Array :
l := v .Len ()
for i := 0 ; i < l ; i ++ {
e .value (v .Index (i ))
}
case reflect .Struct :
t := v .Type ()
l := v .NumField ()
for i := 0 ; i < l ; i ++ {
if v := v .Field (i ); v .CanSet () || t .Field (i ).Name != "_" {
e .value (v )
} else {
e .skip (v )
}
}
case reflect .Slice :
l := v .Len ()
for i := 0 ; i < l ; i ++ {
e .value (v .Index (i ))
}
case reflect .Bool :
e .bool (v .Bool ())
case reflect .Int8 :
e .int8 (int8 (v .Int ()))
case reflect .Int16 :
e .int16 (int16 (v .Int ()))
case reflect .Int32 :
e .int32 (int32 (v .Int ()))
case reflect .Int64 :
e .int64 (v .Int ())
case reflect .Uint8 :
e .uint8 (uint8 (v .Uint ()))
case reflect .Uint16 :
e .uint16 (uint16 (v .Uint ()))
case reflect .Uint32 :
e .uint32 (uint32 (v .Uint ()))
case reflect .Uint64 :
e .uint64 (v .Uint ())
case reflect .Float32 :
e .uint32 (math .Float32bits (float32 (v .Float ())))
case reflect .Float64 :
e .uint64 (math .Float64bits (v .Float ()))
case reflect .Complex64 :
x := v .Complex ()
e .uint32 (math .Float32bits (float32 (real (x ))))
e .uint32 (math .Float32bits (float32 (imag (x ))))
case reflect .Complex128 :
x := v .Complex ()
e .uint64 (math .Float64bits (real (x )))
e .uint64 (math .Float64bits (imag (x )))
}
}
func (d *decoder ) skip (v reflect .Value ) {
d .offset += dataSize (v )
}
func (e *encoder ) skip (v reflect .Value ) {
n := dataSize (v )
clear (e .buf [e .offset : e .offset +n ])
e .offset += n
}
func intDataSize(data any ) (int , []byte ) {
switch data := data .(type ) {
case bool , int8 , uint8 , *bool , *int8 , *uint8 :
return 1 , nil
case []bool :
return len (data ), nil
case []int8 :
return len (data ), nil
case []uint8 :
return len (data ), data
case int16 , uint16 , *int16 , *uint16 :
return 2 , nil
case []int16 :
return 2 * len (data ), nil
case []uint16 :
return 2 * len (data ), nil
case int32 , uint32 , *int32 , *uint32 :
return 4 , nil
case []int32 :
return 4 * len (data ), nil
case []uint32 :
return 4 * len (data ), nil
case int64 , uint64 , *int64 , *uint64 :
return 8 , nil
case []int64 :
return 8 * len (data ), nil
case []uint64 :
return 8 * len (data ), nil
case float32 , *float32 :
return 4 , nil
case float64 , *float64 :
return 8 , nil
case []float32 :
return 4 * len (data ), nil
case []float64 :
return 8 * len (data ), nil
}
return 0 , nil
}
func ensure(buf []byte , n int ) (buf2 , pos []byte ) {
l := len (buf )
buf = slices .Grow (buf , n )[:l +n ]
return buf , buf [l :]
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .