github.com/mymmsc/gox@v1.3.33/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  }