• Felix Lange's avatar
    rlp: add functions for encoding · 552f5b26
    Felix Lange authored
    I'm reasonably confident that the encoding matches the output of
    ethutil.Encode for values that it supports. Some of the tests have been
    adpated from the Ethereum testing repository.
    
    There are still TODOs in the code.
    552f5b26
typecache.go 1.78 KB
package rlp

import (
	"reflect"
	"sync"
)

var (
	typeCacheMutex sync.RWMutex
	typeCache      = make(map[reflect.Type]*typeinfo)
)

type typeinfo struct {
	decoder
	writer
}

type decoder func(*Stream, reflect.Value) error

type writer func(reflect.Value, *encbuf) error

func cachedTypeInfo(typ reflect.Type) (*typeinfo, error) {
	typeCacheMutex.RLock()
	info := typeCache[typ]
	typeCacheMutex.RUnlock()
	if info != nil {
		return info, nil
	}
	// not in the cache, need to generate info for this type.
	typeCacheMutex.Lock()
	defer typeCacheMutex.Unlock()
	return cachedTypeInfo1(typ)
}

func cachedTypeInfo1(typ reflect.Type) (*typeinfo, error) {
	info := typeCache[typ]
	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.
	typeCache[typ] = new(typeinfo)
	info, err := genTypeInfo(typ)
	if err != nil {
		// remove the dummy value if the generator fails
		delete(typeCache, typ)
		return nil, err
	}
	*typeCache[typ] = *info
	return typeCache[typ], err
}

func structFields(typ reflect.Type) (fields []field, err error) {
	for i := 0; i < typ.NumField(); i++ {
		if f := typ.Field(i); f.PkgPath == "" { // exported
			info, err := cachedTypeInfo1(f.Type)
			if err != nil {
				return nil, err
			}
			fields = append(fields, field{i, info})
		}
	}
	return fields, nil
}

func genTypeInfo(typ reflect.Type) (info *typeinfo, err error) {
	info = new(typeinfo)
	if info.decoder, err = makeDecoder(typ); err != nil {
		return nil, err
	}
	if info.writer, err = makeWriter(typ); err != nil {
		return nil, err
	}
	return info, nil
}

func isUint(k reflect.Kind) bool {
	return k >= reflect.Uint && k <= reflect.Uintptr
}