Skip to content

Commit

Permalink
General refactoring, improve simple codec, improve tests and benchmarks.
Browse files Browse the repository at this point in the history
Fixes #30
  • Loading branch information
ugorji committed Feb 20, 2014
1 parent cdeae7b commit 1c58769
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 232 deletions.
72 changes: 51 additions & 21 deletions codec/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ func benchInit() {

benchCheckers = append(benchCheckers,
benchChecker{"msgpack", fnMsgpackEncodeFn, fnMsgpackDecodeFn},
benchChecker{"binc", fnBincEncodeFn, fnBincDecodeFn},
benchChecker{"binc-nosym", fnBincNoSymEncodeFn, fnBincNoSymDecodeFn},
benchChecker{"binc-sym", fnBincSymEncodeFn, fnBincSymDecodeFn},
benchChecker{"simple", fnSimpleEncodeFn, fnSimpleDecodeFn},
benchChecker{"gob", fnGobEncodeFn, fnGobDecodeFn},
benchChecker{"json", fnJsonEncodeFn, fnJsonDecodeFn},
)
Expand All @@ -74,7 +76,7 @@ func benchInit() {
func runBenchInit() {
logT(nil, "..............................................")
logT(nil, "BENCHMARK INIT: %v", time.Now())
logT(nil, "To run full benchmark comparing encodings (MsgPack, Binc, JSON, GOB, etc), "+
logT(nil, "To run full benchmark comparing encodings (MsgPack, Binc, Simple, JSON, GOB, etc), "+
"use: \"go test -bench=.\"")
logT(nil, "Benchmark: ")
logT(nil, "\tStruct recursive Depth: %d", benchDepth)
Expand Down Expand Up @@ -209,13 +211,45 @@ func fnMsgpackDecodeFn(buf []byte, ts interface{}) error {
return NewDecoderBytes(buf, testMsgpackH).Decode(ts)
}

func fnBincEncodeFn(ts interface{}) (bs []byte, err error) {
func fnBincEncodeFn(ts interface{}, sym AsSymbolFlag) (bs []byte, err error) {
tSym := testBincH.AsSymbols
testBincH.AsSymbols = sym
err = NewEncoderBytes(&bs, testBincH).Encode(ts)
testBincH.AsSymbols = tSym
return
}

func fnBincDecodeFn(buf []byte, ts interface{}, sym AsSymbolFlag) (err error) {
tSym := testBincH.AsSymbols
testBincH.AsSymbols = sym
err = NewDecoderBytes(buf, testBincH).Decode(ts)
testBincH.AsSymbols = tSym
return
}

func fnBincNoSymEncodeFn(ts interface{}) (bs []byte, err error) {
return fnBincEncodeFn(ts, AsSymbolNone)
}

func fnBincNoSymDecodeFn(buf []byte, ts interface{}) error {
return fnBincDecodeFn(buf, ts, AsSymbolNone)
}

func fnBincSymEncodeFn(ts interface{}) (bs []byte, err error) {
return fnBincEncodeFn(ts, AsSymbolAll)
}

func fnBincSymDecodeFn(buf []byte, ts interface{}) error {
return fnBincDecodeFn(buf, ts, AsSymbolAll)
}

func fnSimpleEncodeFn(ts interface{}) (bs []byte, err error) {
err = NewEncoderBytes(&bs, testSimpleH).Encode(ts)
return
}

func fnBincDecodeFn(buf []byte, ts interface{}) error {
return NewDecoderBytes(buf, testBincH).Decode(ts)
func fnSimpleDecodeFn(buf []byte, ts interface{}) error {
return NewDecoderBytes(buf, testSimpleH).Decode(ts)
}

func fnGobEncodeFn(ts interface{}) ([]byte, error) {
Expand Down Expand Up @@ -245,31 +279,27 @@ func Benchmark__Msgpack____Decode(b *testing.B) {
}

func Benchmark__Binc_NoSym_Encode(b *testing.B) {
tSym := testBincH.AsSymbols
testBincH.AsSymbols = AsSymbolNone
fnBenchmarkEncode(b, "binc", benchTs, fnBincEncodeFn)
testBincH.AsSymbols = tSym
fnBenchmarkEncode(b, "binc", benchTs, fnBincNoSymEncodeFn)
}

func Benchmark__Binc_NoSym_Decode(b *testing.B) {
tSym := testBincH.AsSymbols
testBincH.AsSymbols = AsSymbolNone
fnBenchmarkDecode(b, "binc", benchTs, fnBincEncodeFn, fnBincDecodeFn, fnBenchNewTs)
testBincH.AsSymbols = tSym
fnBenchmarkDecode(b, "binc", benchTs, fnBincNoSymEncodeFn, fnBincNoSymDecodeFn, fnBenchNewTs)
}

func Benchmark__Binc_Sym___Encode(b *testing.B) {
tSym := testBincH.AsSymbols
testBincH.AsSymbols = AsSymbolAll
fnBenchmarkEncode(b, "binc", benchTs, fnBincEncodeFn)
testBincH.AsSymbols = tSym
fnBenchmarkEncode(b, "binc", benchTs, fnBincSymEncodeFn)
}

func Benchmark__Binc_Sym___Decode(b *testing.B) {
tSym := testBincH.AsSymbols
testBincH.AsSymbols = AsSymbolAll
fnBenchmarkDecode(b, "binc", benchTs, fnBincEncodeFn, fnBincDecodeFn, fnBenchNewTs)
testBincH.AsSymbols = tSym
fnBenchmarkDecode(b, "binc", benchTs, fnBincSymEncodeFn, fnBincSymDecodeFn, fnBenchNewTs)
}

func Benchmark__Simple____Encode(b *testing.B) {
fnBenchmarkEncode(b, "simple", benchTs, fnSimpleEncodeFn)
}

func Benchmark__Simple____Decode(b *testing.B) {
fnBenchmarkDecode(b, "simple", benchTs, fnSimpleEncodeFn, fnSimpleDecodeFn, fnBenchNewTs)
}

func Benchmark__Gob________Encode(b *testing.B) {
Expand Down
19 changes: 4 additions & 15 deletions codec/binc.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,13 @@ func (e *bincEncDriver) encodeFloat64(f float64) {
}

func (e *bincEncDriver) encIntegerPrune(bd byte, pos bool, v uint64, lim uint8) {
eb := e.b[:lim]
if lim == 4 {
bigen.PutUint32(eb, uint32(v))
bigen.PutUint32(e.b[:lim], uint32(v))
} else {
bigen.PutUint64(eb, v)
bigen.PutUint64(e.b[:lim], v)
}
if bincDoPrune {
i := pruneSignExt(eb, pos)
i := pruneSignExt(e.b[:lim], pos)
e.w.writen1(bd | lim - 1 - byte(i))
e.w.writeb(e.b[i:lim])
} else {
Expand Down Expand Up @@ -530,17 +529,7 @@ func (d *bincDecDriver) decodeFloat(chkOverflow32 bool) (f float64) {
_, i, _ := d.decIntAny()
f = float64(i)
}

// check overflow (logic adapted from std pkg reflect/value.go OverflowFloat()
if chkOverflow32 {
f2 := f
if f2 < 0 {
f2 = -f
}
if math.MaxFloat32 < f2 && f2 <= math.MaxFloat64 {
decErr("Overflow float32 value: %v", f2)
}
}
checkOverflowFloat32(f, chkOverflow32)
d.bdRead = false
return
}
Expand Down
95 changes: 52 additions & 43 deletions codec/codecs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ var (
testRpcInt = new(TestRpcInt)
testMsgpackH = &MsgpackHandle{}
testBincH = &BincHandle{}
testSimpleH = &SimpleHandle{}
)

func testInitFlags() {
Expand Down Expand Up @@ -277,18 +278,20 @@ func testInit() {
testMsgpackH.RawToString = true
// testMsgpackH.AddExt(byteSliceTyp, 0, testMsgpackH.BinaryEncodeExt, testMsgpackH.BinaryDecodeExt)
// testMsgpackH.AddExt(timeTyp, 1, testMsgpackH.TimeEncodeExt, testMsgpackH.TimeDecodeExt)
testMsgpackH.AddExt(timeTyp, 1,
func(rv reflect.Value) ([]byte, error) {
return encodeTime(rv.Interface().(time.Time)), nil
},
func(rv reflect.Value, bs []byte) error {
tt, err := decodeTime(bs)
if err == nil {
rv.Set(reflect.ValueOf(tt))
}
return err
},
)
timeEncExt := func(rv reflect.Value) ([]byte, error) {
return encodeTime(rv.Interface().(time.Time)), nil
}
timeDecExt := func(rv reflect.Value, bs []byte) error {
tt, err := decodeTime(bs)
if err == nil {
rv.Set(reflect.ValueOf(tt))
}
return err
}

// add extensions for msgpack, simple for time.Time, so we can encode/decode same way.
testMsgpackH.AddExt(timeTyp, 1, timeEncExt, timeDecExt)
testSimpleH.AddExt(timeTyp, 1, timeEncExt, timeDecExt)

primitives := []interface{}{
int8(-8),
Expand Down Expand Up @@ -561,43 +564,33 @@ func doTestCodecTableOne(t *testing.T, testNil bool, h Handle,
func testCodecTableOne(t *testing.T, h Handle) {
// func TestMsgpackAllExperimental(t *testing.T) {
// dopts := testDecOpts(nil, nil, false, true, true),
var oldWriteExt, oldRawToString bool

switch v := h.(type) {
case *MsgpackHandle:
var oldWriteExt, oldRawToString bool
oldWriteExt, v.WriteExt = v.WriteExt, true
oldRawToString, v.RawToString = v.RawToString, true
}
doTestCodecTableOne(t, false, h, table, tableVerify)
//if true { panic("") }
switch v := h.(type) {
case *MsgpackHandle:
doTestCodecTableOne(t, false, h, table, tableVerify)
v.WriteExt, v.RawToString = oldWriteExt, oldRawToString
default:
doTestCodecTableOne(t, false, h, table, tableVerify)
}
// func TestMsgpackAll(t *testing.T) {

idxTime, numPrim, numMap := 19, 23, 4

//skip []interface{} containing time.Time
doTestCodecTableOne(t, false, h, table[:numPrim], tableVerify[:numPrim])
doTestCodecTableOne(t, false, h, table[numPrim+1:], tableVerify[numPrim+1:])
// func TestMsgpackNilStringMap(t *testing.T) {
var oldMapType reflect.Type
switch v := h.(type) {
case *MsgpackHandle:
oldMapType, v.MapType = v.MapType, mapStrIntfTyp
case *BincHandle:
oldMapType, v.MapType = v.MapType, mapStrIntfTyp
}
v := h.getBasicHandle()
oldMapType, v.MapType = v.MapType, mapStrIntfTyp

//skip time.Time, []interface{} containing time.Time, last map, and newStruc
doTestCodecTableOne(t, true, h, table[:idxTime], tableTestNilVerify[:idxTime])
doTestCodecTableOne(t, true, h, table[numPrim+1:numPrim+numMap], tableTestNilVerify[numPrim+1:numPrim+numMap])

switch v := h.(type) {
case *MsgpackHandle:
v.MapType = oldMapType
case *BincHandle:
v.MapType = oldMapType
}
v.MapType = oldMapType

// func TestMsgpackNilIntf(t *testing.T) {

Expand Down Expand Up @@ -951,6 +944,30 @@ func doTestMsgpackRpcSpecPythonClientToGoSvc(t *testing.T) {
fmt.Sprintf("%#v\n%#v\n", []string{"A1", "B2", "C3"}, TestABC{"Aa", "Bb", "Cc"}), "cmdout=")
}

func TestBincCodecsTable(t *testing.T) {
testCodecTableOne(t, testBincH)
}

func TestBincCodecsMisc(t *testing.T) {
testCodecMiscOne(t, testBincH)
}

func TestBincCodecsEmbeddedPointer(t *testing.T) {
testCodecEmbeddedPointer(t, testBincH)
}

func TestSimpleCodecsTable(t *testing.T) {
testCodecTableOne(t, testSimpleH)
}

func TestSimpleCodecsMisc(t *testing.T) {
testCodecMiscOne(t, testSimpleH)
}

func TestSimpleCodecsEmbeddedPointer(t *testing.T) {
testCodecEmbeddedPointer(t, testSimpleH)
}

func TestMsgpackCodecsTable(t *testing.T) {
testCodecTableOne(t, testMsgpackH)
}
Expand All @@ -963,16 +980,12 @@ func TestMsgpackCodecsEmbeddedPointer(t *testing.T) {
testCodecEmbeddedPointer(t, testMsgpackH)
}

func TestBincCodecsTable(t *testing.T) {
testCodecTableOne(t, testBincH)
}

func TestBincCodecsMisc(t *testing.T) {
testCodecMiscOne(t, testBincH)
func TestBincRpcGo(t *testing.T) {
doTestRpcOne(t, GoRpc, testBincH, true, 0)
}

func TestBincCodecsEmbeddedPointer(t *testing.T) {
testCodecEmbeddedPointer(t, testBincH)
func _TestSimpleRpcGo(t *testing.T) {
doTestRpcOne(t, GoRpc, testSimpleH, true, 0)
}

func TestMsgpackRpcGo(t *testing.T) {
Expand All @@ -983,10 +996,6 @@ func TestMsgpackRpcSpec(t *testing.T) {
doTestRpcOne(t, MsgpackSpecRpc, testMsgpackH, true, 0)
}

func TestBincRpcGo(t *testing.T) {
doTestRpcOne(t, GoRpc, testBincH, true, 0)
}

// TODO:
// Add Tests for:
// - decoding empty list/map in stream into a nil slice/map
Expand Down
6 changes: 6 additions & 0 deletions codec/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ type ioDecReader struct {
}

func (z *ioDecReader) readn(n int) (bs []byte) {
if n <= 0 {
return
}
bs = make([]byte, n)
if _, err := io.ReadAtLeast(z.r, bs, n); err != nil {
panic(err)
Expand Down Expand Up @@ -133,6 +136,9 @@ func (z *bytesDecReader) consume(n int) (oldcursor int) {
}

func (z *bytesDecReader) readn(n int) (bs []byte) {
if n <= 0 {
return
}
c0 := z.consume(n)
bs = z.b[c0:z.c]
return
Expand Down
14 changes: 10 additions & 4 deletions codec/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ func (z *ioEncWriter) writeUint64(v uint64) {
}

func (z *ioEncWriter) writeb(bs []byte) {
if len(bs) == 0 {
return
}
n, err := z.w.Write(bs)
if err != nil {
panic(err)
Expand Down Expand Up @@ -220,6 +223,9 @@ func (z *bytesEncWriter) writeUint64(v uint64) {
}

func (z *bytesEncWriter) writeb(s []byte) {
if len(s) == 0 {
return
}
c := z.grow(len(s))
copy(z.b[c:], s)
}
Expand Down Expand Up @@ -385,7 +391,7 @@ func (f *encFnInfo) kSlice(rv reflect.Value) {
f.ee.encodeStringBytes(c_RAW, rv.Bytes())
return
}

l := rv.Len()
if f.ti.mbs {
if l%2 == 1 {
Expand All @@ -410,15 +416,15 @@ func (f *encFnInfo) kArray(rv reflect.Value) {
// So we have to duplicate the functionality here.
// f.e.encodeValue(rv.Slice(0, rv.Len()))
// f.kSlice(rv.Slice(0, rv.Len()))

l := rv.Len()
// Handle an array of bytes specially (in line with what is done for slices)
if f.ti.rt.Elem().Kind() == reflect.Uint8 {
if l == 0 {
f.ee.encodeStringBytes(c_RAW, nil)
return
}
var bs []byte
var bs []byte
if rv.CanAddr() {
bs = rv.Slice(0, l).Bytes()
} else {
Expand All @@ -430,7 +436,7 @@ func (f *encFnInfo) kArray(rv reflect.Value) {
f.ee.encodeStringBytes(c_RAW, bs)
return
}

if f.ti.mbs {
if l%2 == 1 {
encErr("mapBySlice: invalid length (must be divisible by 2): %v", l)
Expand Down
Loading

0 comments on commit 1c58769

Please sign in to comment.