369 lines
8.7 KiB
Go
369 lines
8.7 KiB
Go
package struc
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/binary"
|
||
"fmt"
|
||
"math"
|
||
"reflect"
|
||
"unsafe"
|
||
)
|
||
|
||
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 typ == CustomType {
|
||
return val.Addr().Interface().(Custom).Size(options)
|
||
} else if f.Slice || f.kind == reflect.String {
|
||
length := val.Len()
|
||
if f.Len > 1 {
|
||
length = f.Len
|
||
}
|
||
size = length * typ.Size()
|
||
} else {
|
||
size = typ.Size()
|
||
}
|
||
align := options.ByteAlign
|
||
if align > 0 && size < align {
|
||
size = align
|
||
}
|
||
return size
|
||
}
|
||
|
||
// 预定义常用的order,避免重复判断
|
||
var defaultOrder = binary.BigEndian
|
||
|
||
// packVal 优化版:减少反射开销+优化内存拷贝+优雅错误处理
|
||
func (f *Field) packVal(buf []byte, val reflect.Value, _ int, options *Options) (size int, err error) {
|
||
// 1. 预缓存order,避免重复判断
|
||
order := f.Order
|
||
if options != nil && options.Order != nil {
|
||
order = options.Order
|
||
}
|
||
if order == nil {
|
||
order = defaultOrder
|
||
}
|
||
|
||
// 2. 处理指针类型:提前解引用,避免后续重复操作
|
||
if f.Ptr {
|
||
if !val.IsNil() {
|
||
val = val.Elem()
|
||
} else {
|
||
return 0, fmt.Errorf("field %s is nil pointer", f.Name)
|
||
}
|
||
}
|
||
|
||
// 3. 预解析类型,避免重复Resolve
|
||
typ := f.Type.Resolve(options)
|
||
kind := val.Kind()
|
||
|
||
// 4. 扁平化分支逻辑,减少嵌套
|
||
switch typ {
|
||
case Struct:
|
||
return f.Fields.Pack(buf, val, options)
|
||
|
||
case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64:
|
||
size = typ.Size()
|
||
if len(buf) < size {
|
||
return 0, fmt.Errorf("buf size %d < required %d", len(buf), size)
|
||
}
|
||
|
||
var n uint64
|
||
switch kind {
|
||
case reflect.Bool:
|
||
n = boolToUint64(val.Bool())
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
n = uint64(val.Int())
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
n = val.Uint()
|
||
default:
|
||
return 0, fmt.Errorf("unsupported kind %s for numeric type %s", kind, typ)
|
||
}
|
||
|
||
// 扁平化数值写入逻辑
|
||
switch typ {
|
||
case Bool:
|
||
buf[0] = byte(n)
|
||
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, n)
|
||
}
|
||
|
||
case Float32, Float64:
|
||
size = typ.Size()
|
||
if len(buf) < size {
|
||
return 0, fmt.Errorf("buf size %d < required %d", len(buf), size)
|
||
}
|
||
|
||
if kind != reflect.Float32 && kind != reflect.Float64 {
|
||
return 0, fmt.Errorf("unsupported kind %s for float type %s", kind, typ)
|
||
}
|
||
n := val.Float()
|
||
|
||
switch typ {
|
||
case Float32:
|
||
order.PutUint32(buf, math.Float32bits(float32(n)))
|
||
case Float64:
|
||
order.PutUint64(buf, math.Float64bits(n))
|
||
}
|
||
|
||
case String:
|
||
// 优化String类型:减少内存拷贝
|
||
switch kind {
|
||
case reflect.String:
|
||
s := val.String()
|
||
size = len(s)
|
||
if len(buf) < size {
|
||
return 0, fmt.Errorf("buf size %d < string length %d", len(buf), size)
|
||
}
|
||
// 用unsafe直接拷贝字符串到buf,避免[]byte(s)的内存分配
|
||
copyStringToBuf(buf, s)
|
||
|
||
case reflect.Slice:
|
||
if val.Type().Elem().Kind() != reflect.Uint8 {
|
||
return 0, fmt.Errorf("unsupported slice type %s for String field", val.Type())
|
||
}
|
||
size = val.Len()
|
||
if len(buf) < size {
|
||
return 0, fmt.Errorf("buf size %d < bytes length %d", len(buf), size)
|
||
}
|
||
// 直接拷贝字节切片,避免冗余操作
|
||
copy(buf, val.Bytes())
|
||
|
||
default:
|
||
return 0, fmt.Errorf("unsupported kind %s for String type", kind)
|
||
}
|
||
|
||
case CustomType:
|
||
// 优化反射断言:提前检查类型,避免panic
|
||
if !val.CanAddr() {
|
||
return 0, fmt.Errorf("custom type %s cannot take address", val.Type())
|
||
}
|
||
custom, ok := val.Addr().Interface().(Custom)
|
||
if !ok {
|
||
return 0, fmt.Errorf("type %s does not implement Custom interface", val.Type())
|
||
}
|
||
return custom.Pack(buf, options)
|
||
|
||
default:
|
||
// 替换panic为error,避免程序崩溃
|
||
return 0, fmt.Errorf("no pack handler for type: %s", typ)
|
||
}
|
||
|
||
return size, nil
|
||
}
|
||
|
||
// 辅助函数:bool转uint64,减少inline重复代码
|
||
func boolToUint64(b bool) uint64 {
|
||
if b {
|
||
return 1
|
||
}
|
||
return 0
|
||
}
|
||
|
||
// 辅助函数:unsafe拷贝字符串到buf,避免[]byte(s)的内存分配
|
||
// 注意:仅在确定buf长度足够时使用
|
||
func copyStringToBuf(buf []byte, s string) {
|
||
// unsafe转换:字符串转字节切片,无内存分配
|
||
src := *(*[]byte)(unsafe.Pointer(&struct {
|
||
string
|
||
cap int
|
||
}{s, len(s)}))
|
||
copy(buf, src)
|
||
}
|
||
|
||
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
|
||
} else {
|
||
copy(buf, buf[:length])
|
||
return length, 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, _ 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)
|
||
}
|
||
}
|