github.com/jimmyx0x/go-ethereum@v1.10.28/rlp/internal/rlpstruct/rlpstruct.go (about)

     1  // Copyright 2022 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package rlpstruct implements struct processing for RLP encoding/decoding.
    18  //
    19  // In particular, this package handles all rules around field filtering,
    20  // struct tags and nil value determination.
    21  package rlpstruct
    22  
    23  import (
    24  	"fmt"
    25  	"reflect"
    26  	"strings"
    27  )
    28  
    29  // Field represents a struct field.
    30  type Field struct {
    31  	Name     string
    32  	Index    int
    33  	Exported bool
    34  	Type     Type
    35  	Tag      string
    36  }
    37  
    38  // Type represents the attributes of a Go type.
    39  type Type struct {
    40  	Name      string
    41  	Kind      reflect.Kind
    42  	IsEncoder bool  // whether type implements rlp.Encoder
    43  	IsDecoder bool  // whether type implements rlp.Decoder
    44  	Elem      *Type // non-nil for Kind values of Ptr, Slice, Array
    45  }
    46  
    47  // DefaultNilValue determines whether a nil pointer to t encodes/decodes
    48  // as an empty string or empty list.
    49  func (t Type) DefaultNilValue() NilKind {
    50  	k := t.Kind
    51  	if isUint(k) || k == reflect.String || k == reflect.Bool || isByteArray(t) {
    52  		return NilKindString
    53  	}
    54  	return NilKindList
    55  }
    56  
    57  // NilKind is the RLP value encoded in place of nil pointers.
    58  type NilKind uint8
    59  
    60  const (
    61  	NilKindString NilKind = 0x80
    62  	NilKindList   NilKind = 0xC0
    63  )
    64  
    65  // Tags represents struct tags.
    66  type Tags struct {
    67  	// rlp:"nil" controls whether empty input results in a nil pointer.
    68  	// nilKind is the kind of empty value allowed for the field.
    69  	NilKind NilKind
    70  	NilOK   bool
    71  
    72  	// rlp:"optional" allows for a field to be missing in the input list.
    73  	// If this is set, all subsequent fields must also be optional.
    74  	Optional bool
    75  
    76  	// rlp:"tail" controls whether this field swallows additional list elements. It can
    77  	// only be set for the last field, which must be of slice type.
    78  	Tail bool
    79  
    80  	// rlp:"-" ignores fields.
    81  	Ignored bool
    82  }
    83  
    84  // TagError is raised for invalid struct tags.
    85  type TagError struct {
    86  	StructType string
    87  
    88  	// These are set by this package.
    89  	Field string
    90  	Tag   string
    91  	Err   string
    92  }
    93  
    94  func (e TagError) Error() string {
    95  	field := "field " + e.Field
    96  	if e.StructType != "" {
    97  		field = e.StructType + "." + e.Field
    98  	}
    99  	return fmt.Sprintf("rlp: invalid struct tag %q for %s (%s)", e.Tag, field, e.Err)
   100  }
   101  
   102  // ProcessFields filters the given struct fields, returning only fields
   103  // that should be considered for encoding/decoding.
   104  func ProcessFields(allFields []Field) ([]Field, []Tags, error) {
   105  	lastPublic := lastPublicField(allFields)
   106  
   107  	// Gather all exported fields and their tags.
   108  	var fields []Field
   109  	var tags []Tags
   110  	for _, field := range allFields {
   111  		if !field.Exported {
   112  			continue
   113  		}
   114  		ts, err := parseTag(field, lastPublic)
   115  		if err != nil {
   116  			return nil, nil, err
   117  		}
   118  		if ts.Ignored {
   119  			continue
   120  		}
   121  		fields = append(fields, field)
   122  		tags = append(tags, ts)
   123  	}
   124  
   125  	// Verify optional field consistency. If any optional field exists,
   126  	// all fields after it must also be optional. Note: optional + tail
   127  	// is supported.
   128  	var anyOptional bool
   129  	var firstOptionalName string
   130  	for i, ts := range tags {
   131  		name := fields[i].Name
   132  		if ts.Optional || ts.Tail {
   133  			if !anyOptional {
   134  				firstOptionalName = name
   135  			}
   136  			anyOptional = true
   137  		} else {
   138  			if anyOptional {
   139  				msg := fmt.Sprintf("must be optional because preceding field %q is optional", firstOptionalName)
   140  				return nil, nil, TagError{Field: name, Err: msg}
   141  			}
   142  		}
   143  	}
   144  	return fields, tags, nil
   145  }
   146  
   147  func parseTag(field Field, lastPublic int) (Tags, error) {
   148  	name := field.Name
   149  	tag := reflect.StructTag(field.Tag)
   150  	var ts Tags
   151  	for _, t := range strings.Split(tag.Get("rlp"), ",") {
   152  		switch t = strings.TrimSpace(t); t {
   153  		case "":
   154  			// empty tag is allowed for some reason
   155  		case "-":
   156  			ts.Ignored = true
   157  		case "nil", "nilString", "nilList":
   158  			ts.NilOK = true
   159  			if field.Type.Kind != reflect.Ptr {
   160  				return ts, TagError{Field: name, Tag: t, Err: "field is not a pointer"}
   161  			}
   162  			switch t {
   163  			case "nil":
   164  				ts.NilKind = field.Type.Elem.DefaultNilValue()
   165  			case "nilString":
   166  				ts.NilKind = NilKindString
   167  			case "nilList":
   168  				ts.NilKind = NilKindList
   169  			}
   170  		case "optional":
   171  			ts.Optional = true
   172  			if ts.Tail {
   173  				return ts, TagError{Field: name, Tag: t, Err: `also has "tail" tag`}
   174  			}
   175  		case "tail":
   176  			ts.Tail = true
   177  			if field.Index != lastPublic {
   178  				return ts, TagError{Field: name, Tag: t, Err: "must be on last field"}
   179  			}
   180  			if ts.Optional {
   181  				return ts, TagError{Field: name, Tag: t, Err: `also has "optional" tag`}
   182  			}
   183  			if field.Type.Kind != reflect.Slice {
   184  				return ts, TagError{Field: name, Tag: t, Err: "field type is not slice"}
   185  			}
   186  		default:
   187  			return ts, TagError{Field: name, Tag: t, Err: "unknown tag"}
   188  		}
   189  	}
   190  	return ts, nil
   191  }
   192  
   193  func lastPublicField(fields []Field) int {
   194  	last := 0
   195  	for _, f := range fields {
   196  		if f.Exported {
   197  			last = f.Index
   198  		}
   199  	}
   200  	return last
   201  }
   202  
   203  func isUint(k reflect.Kind) bool {
   204  	return k >= reflect.Uint && k <= reflect.Uintptr
   205  }
   206  
   207  func isByte(typ Type) bool {
   208  	return typ.Kind == reflect.Uint8 && !typ.IsEncoder
   209  }
   210  
   211  func isByteArray(typ Type) bool {
   212  	return (typ.Kind == reflect.Slice || typ.Kind == reflect.Array) && isByte(*typ.Elem)
   213  }