Support nil pointers for Binary.

If the thing does not already have a typebyte declared,
a fake one will be given (0x01).
A TypeByte of 0x00 is reserved for nil things.
No nil-dogs.
This commit is contained in:
Jae Kwon
2015-04-12 17:46:16 -07:00
parent 1364770cbe
commit 6d6f061f19
20 changed files with 299 additions and 178 deletions

View File

@@ -21,6 +21,9 @@ func ReadBinary(o interface{}, r io.Reader, n *int64, err *error) interface{} {
func WriteBinary(o interface{}, w io.Writer, n *int64, err *error) {
rv := reflect.ValueOf(o)
rt := reflect.TypeOf(o)
if rv.Kind() == reflect.Ptr {
rv, rt = rv.Elem(), rt.Elem()
}
writeReflect(rv, rt, w, n, err)
}
@@ -49,6 +52,9 @@ func ReadJSONFromObject(o interface{}, object interface{}, err *error) interface
func WriteJSON(o interface{}, w io.Writer, n *int64, err *error) {
rv := reflect.ValueOf(o)
rt := reflect.TypeOf(o)
if rv.Kind() == reflect.Ptr {
rv, rt = rv.Elem(), rt.Elem()
}
writeReflectJSON(rv, rt, w, n, err)
}

View File

@@ -15,13 +15,10 @@ import (
type TypeInfo struct {
Type reflect.Type // The type
// Custom encoder/decoder
// NOTE: Not used.
BinaryEncoder Encoder
BinaryDecoder Decoder
// If Type is kind reflect.Interface
ConcreteTypes map[byte]reflect.Type
// If Type is kind reflect.Interface, is registered
IsRegisteredInterface bool
ConcreteTypes map[byte]reflect.Type
ConcreteTypeBytes map[reflect.Type]byte
// If Type is concrete
HasTypeByte bool
@@ -104,6 +101,7 @@ func RegisterInterface(o interface{}, args ...interface{}) *TypeInfo {
panic("RegisterInterface expects an interface")
}
concreteTypes := make(map[byte]reflect.Type, 0)
concreteTypesReversed := make(map[reflect.Type]byte, 0)
for _, arg := range args {
switch arg.(type) {
case ConcreteType:
@@ -114,17 +112,23 @@ func RegisterInterface(o interface{}, args ...interface{}) *TypeInfo {
if !hasTypeByte {
panic(Fmt("Expected concrete type %v to implement HasTypeByte", concreteType))
}
if typeByte == 0x00 {
panic(Fmt("TypeByte of 0x00 is reserved for nil (%v)", concreteType))
}
if concreteTypes[typeByte] != nil {
panic(Fmt("Duplicate TypeByte for type %v and %v", concreteType, concreteTypes[typeByte]))
}
concreteTypes[typeByte] = concreteType
concreteTypesReversed[concreteType] = typeByte
default:
panic(Fmt("Unexpected argument type %v", reflect.TypeOf(arg)))
}
}
typeInfo := &TypeInfo{
Type: it,
ConcreteTypes: concreteTypes,
Type: it,
IsRegisteredInterface: true,
ConcreteTypes: concreteTypes,
ConcreteTypeBytes: concreteTypesReversed,
}
typeInfos[it] = typeInfo
return typeInfo
@@ -148,7 +152,8 @@ func RegisterType(info *TypeInfo) *TypeInfo {
typeInfos[ptrRt] = info
// See if the type implements HasTypeByte
if rt.Kind() != reflect.Interface && rt.Implements(reflect.TypeOf((*HasTypeByte)(nil)).Elem()) {
if rt.Kind() != reflect.Interface &&
rt.Implements(reflect.TypeOf((*HasTypeByte)(nil)).Elem()) {
zero := reflect.Zero(rt)
typeByte := zero.Interface().(HasTypeByte).TypeByte()
if info.HasTypeByte && info.TypeByte != typeByte {
@@ -197,35 +202,60 @@ func RegisterType(info *TypeInfo) *TypeInfo {
func readReflect(rv reflect.Value, rt reflect.Type, r io.Reader, n *int64, err *error) {
log.Debug("Read reflect", "type", rt)
// Get typeInfo
typeInfo := GetTypeInfo(rt)
// Custom decoder
if typeInfo.BinaryDecoder != nil {
decoded := typeInfo.BinaryDecoder(r, n, err)
rv.Set(reflect.ValueOf(decoded))
if rt.Kind() == reflect.Interface {
if !typeInfo.IsRegisteredInterface {
// There's no way we can read such a thing.
*err = errors.New(Fmt("Cannot read unregistered interface type %v", rt))
return
}
typeByte := ReadByte(r, n, err)
if *err != nil {
return
}
if typeByte == 0x00 {
return // nil
}
crt, ok := typeInfo.ConcreteTypes[typeByte]
if !ok {
*err = errors.New(Fmt("Unexpected type byte %X for type %v", typeByte, crt))
return
}
crv := reflect.New(crt).Elem()
r = NewPrefixedReader([]byte{typeByte}, r)
readReflect(crv, crt, r, n, err)
rv.Set(crv) // NOTE: orig rv is ignored.
return
}
// Create a new struct if rv is nil pointer.
if rt.Kind() == reflect.Ptr && rv.IsNil() {
newRv := reflect.New(rt.Elem())
rv.Set(newRv)
rv = newRv
}
// Dereference pointer
// Still addressable, thus settable!
if rv.Kind() == reflect.Ptr {
if rt.Kind() == reflect.Ptr {
typeByte := ReadByte(r, n, err)
if *err != nil {
return
}
if typeByte == 0x00 {
return // nil
}
// Create new if rv is nil.
if rv.IsNil() {
newRv := reflect.New(rt.Elem())
rv.Set(newRv)
rv = newRv
}
// Dereference pointer
rv, rt = rv.Elem(), rt.Elem()
typeInfo = GetTypeInfo(rt)
if typeInfo.HasTypeByte {
r = NewPrefixedReader([]byte{typeByte}, r)
}
// continue...
}
// Read TypeByte prefix
if typeInfo.HasTypeByte {
typeByte := ReadByte(r, n, err)
log.Debug("Read typebyte", "typeByte", typeByte)
if typeByte != typeInfo.TypeByte {
*err = errors.New(Fmt("Expected TypeByte of %X but got %X", typeInfo.TypeByte, typeByte))
return
@@ -233,19 +263,6 @@ func readReflect(rv reflect.Value, rt reflect.Type, r io.Reader, n *int64, err *
}
switch rt.Kind() {
case reflect.Interface:
typeByte := ReadByte(r, n, err)
if *err != nil {
return
}
concreteType, ok := typeInfo.ConcreteTypes[typeByte]
if !ok {
panic(Fmt("TypeByte %X not registered for interface %v", typeByte, rt))
}
newRv := reflect.New(concreteType)
readReflect(newRv.Elem(), concreteType, NewPrefixedReader([]byte{typeByte}, r), n, err)
rv.Set(newRv.Elem())
case reflect.Slice:
elemRt := rt.Elem()
if elemRt.Kind() == reflect.Uint8 {
@@ -349,36 +366,66 @@ func readReflect(rv reflect.Value, rt reflect.Type, r io.Reader, n *int64, err *
}
}
// rv: the reflection value of the thing to write
// rt: the type of rv as declared in the container, not necessarily rv.Type().
func writeReflect(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64, err *error) {
// Get typeInfo
typeInfo := GetTypeInfo(rt)
// Custom encoder, say for an interface type rt.
if typeInfo.BinaryEncoder != nil {
typeInfo.BinaryEncoder(rv.Interface(), w, n, err)
if rt.Kind() == reflect.Interface {
if rv.IsNil() {
// XXX ensure that typeByte 0 is reserved.
WriteByte(0x00, w, n, err)
return
}
crv := rv.Elem() // concrete reflection value
crt := crv.Type() // concrete reflection type
if typeInfo.IsRegisteredInterface {
// See if the crt is registered.
// If so, we're more restrictive.
_, ok := typeInfo.ConcreteTypeBytes[crt]
if !ok {
switch crt.Kind() {
case reflect.Ptr:
*err = errors.New(Fmt("Unexpected pointer type %v. Was it registered as a value receiver rather than as a pointer receiver?", crt))
case reflect.Struct:
*err = errors.New(Fmt("Unexpected struct type %v. Was it registered as a pointer receiver rather than as a value receiver?", crt))
default:
*err = errors.New(Fmt("Unexpected type %v.", crt))
}
return
}
} else {
// We support writing unsafely for convenience.
}
// We don't have to write the typeByte here,
// the writeReflect() call below will write it.
writeReflect(crv, crt, w, n, err)
return
}
// Dereference interface
if rt.Kind() == reflect.Interface {
rv = rv.Elem()
rt = rv.Type()
// If interface type, get typeInfo of underlying type.
typeInfo = GetTypeInfo(rt)
}
// Dereference pointer
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
rv = rv.Elem()
// Dereference pointer
rv, rt = rv.Elem(), rt.Elem()
if !rv.IsValid() {
WriteByte(0x00, w, n, err)
return
}
if !typeInfo.HasTypeByte {
WriteByte(0x01, w, n, err)
// continue...
} else {
// continue...
}
}
// Write TypeByte prefix
// Write type byte
if typeInfo.HasTypeByte {
WriteByte(typeInfo.TypeByte, w, n, err)
}
// All other types
switch rt.Kind() {
case reflect.Slice:
elemRt := rt.Elem()
@@ -478,22 +525,47 @@ func readTypeByteJSON(o interface{}) (typeByte byte, rest interface{}, err error
func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *error) {
log.Debug("Read reflect json", "type", rt)
// Get typeInfo
typeInfo := GetTypeInfo(rt)
// Create a new struct if rv is nil pointer.
if rt.Kind() == reflect.Ptr && rv.IsNil() {
newRv := reflect.New(rt.Elem())
rv.Set(newRv)
rv = newRv
if rt.Kind() == reflect.Interface {
if !typeInfo.IsRegisteredInterface {
// There's no way we can read such a thing.
*err = errors.New(Fmt("Cannot read unregistered interface type %v", rt))
return
}
if o == nil {
return // nil
}
typeByte, _, err_ := readTypeByteJSON(o)
if err_ != nil {
*err = err_
return
}
crt, ok := typeInfo.ConcreteTypes[typeByte]
if !ok {
*err = errors.New(Fmt("TypeByte %X not registered for interface %v", typeByte, rt))
return
}
crv := reflect.New(crt).Elem()
readReflectJSON(crv, crt, o, err)
rv.Set(crv) // NOTE: orig rv is ignored.
return
}
// Dereference pointer
// Still addressable, thus settable!
if rv.Kind() == reflect.Ptr {
if rt.Kind() == reflect.Ptr {
if o == nil {
return // nil
}
// Create new struct if rv is nil.
if rv.IsNil() {
newRv := reflect.New(rt.Elem())
rv.Set(newRv)
rv = newRv
}
// Dereference pointer
rv, rt = rv.Elem(), rt.Elem()
// continue...
}
// Read TypeByte prefix
@@ -511,20 +583,6 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *erro
}
switch rt.Kind() {
case reflect.Interface:
typeByte, _, err_ := readTypeByteJSON(o)
if err_ != nil {
*err = err_
return
}
concreteType, ok := typeInfo.ConcreteTypes[typeByte]
if !ok {
panic(Fmt("TypeByte %X not registered for interface %v", typeByte, rt))
}
newRv := reflect.New(concreteType)
readReflectJSON(newRv.Elem(), concreteType, o, err)
rv.Set(newRv.Elem())
case reflect.Slice:
elemRt := rt.Elem()
if elemRt.Kind() == reflect.Uint8 {
@@ -643,25 +701,55 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64,
// Get typeInfo
typeInfo := GetTypeInfo(rt)
// Dereference interface
if rt.Kind() == reflect.Interface {
rv = rv.Elem()
rt = rv.Type()
// If interface type, get typeInfo of underlying type.
typeInfo = GetTypeInfo(rt)
if rv.IsNil() {
// XXX ensure that typeByte 0 is reserved.
WriteTo([]byte("null"), w, n, err)
return
}
crv := rv.Elem() // concrete reflection value
crt := crv.Type() // concrete reflection type
if typeInfo.IsRegisteredInterface {
// See if the crt is registered.
// If so, we're more restrictive.
_, ok := typeInfo.ConcreteTypeBytes[crt]
if !ok {
switch crt.Kind() {
case reflect.Ptr:
*err = errors.New(Fmt("Unexpected pointer type %v. Was it registered as a value receiver rather than as a pointer receiver?", crt))
case reflect.Struct:
*err = errors.New(Fmt("Unexpected struct type %v. Was it registered as a pointer receiver rather than as a value receiver?", crt))
default:
*err = errors.New(Fmt("Unexpected type %v.", crt))
}
return
}
} else {
// We support writing unsafely for convenience.
}
// We don't have to write the typeByte here,
// the writeReflectJSON() call below will write it.
writeReflectJSON(crv, crt, w, n, err)
return
}
// Dereference pointer
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
rv = rv.Elem()
// Dereference pointer
rv, rt = rv.Elem(), rt.Elem()
if !rv.IsValid() {
WriteTo([]byte("null"), w, n, err)
return
}
// continue...
}
// Write TypeByte prefix
// Write TypeByte
if typeInfo.HasTypeByte {
WriteTo([]byte(Fmt("[%v,", typeInfo.TypeByte)), w, n, err)
defer WriteTo([]byte("]"), w, n, err)
}
// All other types
switch rt.Kind() {
case reflect.Slice:
elemRt := rt.Elem()
@@ -730,10 +818,4 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64,
panic(Fmt("Unknown field type %v", rt.Kind()))
}
// Write TypeByte close bracket
if typeInfo.HasTypeByte {
WriteTo([]byte("]"), w, n, err)
}
}
//-----------------------------------------------------------------------------

View File

@@ -120,13 +120,42 @@ func instantiateBasic() (interface{}, interface{}) {
func validateBasic(o interface{}, t *testing.T) {
cat := o.(Cat)
if cat.String != "String" {
t.Errorf("Expected cat2.String == 'String', got %v", cat.String)
t.Errorf("Expected cat.String == 'String', got %v", cat.String)
}
if string(cat.Bytes) != "Bytes" {
t.Errorf("Expected cat2.Bytes == 'Bytes', got %X", cat.Bytes)
t.Errorf("Expected cat.Bytes == 'Bytes', got %X", cat.Bytes)
}
if cat.Time.Unix() != 123 {
t.Errorf("Expected cat2.Time == 'Unix(123)', got %v", cat.Time)
t.Errorf("Expected cat.Time == 'Unix(123)', got %v", cat.Time)
}
}
//-------------------------------------
type NilTestStruct struct {
IntPtr *int
CatPtr *Cat
Animal Animal
}
func constructNilTestStruct() interface{} {
return NilTestStruct{}
}
func instantiateNilTestStruct() (interface{}, interface{}) {
return NilTestStruct{}, &NilTestStruct{}
}
func validateNilTestStruct(o interface{}, t *testing.T) {
nts := o.(NilTestStruct)
if nts.IntPtr != nil {
t.Errorf("Expected nts.IntPtr to be nil, got %v", nts.IntPtr)
}
if nts.CatPtr != nil {
t.Errorf("Expected nts.CatPtr to be nil, got %v", nts.CatPtr)
}
if nts.Animal != nil {
t.Errorf("Expected nts.Animal to be nil, got %v", nts.Animal)
}
}
@@ -252,7 +281,7 @@ func constructComplexArray() interface{} {
Bytes: []byte("Bytes"),
},
},
&Dog{ // Even though it's a *Dog, we'll get a Dog{} back.
Dog{
SimpleStruct{
String: "Woof",
Bytes: []byte("Bark"),
@@ -321,11 +350,14 @@ func init() {
testCases = append(testCases, TestCase{constructComplex, instantiateComplex, validateComplex})
testCases = append(testCases, TestCase{constructComplex2, instantiateComplex2, validateComplex2})
testCases = append(testCases, TestCase{constructComplexArray, instantiateComplexArray, validateComplexArray})
testCases = append(testCases, TestCase{constructNilTestStruct, instantiateNilTestStruct, validateNilTestStruct})
}
func TestBinary(t *testing.T) {
for _, testCase := range testCases {
for i, testCase := range testCases {
log.Info(fmt.Sprintf("Running test case %v", i))
// Construct an object
o := testCase.Constructor()
@@ -340,7 +372,7 @@ func TestBinary(t *testing.T) {
n, err := new(int64), new(error)
res := ReadBinary(instance, bytes.NewReader(data), n, err)
if *err != nil {
t.Fatalf("Failed to read cat: %v", *err)
t.Fatalf("Failed to read into instance: %v", *err)
}
// Validate object
@@ -350,7 +382,7 @@ func TestBinary(t *testing.T) {
n, err = new(int64), new(error)
res = ReadBinary(instancePtr, bytes.NewReader(data), n, err)
if *err != nil {
t.Fatalf("Failed to read cat: %v", *err)
t.Fatalf("Failed to read into instance: %v", *err)
}
if res != instancePtr {
@@ -365,7 +397,9 @@ func TestBinary(t *testing.T) {
func TestJSON(t *testing.T) {
for _, testCase := range testCases {
for i, testCase := range testCases {
log.Info(fmt.Sprintf("Running test case %v", i))
// Construct an object
o := testCase.Constructor()

View File

@@ -10,7 +10,7 @@ func BinaryBytes(o interface{}) []byte {
w, n, err := new(bytes.Buffer), new(int64), new(error)
WriteBinary(o, w, n, err)
if *err != nil {
panic(err)
panic(*err)
}
return w.Bytes()
}
@@ -19,7 +19,7 @@ func JSONBytes(o interface{}) []byte {
w, n, err := new(bytes.Buffer), new(int64), new(error)
WriteJSON(o, w, n, err)
if *err != nil {
panic(err)
panic(*err)
}
return w.Bytes()
}
@@ -42,7 +42,7 @@ func BinarySha256(o interface{}) []byte {
hasher, n, err := sha256.New(), new(int64), new(error)
WriteBinary(o, hasher, n, err)
if *err != nil {
panic(err)
panic(*err)
}
return hasher.Sum(nil)
}
@@ -51,7 +51,7 @@ func BinaryRipemd160(o interface{}) []byte {
hasher, n, err := ripemd160.New(), new(int64), new(error)
WriteBinary(o, hasher, n, err)
if *err != nil {
panic(err)
panic(*err)
}
return hasher.Sum(nil)
}