2 Star 2 Fork 6

王布衣 / gox

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
field.go 6.32 KB
一键复制 编辑 原始数据 按行查看 历史
package struc
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"reflect"
)
type Field struct {
Name string
Ptr bool
Index int
Type Type
defType Type
Array bool
Slice bool
Len int
Order binary.ByteOrder
Sizeof []int
Sizefrom []int
Fields Fields
kind reflect.Kind
}
func (f *Field) String() string {
var out string
if f.Type == Pad {
return fmt.Sprintf("{type: Pad, len: %d}", f.Len)
} else {
out = fmt.Sprintf("type: %s, order: %v", f.Type.String(), f.Order)
}
if f.Sizefrom != nil {
out += fmt.Sprintf(", sizefrom: %v", f.Sizefrom)
} else if f.Len > 0 {
out += fmt.Sprintf(", len: %d", f.Len)
}
if f.Sizeof != nil {
out += fmt.Sprintf(", sizeof: %v", f.Sizeof)
}
return "{" + out + "}"
}
func (f *Field) Size(val reflect.Value, options *Options) int {
typ := f.Type.Resolve(options)
size := 0
if typ == Struct {
vals := []reflect.Value{val}
if f.Slice {
vals = make([]reflect.Value, val.Len())
for i := 0; i < val.Len(); i++ {
vals[i] = val.Index(i)
}
}
for _, val := range vals {
size += f.Fields.Sizeof(val, options)
}
} else if typ == Pad {
size = f.Len
} else if f.Slice || f.kind == reflect.String {
length := val.Len()
if f.Len > 1 {
length = f.Len
}
size = length * typ.Size()
} else if typ == CustomType {
return val.Addr().Interface().(Custom).Size(options)
} else {
size = typ.Size()
}
align := options.ByteAlign
if align > 0 && size < align {
size = align
}
return size
}
func (f *Field) packVal(buf []byte, val reflect.Value, length int, options *Options) (size int, err error) {
order := f.Order
if options.Order != nil {
order = options.Order
}
if f.Ptr {
val = val.Elem()
}
typ := f.Type.Resolve(options)
switch typ {
case Struct:
return f.Fields.Pack(buf, val, options)
case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64:
size = typ.Size()
var n uint64
switch f.kind {
case reflect.Bool:
if val.Bool() {
n = 1
} else {
n = 0
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n = uint64(val.Int())
default:
n = val.Uint()
}
switch typ {
case Bool:
if n != 0 {
buf[0] = 1
} else {
buf[0] = 0
}
case Int8, Uint8:
buf[0] = byte(n)
case Int16, Uint16:
order.PutUint16(buf, uint16(n))
case Int32, Uint32:
order.PutUint32(buf, uint32(n))
case Int64, Uint64:
order.PutUint64(buf, uint64(n))
}
case Float32, Float64:
size = typ.Size()
n := val.Float()
switch typ {
case Float32:
order.PutUint32(buf, math.Float32bits(float32(n)))
case Float64:
order.PutUint64(buf, math.Float64bits(n))
}
case String:
switch f.kind {
case reflect.String:
size = val.Len()
copy(buf, []byte(val.String()))
default:
// TODO: handle kind != bytes here
size = val.Len()
copy(buf, val.Bytes())
}
case CustomType:
return val.Addr().Interface().(Custom).Pack(buf, options)
default:
panic(fmt.Sprintf("no pack handler for type: %s", typ))
}
return
}
func (f *Field) Pack(buf []byte, val reflect.Value, length int, options *Options) (int, error) {
typ := f.Type.Resolve(options)
if typ == Pad {
for i := 0; i < length; i++ {
buf[i] = 0
}
return length, nil
}
if f.Slice {
// special case strings and byte slices for performance
end := val.Len()
if !f.Array && typ == Uint8 && (f.defType == Uint8 || f.kind == reflect.String) {
var tmp []byte
if f.kind == reflect.String {
tmp = []byte(val.String())
} else {
tmp = val.Bytes()
}
copy(buf, tmp)
if end < length {
// TODO: allow configuring pad byte?
rep := bytes.Repeat([]byte{0}, length-end)
copy(buf[end:], rep)
return length, nil
}
return val.Len(), nil
}
pos := 0
var zero reflect.Value
if end < length {
zero = reflect.Zero(val.Type().Elem())
}
for i := 0; i < length; i++ {
cur := zero
if i < end {
cur = val.Index(i)
}
if n, err := f.packVal(buf[pos:], cur, 1, options); err != nil {
return pos, err
} else {
pos += n
}
}
return pos, nil
} else {
return f.packVal(buf, val, length, options)
}
}
func (f *Field) unpackVal(buf []byte, val reflect.Value, length int, options *Options) error {
order := f.Order
if options.Order != nil {
order = options.Order
}
if f.Ptr {
val = val.Elem()
}
typ := f.Type.Resolve(options)
switch typ {
case Float32, Float64:
var n float64
switch typ {
case Float32:
n = float64(math.Float32frombits(order.Uint32(buf)))
case Float64:
n = math.Float64frombits(order.Uint64(buf))
}
switch f.kind {
case reflect.Float32, reflect.Float64:
val.SetFloat(n)
default:
return fmt.Errorf("struc: refusing to unpack float into field %s of type %s", f.Name, f.kind.String())
}
case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64:
var n uint64
switch typ {
case Int8:
n = uint64(int64(int8(buf[0])))
case Int16:
n = uint64(int64(int16(order.Uint16(buf))))
case Int32:
n = uint64(int64(int32(order.Uint32(buf))))
case Int64:
n = uint64(int64(order.Uint64(buf)))
case Bool, Uint8:
n = uint64(buf[0])
case Uint16:
n = uint64(order.Uint16(buf))
case Uint32:
n = uint64(order.Uint32(buf))
case Uint64:
n = uint64(order.Uint64(buf))
}
switch f.kind {
case reflect.Bool:
val.SetBool(n != 0)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val.SetInt(int64(n))
default:
val.SetUint(n)
}
default:
panic(fmt.Sprintf("no unpack handler for type: %s", typ))
}
return nil
}
func (f *Field) Unpack(buf []byte, val reflect.Value, length int, options *Options) error {
typ := f.Type.Resolve(options)
if typ == Pad || f.kind == reflect.String {
if typ == Pad {
return nil
} else {
val.SetString(string(buf))
return nil
}
} else if f.Slice {
if val.Cap() < length {
val.Set(reflect.MakeSlice(val.Type(), length, length))
} else if val.Len() < length {
val.Set(val.Slice(0, length))
}
// special case byte slices for performance
if !f.Array && typ == Uint8 && f.defType == Uint8 {
copy(val.Bytes(), buf[:length])
return nil
}
pos := 0
size := typ.Size()
for i := 0; i < length; i++ {
if err := f.unpackVal(buf[pos:pos+size], val.Index(i), 1, options); err != nil {
return err
}
pos += size
}
return nil
} else {
return f.unpackVal(buf, val, length, options)
}
}
Go
1
https://gitee.com/quant1x/gox.git
git@gitee.com:quant1x/gox.git
quant1x
gox
gox
v1.21.1

搜索帮助

53164aa7 5694891 3bd8fe86 5694891