io: optimize BinReader.ReadArray()
reflect.MethodByName is a rather expensive function especially when called on hot path. This became obvious during profiling of db restore. This commit replaces reflection with a cast to an interface.
This commit is contained in:
parent
e4d821f32d
commit
1784a14148
3 changed files with 18 additions and 33 deletions
|
@ -47,19 +47,14 @@ func (r *BinReader) ReadArray(t interface{}, maxSize ...int) {
|
|||
panic(value.Type().String() + " is not a pointer to a slice")
|
||||
}
|
||||
|
||||
sliceType := value.Elem().Type()
|
||||
elemType := sliceType.Elem()
|
||||
isPtr := elemType.Kind() == reflect.Ptr
|
||||
if isPtr {
|
||||
checkHasDecodeBinary(elemType)
|
||||
} else {
|
||||
checkHasDecodeBinary(reflect.PtrTo(elemType))
|
||||
}
|
||||
|
||||
if r.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sliceType := value.Elem().Type()
|
||||
elemType := sliceType.Elem()
|
||||
isPtr := elemType.Kind() == reflect.Ptr
|
||||
|
||||
ms := maxArraySize
|
||||
if len(maxSize) != 0 {
|
||||
ms = maxSize[0]
|
||||
|
@ -82,27 +77,18 @@ func (r *BinReader) ReadArray(t interface{}, maxSize ...int) {
|
|||
} else {
|
||||
elem = arr.Index(i).Addr()
|
||||
}
|
||||
method := elem.MethodByName("DecodeBinary")
|
||||
method.Call([]reflect.Value{reflect.ValueOf(r)})
|
||||
|
||||
el, ok := elem.Interface().(decodable)
|
||||
if !ok {
|
||||
panic(elemType.String() + "is not decodable")
|
||||
}
|
||||
|
||||
el.DecodeBinary(r)
|
||||
}
|
||||
|
||||
value.Elem().Set(arr)
|
||||
}
|
||||
|
||||
func checkHasDecodeBinary(v reflect.Type) {
|
||||
method, ok := v.MethodByName("DecodeBinary")
|
||||
if !ok || !isDecodeBinaryMethod(method) {
|
||||
panic(v.String() + " does not have DecodeBinary(*io.BinReader)")
|
||||
}
|
||||
}
|
||||
|
||||
func isDecodeBinaryMethod(method reflect.Method) bool {
|
||||
t := method.Type
|
||||
return t != nil &&
|
||||
t.NumIn() == 2 && t.In(1) == reflect.TypeOf((*BinReader)(nil)) &&
|
||||
t.NumOut() == 0
|
||||
}
|
||||
|
||||
// ReadBE reads from the underlying io.Reader
|
||||
// into the interface v in big-endian format.
|
||||
func (r *BinReader) ReadBE(v interface{}) {
|
||||
|
|
|
@ -326,15 +326,10 @@ func TestBinReader_ReadArray(t *testing.T) {
|
|||
require.NoError(t, r.Err)
|
||||
require.Equal(t, []testSerializable{}, arrVal)
|
||||
|
||||
r = NewBinReaderFromBuf([]byte{0})
|
||||
r.Err = errors.New("error")
|
||||
require.Panics(t, func() { r.ReadArray(&[]*int{}) })
|
||||
r = NewBinReaderFromBuf([]byte{1})
|
||||
require.Panics(t, func() { r.ReadArray(&[]int{1}) })
|
||||
|
||||
r = NewBinReaderFromBuf([]byte{0})
|
||||
r.Err = errors.New("error")
|
||||
require.Panics(t, func() { r.ReadArray(&[]int{}) })
|
||||
|
||||
r = NewBinReaderFromBuf([]byte{0})
|
||||
r.Err = errors.New("error")
|
||||
require.Panics(t, func() { r.ReadArray(0) })
|
||||
require.Panics(t, func() { r.ReadArray(1) })
|
||||
}
|
||||
|
|
|
@ -10,3 +10,7 @@ type Serializable interface {
|
|||
DecodeBinary(*BinReader)
|
||||
EncodeBinary(*BinWriter)
|
||||
}
|
||||
|
||||
type decodable interface {
|
||||
DecodeBinary(*BinReader)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue