gitee.com/quant1x/gox@v1.21.2/encoding/binary/struc/parse.go (about)

     1  package struc
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  )
    13  
    14  // struc:"int32,big,sizeof=Data,skip,sizefrom=Len"
    15  
    16  type strucTag struct {
    17  	Type     string
    18  	Order    binary.ByteOrder
    19  	Sizeof   string
    20  	Skip     bool
    21  	Sizefrom string
    22  }
    23  
    24  func parseStrucTag(tag reflect.StructTag) *strucTag {
    25  	t := &strucTag{
    26  		Order: binary.BigEndian,
    27  	}
    28  	tagStr := tag.Get("struc")
    29  	if tagStr == "" {
    30  		// someone's going to typo this (I already did once)
    31  		// sorry if you made a module actually using this tag
    32  		// and you're mad at me now
    33  		tagStr = tag.Get("struct")
    34  	}
    35  	for _, s := range strings.Split(tagStr, ",") {
    36  		if strings.HasPrefix(s, "sizeof=") {
    37  			tmp := strings.SplitN(s, "=", 2)
    38  			t.Sizeof = tmp[1]
    39  		} else if strings.HasPrefix(s, "sizefrom=") {
    40  			tmp := strings.SplitN(s, "=", 2)
    41  			t.Sizefrom = tmp[1]
    42  		} else if s == "big" {
    43  			t.Order = binary.BigEndian
    44  		} else if s == "little" {
    45  			t.Order = binary.LittleEndian
    46  		} else if s == "skip" {
    47  			t.Skip = true
    48  		} else {
    49  			t.Type = s
    50  		}
    51  	}
    52  	return t
    53  }
    54  
    55  var typeLenRe = regexp.MustCompile(`^\[(\d*)\]`)
    56  
    57  func parseField(f reflect.StructField) (fd *Field, tag *strucTag, err error) {
    58  	tag = parseStrucTag(f.Tag)
    59  	var ok bool
    60  	fd = &Field{
    61  		Name:  f.Name,
    62  		Len:   1,
    63  		Order: tag.Order,
    64  		Slice: false,
    65  		kind:  f.Type.Kind(),
    66  	}
    67  	switch fd.kind {
    68  	case reflect.Array:
    69  		fd.Slice = true
    70  		fd.Array = true
    71  		fd.Len = f.Type.Len()
    72  		fd.kind = f.Type.Elem().Kind()
    73  	case reflect.Slice:
    74  		fd.Slice = true
    75  		fd.Len = -1
    76  		fd.kind = f.Type.Elem().Kind()
    77  	case reflect.Ptr:
    78  		fd.Ptr = true
    79  		fd.kind = f.Type.Elem().Kind()
    80  	}
    81  	// check for custom types
    82  	tmp := reflect.New(f.Type)
    83  	if _, ok := tmp.Interface().(Custom); ok {
    84  		fd.Type = CustomType
    85  		return
    86  	}
    87  	var defTypeOk bool
    88  	fd.defType, defTypeOk = reflectTypeMap[fd.kind]
    89  	// find a type in the struct tag
    90  	pureType := typeLenRe.ReplaceAllLiteralString(tag.Type, "")
    91  	if fd.Type, ok = typeLookup[pureType]; ok {
    92  		fd.Len = 1
    93  		match := typeLenRe.FindAllStringSubmatch(tag.Type, -1)
    94  		if len(match) > 0 && len(match[0]) > 1 {
    95  			fd.Slice = true
    96  			first := match[0][1]
    97  			// Field.Len = -1 indicates a []slice
    98  			if first == "" {
    99  				fd.Len = -1
   100  			} else {
   101  				fd.Len, err = strconv.Atoi(first)
   102  			}
   103  		}
   104  		return
   105  	}
   106  	// the user didn't specify a type
   107  	switch f.Type {
   108  	case reflect.TypeOf(Size_t(0)):
   109  		fd.Type = SizeType
   110  	case reflect.TypeOf(Off_t(0)):
   111  		fd.Type = OffType
   112  	default:
   113  		if defTypeOk {
   114  			fd.Type = fd.defType
   115  		} else {
   116  			err = errors.New(fmt.Sprintf("struc: Could not resolve field '%v' type '%v'.", f.Name, f.Type))
   117  		}
   118  	}
   119  	return
   120  }
   121  
   122  func parseFieldsLocked(v reflect.Value) (Fields, error) {
   123  	// we need to repeat this logic because parseFields() below can't be recursively called due to locking
   124  	for v.Kind() == reflect.Ptr {
   125  		v = v.Elem()
   126  	}
   127  	t := v.Type()
   128  	if v.NumField() < 1 {
   129  		return nil, errors.New("struc: Struct has no fields.")
   130  	}
   131  	sizeofMap := make(map[string][]int)
   132  	fields := make(Fields, v.NumField())
   133  	for i := 0; i < t.NumField(); i++ {
   134  		field := t.Field(i)
   135  		f, tag, err := parseField(field)
   136  		if tag.Skip {
   137  			continue
   138  		}
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  		if !v.Field(i).CanSet() {
   143  			continue
   144  		}
   145  		f.Index = i
   146  		if tag.Sizeof != "" {
   147  			target, ok := t.FieldByName(tag.Sizeof)
   148  			if !ok {
   149  				return nil, fmt.Errorf("struc: `sizeof=%s` field does not exist", tag.Sizeof)
   150  			}
   151  			f.Sizeof = target.Index
   152  			sizeofMap[tag.Sizeof] = field.Index
   153  		}
   154  		if sizefrom, ok := sizeofMap[field.Name]; ok {
   155  			f.Sizefrom = sizefrom
   156  		}
   157  		if tag.Sizefrom != "" {
   158  			source, ok := t.FieldByName(tag.Sizefrom)
   159  			if !ok {
   160  				return nil, fmt.Errorf("struc: `sizefrom=%s` field does not exist", tag.Sizefrom)
   161  			}
   162  			f.Sizefrom = source.Index
   163  		}
   164  		if f.Len == -1 && f.Sizefrom == nil {
   165  			return nil, fmt.Errorf("struc: field `%s` is a slice with no length or sizeof field", field.Name)
   166  		}
   167  		// recurse into nested structs
   168  		// TODO: handle loops (probably by indirecting the []Field and putting pointer in cache)
   169  		if f.Type == Struct {
   170  			typ := field.Type
   171  			if f.Ptr {
   172  				typ = typ.Elem()
   173  			}
   174  			if f.Slice {
   175  				typ = typ.Elem()
   176  			}
   177  			f.Fields, err = parseFieldsLocked(reflect.New(typ))
   178  			if err != nil {
   179  				return nil, err
   180  			}
   181  		}
   182  		fields[i] = f
   183  	}
   184  	return fields, nil
   185  }
   186  
   187  var fieldCache = make(map[reflect.Type]Fields)
   188  var fieldCacheLock sync.RWMutex
   189  var parseLock sync.Mutex
   190  
   191  func fieldCacheLookup(t reflect.Type) Fields {
   192  	fieldCacheLock.RLock()
   193  	defer fieldCacheLock.RUnlock()
   194  	if cached, ok := fieldCache[t]; ok {
   195  		return cached
   196  	}
   197  	return nil
   198  }
   199  
   200  func parseFields(v reflect.Value) (Fields, error) {
   201  	for v.Kind() == reflect.Ptr {
   202  		v = v.Elem()
   203  	}
   204  	t := v.Type()
   205  
   206  	// fast path: hopefully the field parsing is already cached
   207  	if cached := fieldCacheLookup(t); cached != nil {
   208  		return cached, nil
   209  	}
   210  
   211  	// hold a global lock so multiple goroutines can't parse (the same) fields at once
   212  	parseLock.Lock()
   213  	defer parseLock.Unlock()
   214  
   215  	// check cache a second time, in case parseLock was just released by
   216  	// another thread who filled the cache for us
   217  	if cached := fieldCacheLookup(t); cached != nil {
   218  		return cached, nil
   219  	}
   220  
   221  	// no luck, time to parse and fill the cache ourselves
   222  	fields, err := parseFieldsLocked(v)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  	fieldCacheLock.Lock()
   227  	fieldCache[t] = fields
   228  	fieldCacheLock.Unlock()
   229  	return fields, nil
   230  }