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 }