@ -29,8 +29,10 @@ var (
)
)
type typeinfo struct {
type typeinfo struct {
decoder
decoder decoder
writer
decoderErr error // error from makeDecoder
writer writer
writerErr error // error from makeWriter
}
}
// represents struct tags
// represents struct tags
@ -56,12 +58,22 @@ type decoder func(*Stream, reflect.Value) error
type writer func ( reflect . Value , * encbuf ) error
type writer func ( reflect . Value , * encbuf ) error
func cachedTypeInfo ( typ reflect . Type , tags tags ) ( * typeinfo , error ) {
func cachedDecoder ( typ reflect . Type ) ( decoder , error ) {
info := cachedTypeInfo ( typ , tags { } )
return info . decoder , info . decoderErr
}
func cachedWriter ( typ reflect . Type ) ( writer , error ) {
info := cachedTypeInfo ( typ , tags { } )
return info . writer , info . writerErr
}
func cachedTypeInfo ( typ reflect . Type , tags tags ) * typeinfo {
typeCacheMutex . RLock ( )
typeCacheMutex . RLock ( )
info := typeCache [ typekey { typ , tags } ]
info := typeCache [ typekey { typ , tags } ]
typeCacheMutex . RUnlock ( )
typeCacheMutex . RUnlock ( )
if info != nil {
if info != nil {
return info , nil
return info
}
}
// not in the cache, need to generate info for this type.
// not in the cache, need to generate info for this type.
typeCacheMutex . Lock ( )
typeCacheMutex . Lock ( )
@ -69,25 +81,20 @@ func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) {
return cachedTypeInfo1 ( typ , tags )
return cachedTypeInfo1 ( typ , tags )
}
}
func cachedTypeInfo1 ( typ reflect . Type , tags tags ) ( * typeinfo , error ) {
func cachedTypeInfo1 ( typ reflect . Type , tags tags ) * typeinfo {
key := typekey { typ , tags }
key := typekey { typ , tags }
info := typeCache [ key ]
info := typeCache [ key ]
if info != nil {
if info != nil {
// another goroutine got the write lock first
// another goroutine got the write lock first
return info , nil
return info
}
}
// put a dummy value into the cache before generating.
// put a dummy value into the cache before generating.
// if the generator tries to lookup itself, it will get
// if the generator tries to lookup itself, it will get
// the dummy value and won't call itself recursively.
// the dummy value and won't call itself recursively.
typeCache [ key ] = new ( typeinfo )
info = new ( typeinfo )
info , err := genTypeInfo ( typ , tags )
typeCache [ key ] = info
if err != nil {
info . generate ( typ , tags )
// remove the dummy value if the generator fails
return info
delete ( typeCache , key )
return nil , err
}
* typeCache [ key ] = * info
return typeCache [ key ] , err
}
}
type field struct {
type field struct {
@ -96,26 +103,24 @@ type field struct {
}
}
func structFields ( typ reflect . Type ) ( fields [ ] field , err error ) {
func structFields ( typ reflect . Type ) ( fields [ ] field , err error ) {
lastPublic := lastPublicField ( typ )
for i := 0 ; i < typ . NumField ( ) ; i ++ {
for i := 0 ; i < typ . NumField ( ) ; i ++ {
if f := typ . Field ( i ) ; f . PkgPath == "" { // exported
if f := typ . Field ( i ) ; f . PkgPath == "" { // exported
tags , err := parseStructTag ( typ , i )
tags , err := parseStructTag ( typ , i , lastPublic )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
if tags . ignored {
if tags . ignored {
continue
continue
}
}
info , err := cachedTypeInfo1 ( f . Type , tags )
info := cachedTypeInfo1 ( f . Type , tags )
if err != nil {
return nil , err
}
fields = append ( fields , field { i , info } )
fields = append ( fields , field { i , info } )
}
}
}
}
return fields , nil
return fields , nil
}
}
func parseStructTag ( typ reflect . Type , fi int ) ( tags , error ) {
func parseStructTag ( typ reflect . Type , fi , lastPublic int ) ( tags , error ) {
f := typ . Field ( fi )
f := typ . Field ( fi )
var ts tags
var ts tags
for _ , t := range strings . Split ( f . Tag . Get ( "rlp" ) , "," ) {
for _ , t := range strings . Split ( f . Tag . Get ( "rlp" ) , "," ) {
@ -127,7 +132,7 @@ func parseStructTag(typ reflect.Type, fi int) (tags, error) {
ts . nilOK = true
ts . nilOK = true
case "tail" :
case "tail" :
ts . tail = true
ts . tail = true
if fi != typ . NumField ( ) - 1 {
if fi != lastPublic {
return ts , fmt . Errorf ( ` rlp: invalid struct tag "tail" for %v.%s (must be on last field) ` , typ , f . Name )
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 {
if f . Type . Kind ( ) != reflect . Slice {
@ -140,15 +145,19 @@ func parseStructTag(typ reflect.Type, fi int) (tags, error) {
return ts , nil
return ts , nil
}
}
func genTypeInfo ( typ reflect . Type , tags tags ) ( info * typeinfo , err error ) {
func lastPublicField ( typ reflect . Type ) int {
info = new ( typeinfo )
last := 0
if info . decoder , err = makeDecoder ( typ , tags ) ; err != nil {
for i := 0 ; i < typ . NumField ( ) ; i ++ {
return nil , err
if typ . Field ( i ) . PkgPath == "" {
}
last = i
if info . writer , err = makeWriter ( typ , tags ) ; err != nil {
}
return nil , err
}
}
return info , nil
return last
}
func ( i * typeinfo ) generate ( typ reflect . Type , tags tags ) {
i . decoder , i . decoderErr = makeDecoder ( typ , tags )
i . writer , i . writerErr = makeWriter ( typ , tags )
}
}
func isUint ( k reflect . Kind ) bool {
func isUint ( k reflect . Kind ) bool {