浏览代码

codec: Fixes and clarifications for binc and rpc, in prep for c library.

- binc support for pruned values fixed.
- Added closed flag to rpcCodec, and use buffered reader for tracking custom header.
- Added simple codec - a much simplified version of binc (mostly academic).
- Built c implementation of binc support (for use in c, python, etc).
Ugorji Nwoke 12 年之前
父节点
当前提交
cdeae7b766
共有 10 个文件被更改,包括 606 次插入152 次删除
  1. 37 83
      codec/binc.go
  2. 18 17
      codec/codecs_test.go
  3. 16 14
      codec/decode.go
  4. 3 2
      codec/ext_dep_test.go
  5. 18 1
      codec/helper.go
  6. 24 19
      codec/helper_internal.go
  7. 19 14
      codec/msgpack.go
  8. 18 0
      codec/rpc.go
  9. 451 0
      codec/simple.go
  10. 2 2
      codec/time.go

+ 37 - 83
codec/binc.go

@@ -11,6 +11,8 @@ import (
 	//"fmt"
 )
 
+const bincDoPrune = true // No longer needed. Needed before as C lib did not support pruning.
+
 //var _ = fmt.Printf
 
 // vd as low 4 bits (there are 16 slots)
@@ -104,52 +106,53 @@ func (e *bincEncDriver) encodeFloat64(f float64) {
 		return
 	}
 	bigen.PutUint64(e.b[:], math.Float64bits(f))
-	var i int = 7
-	for ; i >= 0 && (e.b[i] == 0); i-- {
-	}
-	i++
-	if i > 6 { // 7 or 8 ie < 2 trailing zeros
-		e.w.writen1(bincVdFloat<<4 | bincFlBin64)
-		e.w.writeb(e.b[:])
-	} else {
-		e.w.writen1(bincVdFloat<<4 | 0x8 | bincFlBin64)
-		e.w.writen1(byte(i))
-		e.w.writeb(e.b[:i])
+	if bincDoPrune {
+		i := 7
+		for ; i >= 0 && (e.b[i] == 0); i-- {
+		}
+		i++
+		if i <= 6 {
+			e.w.writen1(bincVdFloat<<4 | 0x8 | bincFlBin64)
+			e.w.writen1(byte(i))
+			e.w.writeb(e.b[:i])
+			return
+		}
 	}
+	e.w.writen1(bincVdFloat<<4 | bincFlBin64)
+	e.w.writeb(e.b[:])
 }
 
