| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2014 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | package rlp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-12-21 21:05:20 +01:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2015-12-21 21:05:20 +01:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 11:40:32 +01:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	typeCacheMutex sync.RWMutex | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 	typeCache      = make(map[typekey]*typeinfo) | 
					
						
							| 
									
										
										
										
											2014-12-30 11:40:32 +01:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | type typeinfo struct { | 
					
						
							|  |  |  | 	decoder | 
					
						
							| 
									
										
										
										
											2014-12-30 11:40:32 +01:00
										 |  |  | 	writer | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | // represents struct tags | 
					
						
							|  |  |  | type tags struct { | 
					
						
							| 
									
										
										
										
											2015-12-21 21:05:20 +01:00
										 |  |  | 	// rlp:"nil" controls whether empty input results in a nil pointer. | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 	nilOK bool | 
					
						
							| 
									
										
										
										
											2015-12-21 21:05:20 +01:00
										 |  |  | 	// rlp:"tail" controls whether this field swallows additional list | 
					
						
							|  |  |  | 	// elements. It can only be set for the last field, which must be | 
					
						
							|  |  |  | 	// of slice type. | 
					
						
							|  |  |  | 	tail bool | 
					
						
							| 
									
										
										
										
											2017-03-07 12:37:53 +01:00
										 |  |  | 	// rlp:"-" ignores fields. | 
					
						
							|  |  |  | 	ignored bool | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type typekey struct { | 
					
						
							|  |  |  | 	reflect.Type | 
					
						
							|  |  |  | 	// the key must include the struct tags because they | 
					
						
							|  |  |  | 	// might generate a different decoder. | 
					
						
							|  |  |  | 	tags | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 11:40:32 +01:00
										 |  |  | type decoder func(*Stream, reflect.Value) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type writer func(reflect.Value, *encbuf) error | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) { | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 	typeCacheMutex.RLock() | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 	info := typeCache[typekey{typ, tags}] | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 	typeCacheMutex.RUnlock() | 
					
						
							|  |  |  | 	if info != nil { | 
					
						
							|  |  |  | 		return info, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// not in the cache, need to generate info for this type. | 
					
						
							|  |  |  | 	typeCacheMutex.Lock() | 
					
						
							|  |  |  | 	defer typeCacheMutex.Unlock() | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 	return cachedTypeInfo1(typ, tags) | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) { | 
					
						
							|  |  |  | 	key := typekey{typ, tags} | 
					
						
							|  |  |  | 	info := typeCache[key] | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 	if info != nil { | 
					
						
							|  |  |  | 		// another goroutine got the write lock first | 
					
						
							|  |  |  | 		return info, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// put a dummmy value into the cache before generating. | 
					
						
							|  |  |  | 	// if the generator tries to lookup itself, it will get | 
					
						
							|  |  |  | 	// the dummy value and won't call itself recursively. | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 	typeCache[key] = new(typeinfo) | 
					
						
							|  |  |  | 	info, err := genTypeInfo(typ, tags) | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// remove the dummy value if the generator fails | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 		delete(typeCache, key) | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 	*typeCache[key] = *info | 
					
						
							|  |  |  | 	return typeCache[key], err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type field struct { | 
					
						
							|  |  |  | 	index int | 
					
						
							|  |  |  | 	info  *typeinfo | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 11:40:32 +01:00
										 |  |  | func structFields(typ reflect.Type) (fields []field, err error) { | 
					
						
							|  |  |  | 	for i := 0; i < typ.NumField(); i++ { | 
					
						
							|  |  |  | 		if f := typ.Field(i); f.PkgPath == "" { // exported | 
					
						
							| 
									
										
										
										
											2015-12-21 21:05:20 +01:00
										 |  |  | 			tags, err := parseStructTag(typ, i) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-03-07 12:37:53 +01:00
										 |  |  | 			if tags.ignored { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 			info, err := cachedTypeInfo1(f.Type, tags) | 
					
						
							| 
									
										
										
										
											2014-12-30 11:40:32 +01:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			fields = append(fields, field{i, info}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fields, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-21 21:05:20 +01:00
										 |  |  | func parseStructTag(typ reflect.Type, fi int) (tags, error) { | 
					
						
							|  |  |  | 	f := typ.Field(fi) | 
					
						
							|  |  |  | 	var ts tags | 
					
						
							|  |  |  | 	for _, t := range strings.Split(f.Tag.Get("rlp"), ",") { | 
					
						
							|  |  |  | 		switch t = strings.TrimSpace(t); t { | 
					
						
							|  |  |  | 		case "": | 
					
						
							| 
									
										
										
										
											2017-03-07 12:37:53 +01:00
										 |  |  | 		case "-": | 
					
						
							|  |  |  | 			ts.ignored = true | 
					
						
							| 
									
										
										
										
											2015-12-21 21:05:20 +01:00
										 |  |  | 		case "nil": | 
					
						
							|  |  |  | 			ts.nilOK = true | 
					
						
							|  |  |  | 		case "tail": | 
					
						
							|  |  |  | 			ts.tail = true | 
					
						
							|  |  |  | 			if fi != typ.NumField()-1 { | 
					
						
							|  |  |  | 				return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (must be on last field)`, typ, f.Name) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if f.Type.Kind() != reflect.Slice { | 
					
						
							|  |  |  | 				return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (field type is not slice)`, typ, f.Name) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return ts, fmt.Errorf("rlp: unknown struct tag %q on %v.%s", t, typ, f.Name) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ts, nil | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) { | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 	info = new(typeinfo) | 
					
						
							| 
									
										
										
										
											2015-04-17 01:16:46 +02:00
										 |  |  | 	if info.decoder, err = makeDecoder(typ, tags); err != nil { | 
					
						
							| 
									
										
										
										
											2014-12-09 10:39:39 +01:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-12-21 21:05:20 +01:00
										 |  |  | 	if info.writer, err = makeWriter(typ, tags); err != nil { | 
					
						
							| 
									
										
										
										
											2014-12-30 11:40:32 +01:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-12-09 10:39:39 +01:00
										 |  |  | 	return info, nil | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-09 10:41:47 +01:00
										 |  |  | func isUint(k reflect.Kind) bool { | 
					
						
							|  |  |  | 	return k >= reflect.Uint && k <= reflect.Uintptr | 
					
						
							| 
									
										
										
										
											2014-11-13 20:31:48 +01:00
										 |  |  | } |