gitee.com/quant1x/gox@v1.7.6/encoding/binary/struc/fields.go (about) 1 package struc 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "io" 7 "reflect" 8 "strings" 9 ) 10 11 type Fields []*Field 12 13 func (f Fields) SetByteOrder(order binary.ByteOrder) { 14 for _, field := range f { 15 if field != nil { 16 field.Order = order 17 } 18 } 19 } 20 21 func (f Fields) String() string { 22 fields := make([]string, len(f)) 23 for i, field := range f { 24 if field != nil { 25 fields[i] = field.String() 26 } 27 } 28 return "{" + strings.Join(fields, ", ") + "}" 29 } 30 31 func (f Fields) Sizeof(val reflect.Value, options *Options) int { 32 for val.Kind() == reflect.Ptr { 33 val = val.Elem() 34 } 35 size := 0 36 for i, field := range f { 37 if field != nil { 38 size += field.Size(val.Field(i), options) 39 } 40 } 41 return size 42 } 43 44 func (f Fields) sizefrom(val reflect.Value, index []int) int { 45 field := val.FieldByIndex(index) 46 switch field.Kind() { 47 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 48 return int(field.Int()) 49 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 50 n := int(field.Uint()) 51 // all the builtin array length types are native int 52 // so this guards against weird truncation 53 if n < 0 { 54 return 0 55 } 56 return n 57 default: 58 name := val.Type().FieldByIndex(index).Name 59 panic(fmt.Sprintf("sizeof field %T.%s not an integer type", val.Interface(), name)) 60 } 61 } 62 63 func (f Fields) Pack(buf []byte, val reflect.Value, options *Options) (int, error) { 64 for val.Kind() == reflect.Ptr { 65 val = val.Elem() 66 } 67 pos := 0 68 for i, field := range f { 69 if field == nil { 70 continue 71 } 72 v := val.Field(i) 73 length := field.Len 74 if field.Sizefrom != nil { 75 length = f.sizefrom(val, field.Sizefrom) 76 } 77 if length <= 0 && field.Slice { 78 length = v.Len() 79 } 80 if field.Sizeof != nil { 81 length := val.FieldByIndex(field.Sizeof).Len() 82 switch field.kind { 83 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 84 // allocating a new int here has fewer side effects (doesn't update the original struct) 85 // but it's a wasteful allocation 86 // the old method might work if we just cast the temporary int/uint to the target type 87 v = reflect.New(v.Type()).Elem() 88 v.SetInt(int64(length)) 89 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 90 v = reflect.New(v.Type()).Elem() 91 v.SetUint(uint64(length)) 92 default: 93 panic(fmt.Sprintf("sizeof field is not int or uint type: %s, %s", field.Name, v.Type())) 94 } 95 } 96 if n, err := field.Pack(buf[pos:], v, length, options); err != nil { 97 return n, err 98 } else { 99 pos += n 100 } 101 } 102 return pos, nil 103 } 104 105 func (f Fields) Unpack(r io.Reader, val reflect.Value, options *Options) error { 106 for val.Kind() == reflect.Ptr { 107 val = val.Elem() 108 } 109 var tmp [8]byte 110 var buf []byte 111 for i, field := range f { 112 if field == nil { 113 continue 114 } 115 v := val.Field(i) 116 length := field.Len 117 if field.Sizefrom != nil { 118 length = f.sizefrom(val, field.Sizefrom) 119 } 120 if v.Kind() == reflect.Ptr && !v.Elem().IsValid() { 121 v.Set(reflect.New(v.Type().Elem())) 122 } 123 if field.Type == Struct { 124 if field.Slice { 125 vals := v 126 if !field.Array { 127 vals = reflect.MakeSlice(v.Type(), length, length) 128 } 129 for i := 0; i < length; i++ { 130 v := vals.Index(i) 131 fields, err := parseFields(v) 132 if err != nil { 133 return err 134 } 135 if err := fields.Unpack(r, v, options); err != nil { 136 return err 137 } 138 } 139 if !field.Array { 140 v.Set(vals) 141 } 142 } else { 143 // TODO: DRY (we repeat the inner loop above) 144 fields, err := parseFields(v) 145 if err != nil { 146 return err 147 } 148 if err := fields.Unpack(r, v, options); err != nil { 149 return err 150 } 151 } 152 continue 153 } else { 154 typ := field.Type.Resolve(options) 155 if typ == CustomType { 156 if err := v.Addr().Interface().(Custom).Unpack(r, length, options); err != nil { 157 return err 158 } 159 } else { 160 size := length * field.Type.Resolve(options).Size() 161 if size < 8 { 162 buf = tmp[:size] 163 } else { 164 buf = make([]byte, size) 165 } 166 if _, err := io.ReadFull(r, buf); err != nil { 167 return err 168 } 169 err := field.Unpack(buf[:size], v, length, options) 170 if err != nil { 171 return err 172 } 173 } 174 } 175 } 176 return nil 177 }