-func (e *bincEncDriver) encInteger4(bd byte, v uint32) {
-	const lim int = 4
-	eb := e.b[:lim]
-	bigen.PutUint32(eb, v)
-	i := pruneSignExt(eb)
-	e.w.writen1(bd | byte(lim-1-i))
-	e.w.writeb(e.b[i:lim])
-}
-
-func (e *bincEncDriver) encInteger8(bd byte, v uint64) {
-	const lim int = 8
+func (e *bincEncDriver) encIntegerPrune(bd byte, pos bool, v uint64, lim uint8) {
 	eb := e.b[:lim]
-	bigen.PutUint64(eb, v)
-	i := pruneSignExt(eb)
-	e.w.writen1(bd | byte(lim-1-i))
-	e.w.writeb(e.b[i:lim])
+	if lim == 4 {
+		bigen.PutUint32(eb, uint32(v))
+	} else {
+		bigen.PutUint64(eb, v)
+	}
+	if bincDoPrune {
+		i := pruneSignExt(eb, pos)
+		e.w.writen1(bd | lim - 1 - byte(i))
+		e.w.writeb(e.b[i:lim])
+	} else {
+		e.w.writen1(bd | lim - 1)
+		e.w.writeb(e.b[:lim])
+	}
 }
 
 func (e *bincEncDriver) encodeInt(v int64) {
 	const nbd byte = bincVdNegInt << 4
 	switch {
 	case v >= 0:
-		e.encUint(bincVdPosInt << 4, true, uint64(v))
+		e.encUint(bincVdPosInt<<4, true, uint64(v))
 	case v == -1:
 		e.w.writen1(bincVdSpecial<<4 | bincSpNegOne)
 	default:
-		e.encUint(bincVdNegInt << 4, false, uint64(-v))
+		e.encUint(bincVdNegInt<<4, false, uint64(-v))
 	}
 }
 
 func (e *bincEncDriver) encodeUint(v uint64) {
-	e.encUint(bincVdPosInt << 4, true, v)
+	e.encUint(bincVdPosInt<<4, true, v)
 }
 
 func (e *bincEncDriver) encUint(bd byte, pos bool, v uint64) {
@@ -164,9 +167,9 @@ func (e *bincEncDriver) encUint(bd byte, pos bool, v uint64) {
 		e.w.writen1(bd | 0x01)
 		e.w.writeUint16(uint16(v))
 	case v <= math.MaxUint32:
-		e.encInteger4(bd, uint32(v))
+		e.encIntegerPrune(bd, pos, v, 4)
 	default:
-		e.encInteger8(bd, v)
+		e.encIntegerPrune(bd, pos, v, 8)
 	}
 }
 
@@ -428,45 +431,6 @@ func (d *bincDecDriver) decFloat() (f float64) {
 	return
 }
 
-// func (d *bincDecDriver) decInt() (v int64) {
-// 	// need to inline the code (interface conversion and type assertion expensive)
-// 	switch d.vs {
-// 	case 0:
-// 		v = int64(int8(d.r.readn1()))
-// 	case 1:
-// 		d.r.readb(d.b[6:])
-// 		v = int64(int16(bigen.Uint16(d.b[6:])))
-// 	case 2:
-// 		d.r.readb(d.b[5:])
-// 		if d.b[5]&0x80 == 0 {
-// 			d.b[4] = 0
-// 		} else {
-// 			d.b[4] = 0xff
-// 		}
-// 		v = int64(int32(bigen.Uint32(d.b[4:])))
-// 	case 3:
-// 		d.r.readb(d.b[4:])
-// 		v = int64(int32(bigen.Uint32(d.b[4:])))
-// 	case 4, 5, 6:
-// 		lim := int(7 - d.vs)
-// 		d.r.readb(d.b[lim:])
-// 		var fillval byte = 0
-// 		if d.b[lim]&0x80 != 0 {
-// 			fillval = 0xff
-// 		}
-// 		for i := 0; i < lim; i++ {
-// 			d.b[i] = fillval
-// 		}
-// 		v = int64(bigen.Uint64(d.b[:]))
-// 	case 7:
-// 		d.r.readb(d.b[:])
-// 		v = int64(bigen.Uint64(d.b[:]))
-// 	default:
-// 		decErr("integers with greater than 64 bits of precision not supported")
-// 	}
-// 	return
-// }
-
 func (d *bincDecDriver) decUint() (v uint64) {
 	// need to inline the code (interface conversion and type assertion expensive)
 	switch d.vs {
@@ -529,12 +493,7 @@ func (d *bincDecDriver) decIntAny() (ui uint64, i int64, neg bool) {
 
 func (d *bincDecDriver) decodeInt(bitsize uint8) (i int64) {
 	_, i, _ = d.decIntAny()
-	// check overflow (logic adapted from std pkg reflect/value.go OverflowUint()
-	if bitsize > 0 {
-		if trunc := (i << (64 - bitsize)) >> (64 - bitsize); i != trunc {
-			decErr("Overflow int value: %v", i)
-		}
-	}
+	checkOverflow(0, i, bitsize)
 	d.bdRead = false
 	return
 }
@@ -544,12 +503,7 @@ func (d *bincDecDriver) decodeUint(bitsize uint8) (ui uint64) {
 	if neg {
 		decErr("Assigning negative signed value: %v, to unsigned type", i)
 	}
-	// check overflow (logic adapted from std pkg reflect/value.go OverflowUint()
-	if bitsize > 0 {
-		if trunc := (ui << (64 - bitsize)) >> (64 - bitsize); ui != trunc {
-			decErr("Overflow uint value: %v", ui)
-		}
-	}
+	checkOverflow(ui, 0, bitsize)
 	d.bdRead = false
 	return
 }
@@ -759,7 +713,7 @@ func (d *bincDecDriver) decodeNaked() (v interface{}, vt valueType, decodeFurthe
 		}
 	case bincVdSmallInt:
 		vt = valueTypeUint
-		v = int64(int8(d.vs)) + 1 // int8(d.vs) + 1
+		v = uint64(int8(d.vs)) + 1 // int8(d.vs) + 1
 	case bincVdPosInt:
 		vt = valueTypeUint
 		v = d.decUint()

+ 18 - 17
codec/codecs_test.go

@@ -345,9 +345,9 @@ func testInit() {
 			"SHORT STRING": "1234567890",
 		},
 		map[interface{}]interface{}{
-			true:     "true",
+			true:       "true",
 			uint8(138): false,
-			"false":  uint8(200),
+			"false":    uint8(200),
 		},
 		newTestStruc(0, false),
 	}
@@ -433,7 +433,7 @@ func testUnmarshalErr(v interface{}, data []byte, h Handle, t *testing.T, name s
 		logT(t, "Error Decoding into %s: %v, Err: %v", name, v, err)
 		t.FailNow()
 	}
-	return   
+	return
 }
 
 func newTestStruc(depth int, bench bool) (ts *TestStruc) {
@@ -532,7 +532,6 @@ func doTestCodecTableOne(t *testing.T, testNil bool, h Handle,
 		}
 
 		logT(t, "         v1 returned: %T, %#v", v1, v1)
-		// t.FailNow() //todo: ugorji: remove
 		// if v1 != nil {
 		//	logT(t, "         v1 returned: %T, %#v", v1, v1)
 		//	//we always indirect, because ptr to typed value may be passed (if not testNil)
@@ -666,7 +665,7 @@ func testCodecMiscOne(t *testing.T, h Handle) {
 		logT(t, "Not Equal: %v. m: %v, m2: %v", err, m, m2)
 		t.FailNow()
 	}
-	
+
 	// func TestMsgpackDecodeStructSubset(t *testing.T) {
 	// test that we can decode a subset of the stream
 	mm := map[string]interface{}{"A": 5, "B": 99, "C": 333}
@@ -683,16 +682,16 @@ func testCodecMiscOne(t *testing.T, h Handle) {
 	// println(">>>>>")
 	// test simple arrays, non-addressable arrays, slices
 	type tarr struct {
-		A int64 
-		B [3]int64 
-		C []byte  
-		D [3]byte 
+		A int64
+		B [3]int64
+		C []byte
+		D [3]byte
 	}
-	var tarr0 = tarr{1, [3]int64{2,3,4}, []byte{4,5,6}, [3]byte{7,8,9} }
+	var tarr0 = tarr{1, [3]int64{2, 3, 4}, []byte{4, 5, 6}, [3]byte{7, 8, 9}}
 	// test both pointer and non-pointer (value)
 	for _, tarr1 := range []interface{}{tarr0, &tarr0} {
 		bs, err = testMarshalErr(tarr1, h, t, "tarr1")
-		var tarr2 tarr 
+		var tarr2 tarr
 		testUnmarshalErr(&tarr2, bs, h, t, "tarr2")
 		checkEqualT(t, tarr0, tarr2, "tarr0=tarr2")
 		// fmt.Printf(">>>> err: %v. tarr1: %v, tarr2: %v\n", err, tarr0, tarr2)
@@ -750,17 +749,19 @@ func doTestRpcOne(t *testing.T, rr Rpc, h Handle, doRequest bool, exitSleepMs ti
 	serverFn := func() {
 		for {
 			conn1, err1 := ln.Accept()
-			if err1 != nil {
-				//fmt.Printf("accept err1: %v\n", err1)
-				continue
-			}
+			// if err1 != nil {
+			// 	//fmt.Printf("accept err1: %v\n", err1)
+			// 	continue
+			// }
 			if atomic.LoadUint64(&serverExitFlag) == 1 {
 				serverExitChan <- true
 				conn1.Close()
 				return // exit serverFn goroutine
 			}
-			var sc rpc.ServerCodec = rr.ServerCodec(conn1, h)
-			srv.ServeCodec(sc)
+			if err1 == nil {
+				var sc rpc.ServerCodec = rr.ServerCodec(conn1, h)
+				srv.ServeCodec(sc)
+			}
 		}
 	}
 

+ 16 - 14
codec/decode.go

@@ -415,17 +415,17 @@ func (f *decFnInfo) kStruct(rv reflect.Value) {
 func (f *decFnInfo) kSlice(rv reflect.Value) {
 	// A slice can be set from a map or array in stream.
 	currEncodedType := f.dd.currentEncodedType()
-	
+
 	switch currEncodedType {
 	case valueTypeBytes, valueTypeString:
-		if f.ti.rtid == uint8SliceTypId || f.ti.rt.Elem().Kind() == reflect.Uint8 { 
+		if f.ti.rtid == uint8SliceTypId || f.ti.rt.Elem().Kind() == reflect.Uint8 {
 			if bs2, changed2 := f.dd.decodeBytes(rv.Bytes()); changed2 {
 				rv.SetBytes(bs2)
 			}
 			return
 		}
 	}
-	
+
 	if shortCircuitReflectToFastPath && rv.CanAddr() {
 		switch f.ti.rtid {
 		case intfSliceTypId:
@@ -450,11 +450,11 @@ func (f *decFnInfo) kSlice(rv reflect.Value) {
 	if rv.IsNil() {
 		rv.Set(reflect.MakeSlice(f.ti.rt, containerLenS, containerLenS))
 	}
-	
+
 	if containerLen == 0 {
 		return
 	}
-	
+
 	if rvcap, rvlen := rv.Len(), rv.Cap(); containerLenS > rvcap {
 		if f.array { // !rv.CanSet()
 			decErr(msgDecCannotExpandArr, rvcap, containerLenS)
@@ -634,7 +634,7 @@ func (d *Decoder) decode(iv interface{}) {
 
 	case reflect.Value:
 		d.chkPtrValue(v)
-		d.decodeValue(v)
+		d.decodeValue(v.Elem())
 
 	case *string:
 		*v = d.d.decodeString()
@@ -665,8 +665,7 @@ func (d *Decoder) decode(iv interface{}) {
 	case *float64:
 		*v = d.d.decodeFloat(false)
 	case *[]byte:
-		v2 := *v
-		*v, _ = d.d.decodeBytes(v2)
+		*v, _ = d.d.decodeBytes(*v)
 
 	case *[]interface{}:
 		d.decSliceIntf(v, valueTypeInvalid, false)
@@ -691,7 +690,7 @@ func (d *Decoder) decode(iv interface{}) {
 	default:
 		rv := reflect.ValueOf(iv)
 		d.chkPtrValue(rv)
-		d.decodeValue(rv)
+		d.decodeValue(rv.Elem())
 	}
 }
 
@@ -700,9 +699,15 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 
 	if d.d.tryDecodeAsNil() {
 		// If value in stream is nil, set the dereferenced value to its "zero" value (if settable)
-		for rv.Kind() == reflect.Ptr {
-			rv = rv.Elem()
+		if rv.Kind() == reflect.Ptr {
+			if !rv.IsNil() {
+				rv.Set(reflect.Zero(rv.Type()))
+			}
+			return
 		}
+		// for rv.Kind() == reflect.Ptr {
+		// 	rv = rv.Elem()
+		// }
 		if rv.IsValid() { // rv.CanSet() // always settable, except it's invalid
 			rv.Set(reflect.Zero(rv.Type()))
 		}
@@ -1035,6 +1040,3 @@ func decContLens(dd decDriver, currEncodedType valueType) (containerLen, contain
 func decErr(format string, params ...interface{}) {
 	doPanic(msgTagDec, format, params...)
 }
-
-
-

+ 3 - 2
codec/ext_dep_test.go

@@ -1,4 +1,4 @@
-//+build ignore
+// //+build ignore
 
 // Copyright (c) 2012, 2013 Ugorji Nwoke. All rights reserved.
 // Use of this source code is governed by a BSD-style license found in the LICENSE file.
@@ -17,9 +17,10 @@ package codec
 //       go test -bi -bench=.
 
 import (
+	"testing"
+
 	vmsgpack "github.com/vmihailenco/msgpack"
 	"labix.org/v2/mgo/bson"
-	"testing"
 )
 
 func init() {

+ 18 - 1
codec/helper.go

@@ -74,7 +74,7 @@ const (
 	valueTypeArray
 	valueTypeTimestamp
 	valueTypeExt
-	
+
 	valueTypeInvalid = 0xff
 )
 
@@ -555,3 +555,20 @@ func doPanic(tag string, format string, params ...interface{}) {
 	copy(params2[1:], params)
 	panic(fmt.Errorf("%s: "+format, params2...))
 }
+
+func checkOverflow(ui uint64, i int64, bitsize uint8) {
+	// check overflow (logic adapted from std pkg reflect/value.go OverflowUint()
+	if bitsize == 0 {
+		return
+	}
+	if i != 0 {
+		if trunc := (i << (64 - bitsize)) >> (64 - bitsize); i != trunc {
+			decErr("Overflow int value: %v", i)
+		}
+	}
+	if ui != 0 {
+		if trunc := (ui << (64 - bitsize)) >> (64 - bitsize); ui != trunc {
+			decErr("Overflow uint value: %v", ui)
+		}
+	}
+}

+ 24 - 19
codec/helper_internal.go

@@ -34,6 +34,7 @@ func panicValToErr(panicVal interface{}, err *error) {
 }
 
 func isEmptyValue(v reflect.Value) bool {
+	const deref = true
 	switch v.Kind() {
 	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
 		return v.Len() == 0
@@ -46,7 +47,23 @@ func isEmptyValue(v reflect.Value) bool {
 	case reflect.Float32, reflect.Float64:
 		return v.Float() == 0
 	case reflect.Interface, reflect.Ptr:
-		return v.IsNil()
+		if deref {
+			if v.IsNil() {
+				return true
+			}
+			return isEmptyValue(v.Elem())
+		} else {
+			return v.IsNil()
+		}
+	case reflect.Struct:
+		// return true if all fields are empty. else return false.
+		return v.Interface() == reflect.Zero(v.Type()).Interface()
+		// for i, n := 0, v.NumField(); i < n; i++ {
+		// 	if !isEmptyValue(v.Field(i), deref) {
+		// 		return false
+		// 	}
+		// }
+		// return true
 	}
 	return false
 }
@@ -60,26 +77,14 @@ func debugf(format string, args ...interface{}) {
 	}
 }
 
-func pruneSignExt(v []byte) (n int) {
-	l := len(v)
-	if l < 2 {
-		return
-	}
-	if v[0] == 0 {
-		n2 := n + 1
-		for v[n] == 0 && n2 < l && (v[n2]&(1<<7) == 0) {
-			n++
-			n2++
+func pruneSignExt(v []byte, pos bool) (n int) {
+	if len(v) < 2 {
+	} else if pos && v[0] == 0 {
+		for ; v[n] == 0 && n+1 < len(v) && (v[n+1]&(1<<7) == 0); n++ {
 		}
-		return
-	}
-	if v[0] == 0xff {
-		n2 := n + 1
-		for v[n] == 0xff && n2 < l && (v[n2]&(1<<7) != 0) {
-			n++
-			n2++
+	} else if !pos && v[0] == 0xff {
+		for ; v[n] == 0xff && n+1 < len(v) && (v[n+1]&(1<<7) != 0); n++ {
 		}
-		return
 	}
 	return
 }

+ 19 - 14
codec/msgpack.go

@@ -9,11 +9,11 @@ We need to maintain compatibility with it and how it encodes integer values
 without caring about the type.
 
 For compatibility with behaviour of msgpack-c reference implementation:
-  - Go intX (>0) and uintX 
-       IS ENCODED AS 
+  - Go intX (>0) and uintX
+       IS ENCODED AS
     msgpack +ve fixnum, unsigned
   - Go intX (<0)
-       IS ENCODED AS 
+       IS ENCODED AS
     msgpack -ve fixnum, signed
 
 */
@@ -118,7 +118,7 @@ func (e *msgpackEncDriver) encodeNil() {
 }
 
 func (e *msgpackEncDriver) encodeInt(i int64) {
-	
+
 	switch {
 	case i >= 0:
 		e.encodeUint(uint64(i))
@@ -763,24 +763,29 @@ func (c *msgpackSpecRpcCodec) ReadRequestBody(body interface{}) error {
 
 func (c *msgpackSpecRpcCodec) parseCustomHeader(expectTypeByte byte, msgid *uint64, methodOrError *string) (err error) {
 
+	if c.cls {
+		return io.EOF
+	}
+
 	// We read the response header by hand
 	// so that the body can be decoded on its own from the stream at a later time.
 
-	bs := make([]byte, 1)
-	n, err := c.rwc.Read(bs)
+	const fia byte = 0x94 //four item array descriptor value
+	// Not sure why the panic of EOF is swallowed above.
+	// if bs1 := c.dec.r.readn1(); bs1 != fia {
+	// 	err = fmt.Errorf("Unexpected value for array descriptor: Expecting %v. Received %v", fia, bs1)
+	// 	return
+	// }
+	var b byte
+	b, err = c.br.ReadByte()
 	if err != nil {
 		return
 	}
-	if n != 1 {
-		err = fmt.Errorf("Couldn't read array descriptor: No bytes read")
+	if b != fia {
+		err = fmt.Errorf("Unexpected value for array descriptor: Expecting %v. Received %v", fia, b)
 		return
 	}
-	const fia byte = 0x94 //four item array descriptor value
-	if bs[0] != fia {
-		err = fmt.Errorf("Unexpected value for array descriptor: Expecting %v. Received %v", fia, bs[0])
-		return
-	}
-	var b byte
+
 	if err = c.read(&b); err != nil {
 		return
 	}

+ 18 - 0
codec/rpc.go

@@ -7,6 +7,7 @@ import (
 	"bufio"
 	"io"
 	"net/rpc"
+	"sync"
 )
 
 // Rpc provides a rpc Server or Client Codec for rpc communication.
@@ -33,6 +34,8 @@ type rpcCodec struct {
 	enc *Encoder
 	bw  *bufio.Writer
 	br  *bufio.Reader
+	mu  sync.Mutex
+	cls bool
 }
 
 func newRPCCodec(conn io.ReadWriteCloser, h Handle) rpcCodec {
@@ -56,6 +59,9 @@ func (c *rpcCodec) BufferedWriter() *bufio.Writer {
 }
 
 func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2, doFlush bool) (err error) {
+	if c.cls {
+		return io.EOF
+	}
 	if err = c.enc.Encode(obj1); err != nil {
 		return
 	}
@@ -71,6 +77,9 @@ func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2, doFlush bool) (err e
 }
 
 func (c *rpcCodec) read(obj interface{}) (err error) {
+	if c.cls {
+		return io.EOF
+	}
 	//If nil is passed in, we should still attempt to read content to nowhere.
 	if obj == nil {
 		var obj2 interface{}
@@ -80,6 +89,10 @@ func (c *rpcCodec) read(obj interface{}) (err error) {
 }
 
 func (c *rpcCodec) Close() error {
+	if c.cls {
+		return io.EOF
+	}
+	c.cls = true
 	return c.rwc.Close()
 }
 
@@ -94,10 +107,15 @@ type goRpcCodec struct {
 }
 
 func (c *goRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {
+	// Must protect for concurrent access as per API
+	c.mu.Lock()
+	defer c.mu.Unlock()
 	return c.write(r, body, true, true)
 }
 
 func (c *goRpcCodec) WriteResponse(r *rpc.Response, body interface{}) error {
+	c.mu.Lock()
+	defer c.mu.Unlock()
 	return c.write(r, body, true, true)
 }
 

+ 451 - 0
codec/simple.go

@@ -0,0 +1,451 @@
+// Copyright (c) 2012, 2013 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a BSD-style license found in the LICENSE file.
+
+/*
+simple is a simplistic codec which does not pack integers, floats, etc into smaller chunks.
+
+All numbers are encoded with 8 bytes (int64, uint64, float64).
+
+Beyond this, it looks very similar to binc or msgpack.
+
+- nil, false, true are represented with single bytes
+- positive/negative ints, and floats are distinct types, all represented by 1,2,4 or 8 bytes
+- lengths of containers (strings, arrays, ext, map) occupy 2 bytes and
+  exist right after the bd
+- maps are encoded as [bd] [length] [[key][value]]...
+- arrays are encoded as [bd] [length] [value]...
+- extensions are encoded as [bd] [length] [tag] [byte]...
+- strings/bytearrays are encoded as [bd] [length] [tag] [byte]...
+
+*/
+
+package codec
+
+import "math"
+
+const (
+	_ byte = iota
+	simpleVdNil
+	simpleVdFalse
+	simpleVdTrue
+	simpleVdPosInt1
+	simpleVdPosInt2
+	simpleVdPosInt4
+	simpleVdPosInt8
+	simpleVdNegInt1
+	simpleVdNegInt2
+	simpleVdNegInt4
+	simpleVdNegInt8
+	simpleVdFloat32
+	simpleVdFloat64
+
+	simpleVdString
+	simpleVdByteArray
+	simpleVdArray
+	simpleVdMap
+
+	simpleVdExt
+)
+
+type simpleEncDriver struct {
+	w encWriter
+	b [8]byte
+}
+
+func (e *simpleEncDriver) isBuiltinType(rt uintptr) bool {
+	return false
+}
+
+func (e *simpleEncDriver) encodeBuiltin(rt uintptr, v interface{}) {
+}
+
+func (e *simpleEncDriver) encodeNil() {
+	e.w.writen1(simpleVdNil)
+	// e.w.writen1(0)
+}
+
+func (e *simpleEncDriver) encodeBool(b bool) {
+	if b {
+		e.w.writen1(simpleVdTrue)
+	} else {
+		e.w.writen1(simpleVdFalse)
+	}
+}
+
+func (e *simpleEncDriver) encodeFloat32(f float32) {
+	e.w.writen1(simpleVdFloat32)
+	e.w.writeUint32(math.Float32bits(f))
+}
+
+func (e *simpleEncDriver) encodeFloat64(f float64) {
+	e.w.writen1(simpleVdFloat64)
+	e.w.writeUint64(math.Float64bits(f))
+}
+
+func (e *simpleEncDriver) encodeInt(v int64) {
+	if v < 0 {
+		e.encUint(uint64(-v), false)
+	} else {
+		e.encUint(uint64(v), true)
+	}
+}
+
+func (e *simpleEncDriver) encodeUint(v uint64) {
+	e.encUint(v, true)
+}
+
+func (e *simpleEncDriver) encUint(v uint64, pos bool) {
+	switch {
+	case v <= math.MaxUint8:
+		if pos {
+			e.w.writen1(simpleVdPosInt1)
+		} else {
+			e.w.writen1(simpleVdNegInt1)
+		}
+		e.w.writen1(uint8(v))
+	case v <= math.MaxUint16:
+		if pos {
+			e.w.writen1(simpleVdPosInt2)
+		} else {
+			e.w.writen1(simpleVdNegInt2)
+		}
+		e.w.writeUint16(uint16(v))
+	case v <= math.MaxUint32:
+		if pos {
+			e.w.writen1(simpleVdPosInt4)
+		} else {
+			e.w.writen1(simpleVdNegInt4)
+		}
+		e.w.writeUint32(uint32(v))
+	case v <= math.MaxUint64:
+		if pos {
+			e.w.writen1(simpleVdPosInt8)
+		} else {
+			e.w.writen1(simpleVdNegInt8)
+		}
+		e.w.writeUint64(v)
+	}
+}
+
+func (e *simpleEncDriver) encodeExtPreamble(xtag byte, length int) {
+	e.w.writen1(simpleVdExt)
+	e.w.writeUint32(uint32(length))
+	e.w.writen1(xtag)
+}
+
+func (e *simpleEncDriver) encodeArrayPreamble(length int) {
+	e.w.writen1(simpleVdArray)
+	e.w.writeUint32(uint32(length))
+}
+
+func (e *simpleEncDriver) encodeMapPreamble(length int) {
+	e.w.writen1(simpleVdMap)
+	e.w.writeUint32(uint32(length))
+}
+
+func (e *simpleEncDriver) encodeString(c charEncoding, v string) {
+	e.w.writen1(simpleVdString)
+	e.w.writeUint32(uint32(len(v)))
+	e.w.writestr(v)
+}
+
+func (e *simpleEncDriver) encodeSymbol(v string) {
+	e.encodeString(c_UTF8, v)
+}
+
+func (e *simpleEncDriver) encodeStringBytes(c charEncoding, v []byte) {
+	e.w.writen1(simpleVdByteArray)
+	e.w.writeUint32(uint32(len(v)))
+	e.w.writeb(v)
+}
+
+//------------------------------------
+
+type simpleDecDriver struct {
+	r      decReader
+	bdRead bool
+	bdType valueType
+	bd     byte
+	b      [8]byte
+}
+
+func (d *simpleDecDriver) initReadNext() {
+	if d.bdRead {
+		return
+	}
+	d.bd = d.r.readn1()
+	d.bdRead = true
+	d.bdType = valueTypeUnset
+}
+
+func (d *simpleDecDriver) currentEncodedType() valueType {
+	if d.bdType == valueTypeUnset {
+		switch d.bd {
+		case simpleVdNil:
+			d.bdType = valueTypeNil
+		case simpleVdTrue, simpleVdFalse:
+			d.bdType = valueTypeBool
+		case simpleVdPosInt1, simpleVdPosInt2, simpleVdPosInt4, simpleVdPosInt8:
+			d.bdType = valueTypeUint
+		case simpleVdNegInt1, simpleVdNegInt2, simpleVdNegInt4, simpleVdNegInt8:
+			d.bdType = valueTypeInt
+		case simpleVdFloat32, simpleVdFloat64:
+			d.bdType = valueTypeFloat
+		case simpleVdString:
+			d.bdType = valueTypeString
+		case simpleVdByteArray:
+			d.bdType = valueTypeBytes
+		case simpleVdExt:
+			d.bdType = valueTypeExt
+		case simpleVdArray:
+			d.bdType = valueTypeArray
+		case simpleVdMap:
+			d.bdType = valueTypeMap
+		default:
+			decErr("currentEncodedType: Unrecognized d.vd: 0x%x", d.bd)
+		}
+	}
+	return d.bdType
+}
+
+func (d *simpleDecDriver) tryDecodeAsNil() bool {
+	if d.bd == simpleVdNil {
+		d.bdRead = false
+		return true
+	}
+	return false
+}
+
+func (d *simpleDecDriver) isBuiltinType(rt uintptr) bool {
+	return false
+}
+
+func (d *simpleDecDriver) decodeBuiltin(rt uintptr, v interface{}) {
+}
+
+func (d *simpleDecDriver) decIntAny() (ui uint64, i int64, neg bool) {
+	switch d.bd {
+	case simpleVdPosInt1:
+		ui = uint64(d.r.readn1())
+	case simpleVdPosInt2:
+		ui = uint64(d.r.readUint16())
+	case simpleVdPosInt4:
+		ui = uint64(d.r.readUint32())
+	case simpleVdPosInt8:
+		ui = uint64(d.r.readUint64())
+	case simpleVdNegInt1:
+		ui = uint64(d.r.readn1())
+		neg = true
+	case simpleVdNegInt2:
+		ui = uint64(d.r.readUint16())
+		neg = true
+	case simpleVdNegInt4:
+		ui = uint64(d.r.readUint32())
+		neg = true
+	case simpleVdNegInt8:
+		ui = uint64(d.r.readUint64())
+		neg = true
+	default:
+		decErr("Integer only valid from pos/neg integer1..8. Invalid descriptor: %v", d.bd)
+	}
+	if neg {
+		i = -(int64(ui))
+	} else {
+		i = int64(ui)
+	}
+	return
+}
+
+func (d *simpleDecDriver) decodeInt(bitsize uint8) (i int64) {
+	_, i, _ = d.decIntAny()
+	checkOverflow(0, i, bitsize)
+	d.bdRead = false
+	return
+}
+
+func (d *simpleDecDriver) decodeUint(bitsize uint8) (ui uint64) {
+	ui, i, neg := d.decIntAny()
+	if neg {
+		decErr("Assigning negative signed value: %v, to unsigned type", i)
+	}
+	checkOverflow(ui, 0, bitsize)
+	d.bdRead = false
+	return
+}
+
+func (d *simpleDecDriver) decodeFloat(chkOverflow32 bool) (f float64) {
+	switch d.bd {
+	case simpleVdFloat32:
+		f = float64(math.Float32frombits(d.r.readUint32()))
+	case simpleVdFloat64:
+		f = math.Float64frombits(d.r.readUint64())
+	default:
+		if d.bd >= simpleVdPosInt1 && d.bd <= simpleVdNegInt8 {
+			_, i, _ := d.decIntAny()
+			f = float64(i)
+		} else {
+			decErr("Float only valid from float32/64: Invalid descriptor: %v", d.bd)
+		}
+	}
+	// 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)
+		}
+	}
+	return
+}
+
+// bool can be decoded from bool only (single byte).
+func (d *simpleDecDriver) decodeBool() (b bool) {
+	b = d.r.readn1() != 0
+	d.bdRead = false
+	return
+}
+
+func (d *simpleDecDriver) readMapLen() (length int) {
+	return d.decLen()
+}
+
+func (d *simpleDecDriver) readArrayLen() (length int) {
+	return d.decLen()
+}
+
+func (d *simpleDecDriver) decLen() int {
+	d.r.readb(d.b[:4])
+	return int(bigen.Uint32(d.b[:4]))
+}
+
+func (d *simpleDecDriver) decodeString() (s string) {
+	s = string(d.r.readn(d.decLen()))
+	d.bdRead = false
+	return
+}
+
+func (d *simpleDecDriver) decodeBytes(bs []byte) (bsOut []byte, changed bool) {
+	if clen := d.decLen(); clen > 0 {
+		// if no contents in stream, don't update the passed byteslice
+		if len(bs) != clen {
+			if len(bs) > clen {
+				bs = bs[:clen]
+			} else {
+				bs = make([]byte, clen)
+			}
+			bsOut = bs
+			changed = true
+		}
+		d.r.readb(bs)
+	}
+	d.bdRead = false
+	return
+}
+
+func (d *simpleDecDriver) decodeExt(verifyTag bool, tag byte) (xtag byte, xbs []byte) {
+	switch d.bd {
+	case simpleVdExt:
+		l := d.decLen()
+		xtag = d.r.readn1()
+		if verifyTag && xtag != tag {
+			decErr("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
+		}
+		xbs = d.r.readn(l)
+	case simpleVdByteArray:
+		xbs, _ = d.decodeBytes(nil)
+	default:
+		decErr("Invalid d.vd for extensions (Expecting extensions or byte array). Got: 0x%x", d.bd)
+	}
+	d.bdRead = false
+	return
+}
+
+func (d *simpleDecDriver) decodeNaked() (v interface{}, vt valueType, decodeFurther bool) {
+	d.initReadNext()
+
+	switch d.bd {
+	case simpleVdNil:
+		vt = valueTypeNil
+	case simpleVdFalse:
+		vt = valueTypeBool
+		v = false
+	case simpleVdTrue:
+		vt = valueTypeBool
+		v = true
+	case simpleVdPosInt1, simpleVdPosInt2, simpleVdPosInt4, simpleVdPosInt8:
+		vt = valueTypeUint
+		ui, _, _ := d.decIntAny()
+		v = ui
+	case simpleVdNegInt1, simpleVdNegInt2, simpleVdNegInt4, simpleVdNegInt8:
+		vt = valueTypeInt
+		_, i, _ := d.decIntAny()
+		v = i
+	case simpleVdFloat32:
+		vt = valueTypeFloat
+		v = d.decodeFloat(true)
+	case simpleVdFloat64:
+		vt = valueTypeFloat
+		v = d.decodeFloat(false)
+	case simpleVdString:
+		vt = valueTypeString
+		v = d.decodeString()
+	case simpleVdByteArray:
+		vt = valueTypeBytes
+		v, _ = d.decodeBytes(nil)
+	case simpleVdExt:
+		vt = valueTypeExt
+		l := d.decLen()
+		var re RawExt
+		re.Tag = d.r.readn1()
+		re.Data = d.r.readn(l)
+		v = &re
+		vt = valueTypeExt
+	case simpleVdArray:
+		vt = valueTypeArray
+		decodeFurther = true
+	case simpleVdMap:
+		vt = valueTypeMap
+		decodeFurther = true
+	default:
+		decErr("decodeNaked: Unrecognized d.vd: 0x%x", d.bd)
+	}
+
+	if !decodeFurther {
+		d.bdRead = false
+	}
+	return
+}
+
+//------------------------------------
+
+//SimpleHandle is a Handle for a very simple encoding format.
+//
+//The simple format is similar to binc:
+//  - All numbers are represented with 8 bytes (int64, uint64, float64)
+//  - Strings, []byte, arrays and maps have the length pre-pended as a uint64
+//  - Thus, there isn't much packing, but the format is extremely simple.
+//    and easy to create different implementations of (e.g. in C).
+type SimpleHandle struct {
+	BasicHandle
+}
+
+func (h *SimpleHandle) newEncDriver(w encWriter) encDriver {
+	return &simpleEncDriver{w: w}
+}
+
+func (h *SimpleHandle) newDecDriver(r decReader) decDriver {
+	return &simpleDecDriver{r: r}
+}
+
+func (_ *SimpleHandle) writeExt() bool {
+	return true
+}
+
+func (h *SimpleHandle) getBasicHandle() *BasicHandle {
+	return &h.BasicHandle
+}
+
+var _ decDriver = (*simpleDecDriver)(nil)
+var _ encDriver = (*simpleEncDriver)(nil)

+ 2 - 2
codec/time.go

@@ -76,7 +76,7 @@ func encodeTime(t time.Time) []byte {
 	if tsecs != 0 {
 		bd = bd | 0x80
 		bigen.PutUint64(btmp[:], uint64(tsecs))
-		f := pruneSignExt(btmp[:])
+		f := pruneSignExt(btmp[:], tsecs >= 0)
 		bd = bd | (byte(7-f) << 2)
 		copy(bs[i:], btmp[f:])
 		i = i + (8 - f)
@@ -84,7 +84,7 @@ func encodeTime(t time.Time) []byte {
 	if tnsecs != 0 {
 		bd = bd | 0x40
 		bigen.PutUint32(btmp[:4], uint32(tnsecs))
-		f := pruneSignExt(btmp[:4])
+		f := pruneSignExt(btmp[:4], true)
 		bd = bd | byte(3-f)
 		copy(bs[i:], btmp[f:4])
 		i = i + (4 - f)