Files
bl/common/utils/sturc/field.go
xinian d159944d37
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
1
2026-02-21 22:41:59 +08:00

369 lines
8.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}
}