1
0
mirror of https://github.com/golang/go synced 2024-11-22 20:04:47 -07:00

encoding/binary: make type error more specific

Right now it says 'invalid type S' for a struct type S.
Instead, say which type inside the struct is the problem.

Fixes #4825.

R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7301102
This commit is contained in:
Russ Cox 2013-02-15 13:12:28 -05:00
parent d47cc872b5
commit 2b9787c2f3
2 changed files with 39 additions and 27 deletions

View File

@ -167,9 +167,9 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
default: default:
return errors.New("binary.Read: invalid type " + d.Type().String()) return errors.New("binary.Read: invalid type " + d.Type().String())
} }
size := dataSize(v) size, err := dataSize(v)
if size < 0 { if err != nil {
return errors.New("binary.Read: invalid type " + v.Type().String()) return errors.New("binary.Read: " + err.Error())
} }
d := &decoder{order: order, buf: make([]byte, size)} d := &decoder{order: order, buf: make([]byte, size)}
if _, err := io.ReadFull(r, d.buf); err != nil { if _, err := io.ReadFull(r, d.buf); err != nil {
@ -247,64 +247,68 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error {
// Fallback to reflect-based encoding. // Fallback to reflect-based encoding.
v := reflect.Indirect(reflect.ValueOf(data)) v := reflect.Indirect(reflect.ValueOf(data))
size := dataSize(v) size, err := dataSize(v)
if size < 0 { if err != nil {
return errors.New("binary.Write: invalid type " + v.Type().String()) return errors.New("binary.Write: " + err.Error())
} }
buf := make([]byte, size) buf := make([]byte, size)
e := &encoder{order: order, buf: buf} e := &encoder{order: order, buf: buf}
e.value(v) e.value(v)
_, err := w.Write(buf) _, err = w.Write(buf)
return err return err
} }
// Size returns how many bytes Write would generate to encode the value v, which // Size returns how many bytes Write would generate to encode the value v, which
// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. // must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
func Size(v interface{}) int { func Size(v interface{}) int {
return dataSize(reflect.Indirect(reflect.ValueOf(v))) n, err := dataSize(reflect.Indirect(reflect.ValueOf(v)))
if err != nil {
return -1
}
return n
} }
// dataSize returns the number of bytes the actual data represented by v occupies in memory. // dataSize returns the number of bytes the actual data represented by v occupies in memory.
// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice // For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice
// it returns the length of the slice times the element size and does not count the memory // it returns the length of the slice times the element size and does not count the memory
// occupied by the header. // occupied by the header.
func dataSize(v reflect.Value) int { func dataSize(v reflect.Value) (int, error) {
if v.Kind() == reflect.Slice { if v.Kind() == reflect.Slice {
elem := sizeof(v.Type().Elem()) elem, err := sizeof(v.Type().Elem())
if elem < 0 { if err != nil {
return -1 return 0, err
} }
return v.Len() * elem return v.Len() * elem, nil
} }
return sizeof(v.Type()) return sizeof(v.Type())
} }
func sizeof(t reflect.Type) int { func sizeof(t reflect.Type) (int, error) {
switch t.Kind() { switch t.Kind() {
case reflect.Array: case reflect.Array:
n := sizeof(t.Elem()) n, err := sizeof(t.Elem())
if n < 0 { if err != nil {
return -1 return 0, err
} }
return t.Len() * n return t.Len() * n, nil
case reflect.Struct: case reflect.Struct:
sum := 0 sum := 0
for i, n := 0, t.NumField(); i < n; i++ { for i, n := 0, t.NumField(); i < n; i++ {
s := sizeof(t.Field(i).Type) s, err := sizeof(t.Field(i).Type)
if s < 0 { if err != nil {
return -1 return 0, err
} }
sum += s sum += s
} }
return sum return sum, nil
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
return int(t.Size()) return int(t.Size()), nil
} }
return -1 return 0, errors.New("invalid type " + t.String())
} }
type coder struct { type coder struct {
@ -514,11 +518,12 @@ func (e *encoder) value(v reflect.Value) {
} }
func (d *decoder) skip(v reflect.Value) { func (d *decoder) skip(v reflect.Value) {
d.buf = d.buf[dataSize(v):] n, _ := dataSize(v)
d.buf = d.buf[n:]
} }
func (e *encoder) skip(v reflect.Value) { func (e *encoder) skip(v reflect.Value) {
n := dataSize(v) n, _ := dataSize(v)
for i := range e.buf[0:n] { for i := range e.buf[0:n] {
e.buf[i] = 0 e.buf[i] = 0
} }

View File

@ -9,6 +9,7 @@ import (
"io" "io"
"math" "math"
"reflect" "reflect"
"strings"
"testing" "testing"
) )
@ -149,8 +150,14 @@ func TestWriteT(t *testing.T) {
tv := reflect.Indirect(reflect.ValueOf(ts)) tv := reflect.Indirect(reflect.ValueOf(ts))
for i, n := 0, tv.NumField(); i < n; i++ { for i, n := 0, tv.NumField(); i < n; i++ {
typ := tv.Field(i).Type().String()
if typ == "[4]int" {
typ = "int" // the problem is int, not the [4]
}
if err := Write(buf, BigEndian, tv.Field(i).Interface()); err == nil { if err := Write(buf, BigEndian, tv.Field(i).Interface()); err == nil {
t.Errorf("WriteT.%v: have err == nil, want non-nil", tv.Field(i).Type()) t.Errorf("WriteT.%v: have err == nil, want non-nil", tv.Field(i).Type())
} else if !strings.Contains(err.Error(), typ) {
t.Errorf("WriteT: have err == %q, want it to mention %s", err, typ)
} }
} }
} }
@ -238,7 +245,7 @@ func BenchmarkReadStruct(b *testing.B) {
bsr := &byteSliceReader{} bsr := &byteSliceReader{}
var buf bytes.Buffer var buf bytes.Buffer
Write(&buf, BigEndian, &s) Write(&buf, BigEndian, &s)
n := dataSize(reflect.ValueOf(s)) n, _ := dataSize(reflect.ValueOf(s))
b.SetBytes(int64(n)) b.SetBytes(int64(n))
t := s t := s
b.ResetTimer() b.ResetTimer()