Browse Source

codec/binc: support symbols, compact integers, compact floats.

Also, harden decodeNaked so no-schema decoding of integers/floats are
int64, uint64 and float64.
Ugorji Nwoke 12 years ago
parent
commit
f1877ccca0
8 changed files with 613 additions and 274 deletions
  1. 22 21
      codec/0doc.go
  2. 26 25
      codec/README.md
  3. 288 97
      codec/binc.go
  4. 174 55
      codec/codecs_test.go
  5. 34 34
      codec/decode.go
  6. 43 20
      codec/encode.go
  7. 1 1
      codec/helper.go
  8. 25 21
      codec/msgpack.go

+ 22 - 21
codec/0doc.go

@@ -7,7 +7,7 @@ High Performance, Feature-Rich Idiomatic Go encoding library for msgpack and bin
 Supported Serialization formats are:
 
   - msgpack: [http://wiki.msgpack.org/display/MSGPACK/Format+specification]
-  - binc: [http://www.ugorji.net/project/binc]
+  - binc: [http://github.com/ugorji/binc]
 
 To install:
 
@@ -135,28 +135,29 @@ A sample run of benchmark using "go test -bi -bench=.":
     ..............................................
     Benchmark: 
     	Struct recursive Depth:             1
-    	ApproxDeepSize Of benchmark Struct: 4758
+    	ApproxDeepSize Of benchmark Struct: 4786
     Benchmark One-Pass Run:
-    	   msgpack: len: 1504
-    	      binc: len: 1508
-    	       gob: len: 1908
-    	      json: len: 2402
-    	 v-msgpack: len: 1536
-    	      bson: len: 3009
+    	   msgpack: len: 1564
+    	      binc: len: 1191
+    	       gob: len: 1972
+    	      json: len: 2538
+    	 v-msgpack: len: 1600
+    	      bson: len: 3025
     ..............................................
-    Benchmark__Msgpack__Encode	   50000	     60824 ns/op
-    Benchmark__Msgpack__Decode	   10000	    115119 ns/op
-    Benchmark__Binc_____Encode	   50000	     55140 ns/op
-    Benchmark__Binc_____Decode	   10000	    112132 ns/op
-    Benchmark__Gob______Encode	   10000	    143350 ns/op
-    Benchmark__Gob______Decode	    5000	    434248 ns/op
-    Benchmark__Json_____Encode	   10000	    157298 ns/op
-    Benchmark__Json_____Decode	    5000	    303729 ns/op
-    Benchmark__Bson_____Encode	   10000	    174250 ns/op
-    Benchmark__Bson_____Decode	   10000	    223602 ns/op
-    Benchmark__VMsgpack_Encode	   20000	     80438 ns/op
-    Benchmark__VMsgpack_Decode	   10000	    157330 ns/op
-    
+    PASS
+    Benchmark__Msgpack__Encode	   50000	     61731 ns/op
+    Benchmark__Msgpack__Decode	   10000	    115947 ns/op
+    Benchmark__Binc_____Encode	   50000	     64568 ns/op
+    Benchmark__Binc_____Decode	   10000	    113843 ns/op
+    Benchmark__Gob______Encode	   10000	    143956 ns/op
+    Benchmark__Gob______Decode	    5000	    431889 ns/op
+    Benchmark__Json_____Encode	   10000	    158662 ns/op
+    Benchmark__Json_____Decode	    5000	    310744 ns/op
+    Benchmark__Bson_____Encode	   10000	    172905 ns/op
+    Benchmark__Bson_____Decode	   10000	    228564 ns/op
+    Benchmark__VMsgpack_Encode	   20000	     81752 ns/op
+    Benchmark__VMsgpack_Decode	   10000	    160050 ns/op
+
 To run full benchmark suite (including against vmsgpack and bson), 
 see notes in ext_dep_test.go
 

+ 26 - 25
codec/README.md

@@ -6,7 +6,7 @@ encode/decode support for different serialization formats.
 Supported Serialization formats are:
 
   - msgpack: [http://wiki.msgpack.org/display/MSGPACK/Format+specification]
-  - binc: [http://www.ugorji.net/project/binc]
+  - binc: [http://github.com/ugorji/binc]
 
 To install:
 
@@ -51,7 +51,7 @@ Rich Feature Set includes:
       - RPC Server/Client Codec for msgpack-rpc protocol defined at: 
         http://wiki.msgpack.org/display/MSGPACK/RPC+specification
 
-## Extension Support
+Extension Support
 
 Users can register a function to handle the encoding or decoding of
 their custom types. 
@@ -76,7 +76,7 @@ an inter-operable format. For msgpack, these are Binary and
 time.Time. Library users will have to explicitly configure these as seen
 in the usage below.
 
-## Usage
+Usage
 
 Typical usage model:
 
@@ -129,35 +129,36 @@ Typical usage model:
     rpcCodec := rpcH.ClientCodec(conn, h)  
     client := rpc.NewClientWithCodec(rpcCodec)
 
-## Representative Benchmark Results
+Representative Benchmark Results
 
 A sample run of benchmark using "go test -bi -bench=.":
 
     ..............................................
     Benchmark: 
     	Struct recursive Depth:             1
-    	ApproxDeepSize Of benchmark Struct: 4758
+    	ApproxDeepSize Of benchmark Struct: 4786
     Benchmark One-Pass Run:
-    	   msgpack: len: 1504
-    	      binc: len: 1508
-    	       gob: len: 1908
-    	      json: len: 2402
-    	 v-msgpack: len: 1536
-    	      bson: len: 3009
+    	   msgpack: len: 1564
+    	      binc: len: 1191
+    	       gob: len: 1972
+    	      json: len: 2538
+    	 v-msgpack: len: 1600
+    	      bson: len: 3025
     ..............................................
-    Benchmark__Msgpack__Encode	   50000	     60824 ns/op
-    Benchmark__Msgpack__Decode	   10000	    115119 ns/op
-    Benchmark__Binc_____Encode	   50000	     55140 ns/op
-    Benchmark__Binc_____Decode	   10000	    112132 ns/op
-    Benchmark__Gob______Encode	   10000	    143350 ns/op
-    Benchmark__Gob______Decode	    5000	    434248 ns/op
-    Benchmark__Json_____Encode	   10000	    157298 ns/op
-    Benchmark__Json_____Decode	    5000	    303729 ns/op
-    Benchmark__Bson_____Encode	   10000	    174250 ns/op
-    Benchmark__Bson_____Decode	   10000	    223602 ns/op
-    Benchmark__VMsgpack_Encode	   20000	     80438 ns/op
-    Benchmark__VMsgpack_Decode	   10000	    157330 ns/op
-    
+    PASS
+    Benchmark__Msgpack__Encode	   50000	     61731 ns/op
+    Benchmark__Msgpack__Decode	   10000	    115947 ns/op
+    Benchmark__Binc_____Encode	   50000	     64568 ns/op
+    Benchmark__Binc_____Decode	   10000	    113843 ns/op
+    Benchmark__Gob______Encode	   10000	    143956 ns/op
+    Benchmark__Gob______Decode	    5000	    431889 ns/op
+    Benchmark__Json_____Encode	   10000	    158662 ns/op
+    Benchmark__Json_____Decode	    5000	    310744 ns/op
+    Benchmark__Bson_____Encode	   10000	    172905 ns/op
+    Benchmark__Bson_____Decode	   10000	    228564 ns/op
+    Benchmark__VMsgpack_Encode	   20000	     81752 ns/op
+    Benchmark__VMsgpack_Decode	   10000	    160050 ns/op
+
 To run full benchmark suite (including against vmsgpack and bson), 
-see notes in ext\_dep_test.go
+see notes in ext\_dep\_test.go
 

+ 288 - 97
codec/binc.go

@@ -7,13 +7,17 @@ import (
 	"math"
 	"reflect"
 	"time"
+	"sync/atomic"
+	//"fmt"
 )
 
+//var _ = fmt.Printf
+
 //BincHandle is a Handle for the Binc Schema-Free Encoding Format
 //defined at http://www.ugorji.net/project/binc .
 //
 //BincHandle currently supports all Binc features with the following EXCEPTIONS:
-//  - only integers up to 64 bits of precision (degree of precision <= 3) are supported.
+//  - only integers up to 64 bits of precision are supported.
 //    big integers are unsupported.
 //  - Only IEEE 754 binary32 and binary64 floats are supported (ie Go float32 and float64 types).
 //    extended precision and decimal IEEE 754 floats are unsupported.
@@ -40,9 +44,11 @@ const (
 	bincVdTimestamp
 	bincVdSmallInt
 	bincVdUnicodeOther
-
-	// 4 open slots left ...
-
+	bincVdSymbol
+	
+	bincVdDecimal
+	_ // open slot
+	_ // open slot
 	bincVdCustomExt = 0x0f
 )
 
@@ -53,6 +59,7 @@ const (
 	bincSpNan
 	bincSpPosInf
 	bincSpNegInf
+	bincSpZeroFloat
 	bincSpZero
     bincSpNegOne
 )
@@ -62,11 +69,15 @@ const (
 	bincFlBin32
 	_ // bincFlBin32e
 	bincFlBin64
+	_ // bincFlBin64e
 	// others not currently supported
 )
 
 type bincEncoder struct { 
 	w encWriter
+	m map[string]uint16 // symbols
+	s uint32 // symbols sequencer
+	b [8]byte
 }
 
 type bincDecoder struct {
@@ -75,6 +86,8 @@ type bincDecoder struct {
 	bd byte 
 	vd byte
 	vs byte 
+	b [8]byte
+	m map[uint16]string // symbols (TODO: maybe use uint32 as key, as map optimizes for it)
 }
 
 func (_ *BincHandle) newEncoder(w encWriter) encoder {
@@ -113,16 +126,56 @@ func (e *bincEncoder) encodeBool(b bool) {
 }
 
 func (e *bincEncoder) encodeFloat32(f float32) { 
+	if f == 0 {
+		e.w.writen1(bincVdSpecial << 4 | bincSpZeroFloat)
+		return
+	}
 	e.w.writen1(bincVdFloat << 4 | bincFlBin32)
 	e.w.writeUint32(math.Float32bits(f))
 }
 
 func (e *bincEncoder) encodeFloat64(f float64) { 
-	e.w.writen1(bincVdFloat << 4 | bincFlBin64)
-	e.w.writeUint64(math.Float64bits(f))
+	//if true { e.w.writen1(bincVdFloat << 4 | bincFlBin64); e.w.writeUint64(math.Float64bits(f)); return; }
+	if f == 0 {
+		e.w.writen1(bincVdSpecial << 4 | bincSpZeroFloat)
+		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])
+	}
+}
+
+func (e *bincEncoder) encInteger4(bd byte, trim byte, v uint32) {
+	const lim int = 4
+	bigen.PutUint32(e.b[:lim], v)
+	var i int 
+	for ; i < (lim-1) && (e.b[i] == trim); i++ { }
+	e.w.writen1(bd | byte(lim-i-1))
+	e.w.writeb(e.b[i:lim])
+}
+
+func (e *bincEncoder) encInteger8(bd byte, trim byte, v uint64) {
+	const lim int = 8
+	bigen.PutUint64(e.b[:lim], v)
+	var i int 
+	for ; i < (lim-1) && (e.b[i] == trim); i++ { }
+	e.w.writen1(bd | byte(lim-i-1))
+	e.w.writeb(e.b[i:lim])
+	// e.w.writen1(bincVdInt << 4 | 0x03)
+	// e.w.writeUint64(uint64(v))
 }
 
 func (e *bincEncoder) encodeInt(v int64) { 
+	const bd byte = bincVdInt << 4
 	switch {
 	case v == 0:
 		e.w.writen1(bincVdSpecial << 4 | bincSpZero)
@@ -131,21 +184,38 @@ func (e *bincEncoder) encodeInt(v int64) {
 	case v >= 1 && v <= 16:
 		e.w.writen1(bincVdSmallInt << 4 | byte(v-1))
 	case v >= math.MinInt8 && v <= math.MaxInt8:
-		e.w.writen2(bincVdInt << 4, byte(v))
+		e.w.writen2(bd | 0x0, byte(v))
 	case v >= math.MinInt16 && v <= math.MaxInt16:
-		e.w.writen1(bincVdInt << 4 | 0x01)
+		e.w.writen1(bd | 0x1)
 		e.w.writeUint16(uint16(v))
 	case v >= math.MinInt32 && v <= math.MaxInt32:
-		e.w.writen1(bincVdInt << 4 | 0x02)
-		e.w.writeUint32(uint32(v))
+		if v < 0 {
+			e.encInteger4(bd, 0xff, uint32(v))
+		} else {
+			e.encInteger4(bd, 0, uint32(v))
+		} 	
 	default:
-		e.w.writen1(bincVdInt << 4 | 0x03)
-		e.w.writeUint64(uint64(v))
+		if v < 0 {
+			e.encInteger8(bd, 0xff, uint64(v))
+		} else {
+			e.encInteger8(bd, 0, uint64(v))
+		}
 	}
 }
 
 func (e *bincEncoder) encodeUint(v uint64) { 
-	e.encNumber(bincVdUint << 4, v)
+	const bd byte = bincVdUint << 4
+	switch {
+	case v <= math.MaxUint8:
+		e.w.writen2(bd | 0x0, byte(v))
+	case v <= math.MaxUint16:
+		e.w.writen1(bd | 0x01)
+		e.w.writeUint16(uint16(v))
+	case v <= math.MaxUint32:
+		e.encInteger4(bd, 0, uint32(v))
+	default:
+		e.encInteger8(bd, 0, v)
+	}
 }
 
 func (e *bincEncoder) encodeExtPreamble(xtag byte, length int)  {
@@ -169,6 +239,68 @@ func (e *bincEncoder) encodeString(c charEncoding, v string) {
 	}	
 }
 
+func (e *bincEncoder) encodeSymbol(v string) { 
+	//if true { e.encodeString(c_UTF8, v); return; }
+	
+	//symbols only offer benefit when string length > 1.
+	//This is because strings with length 1 take only 2 bytes to store 
+	//(bd with embedded length, and single byte for string val).
+	
+	l := len(v)
+	switch l {
+	case 0:
+		e.encBytesLen(c_UTF8, 0)
+		return
+	case 1:
+		e.encBytesLen(c_UTF8, 1)
+		e.w.writen1(v[0])
+		return
+	}
+	if e.m == nil {
+		e.m = make(map[string]uint16, 16)
+	}
+	ui, ok := e.m[v]
+	if ok {
+		if ui <= math.MaxUint8 {
+			e.w.writen2(bincVdSymbol << 4, byte(ui))
+		} else {
+			e.w.writen1(bincVdSymbol << 4 | 0x8)
+			e.w.writeUint16(ui)
+		}
+	} else {
+		ui = uint16(atomic.AddUint32(&e.s, 1))
+		e.m[v] = ui
+		var lenprec uint8
+		switch {
+		case l <= math.MaxUint8:
+			// lenprec = 0
+		case l <= math.MaxUint16:
+			lenprec = 1
+		case l <= math.MaxUint32:
+			lenprec = 2
+		default:
+			lenprec = 3
+		}
+		if ui <= math.MaxUint8 {
+			e.w.writen2(bincVdSymbol << 4 | 0x0 | 0x4 | lenprec, byte(ui))
+		} else {
+			e.w.writen1(bincVdSymbol << 4 | 0x8 | 0x4 | lenprec)
+			e.w.writeUint16(ui)
+		}
+		switch lenprec {
+		case 0:
+			e.w.writen1(byte(l))
+		case 1:
+			e.w.writeUint16(uint16(l))
+		case 2:
+			e.w.writeUint32(uint32(l))
+		default:
+			e.w.writeUint64(uint64(l))
+		}
+		e.w.writestr(v)
+	}	
+}
+
 func (e *bincEncoder) encodeStringBytes(c charEncoding, v []byte) { 
 	l := uint64(len(v))
 	e.encBytesLen(c, l)
@@ -190,11 +322,11 @@ func (e *bincEncoder) encLen(bd byte, l uint64) {
 	if l < 12 {
 		e.w.writen1(bd | uint8(l + 4))
 	} else {
-		e.encNumber(bd, l)
+		e.encLenNumber(bd, l)
 	}
 }
 	
-func (e *bincEncoder) encNumber(bd byte, v uint64) {
+func (e *bincEncoder) encLenNumber(bd byte, v uint64) {
 	switch {
 	case v <= math.MaxUint8:
 		e.w.writen2(bd, byte(v))
@@ -218,7 +350,7 @@ func (d *bincDecoder) initReadNext() {
 	if d.bdRead {
 		return
 	}
-	d.bd = d.r.readUint8()
+	d.bd = d.r.readn1()
 	d.vd = d.bd >> 4
 	d.vs = d.bd & 0x0f
 	d.bdRead = true
@@ -249,92 +381,104 @@ func (d *bincDecoder) decodeBuiltinType(rt reflect.Type, rv reflect.Value) bool
 	return false
 }
 
-func (d *bincDecoder) decFloat() (f float64) {
-	switch d.vs {
-	case bincFlBin32:
-		f = float64(math.Float32frombits(d.r.readUint32()))
-	case bincFlBin64:
-		f = math.Float64frombits(d.r.readUint64())
-	default:
-		decErr("only float32 and float64 are supported")
+func (d *bincDecoder) decFloatPre(vs, defaultLen byte) {
+	if vs & 0x8 == 0 {
+		d.r.readb(d.b[0:defaultLen])
+	} else {
+		l := d.r.readn1()
+		if l > 8 {
+			decErr("At most 8 bytes used to represent float. Received: %v bytes", l)
+		}
+		for i := l; i < 8; i++ {
+			d.b[i] = 0
+		}
+		d.r.readb(d.b[0:l])
 	}
-	return
 }
 
-func (d *bincDecoder) decFloatI() (f interface{}) {
-	switch d.vs {
+func (d *bincDecoder) decFloat() (f float64) {
+	//if true { f = math.Float64frombits(d.r.readUint64()); break; }
+	switch vs := d.vs; (vs & 0x7) {
 	case bincFlBin32:
-		f = math.Float32frombits(d.r.readUint32())
+		d.decFloatPre(vs, 4)
+		f = float64(math.Float32frombits(bigen.Uint32(d.b[0:4])))
 	case bincFlBin64:
-		f = math.Float64frombits(d.r.readUint64())
+		d.decFloatPre(vs, 8)
+		f = math.Float64frombits(bigen.Uint64(d.b[:]))
 	default:
-		decErr("only float32 and float64 are supported")
+		decErr("only float32 and float64 are supported. d.vd: 0x%x, d.vs: 0x%x", d.vd, d.vs)
 	}
 	return
 }
 
 func (d *bincDecoder) decInt() (v int64) {
+	// need to inline the code (interface conversion and type assertion expensive)
 	switch d.vs {
 	case 0:
-		v = int64(int8(d.r.readUint8()))
-	case 1:
-		v = int64(int16(d.r.readUint16()))
-	case 2:
-		v = int64(int32(d.r.readUint32()))
-	case 3:
-		v = int64(d.r.readUint64())
-	default:
-		decErr("integers with greater than 64 bits of precision not supported")
-	}
-	return
-}
-
-func (d *bincDecoder) decIntI() (v interface{}) {
-	switch d.vs {
-	case 0:
-		v = int8(d.r.readUint8())
+		v = int64(int8(d.r.readn1()))
 	case 1:
-		v = int16(d.r.readUint16())
+		d.r.readb(d.b[6:])
+		v = int64(int16(bigen.Uint16(d.b[6:])))
 	case 2:
-		v = int32(d.r.readUint32())
+		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:
-		v = int64(d.r.readUint64())
+		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
+	return 
 }
 
 func (d *bincDecoder) decUint() (v uint64) {
+	// need to inline the code (interface conversion and type assertion expensive)
 	switch d.vs {
 	case 0:
-		v = uint64(d.r.readUint8())
-	case 1:
-		v = uint64(d.r.readUint16())
-	case 2:
-		v = uint64(d.r.readUint32())
-	case 3:
-		v = uint64(d.r.readUint64())
-	default:
-		decErr("integers with greater than 64 bits of precision not supported")
-	}
-	return
-}
-
-func (d *bincDecoder) decUintI() (v interface{}) {
-	switch d.vs {
-	case 0:
-		v = d.r.readUint8()
+		v = uint64(d.r.readn1())
 	case 1:
-		v = d.r.readUint16()
+		d.r.readb(d.b[6:])
+		v = uint64(bigen.Uint16(d.b[6:]))
 	case 2:
-		v = d.r.readUint32()
+		d.b[4] = 0
+		d.r.readb(d.b[5:])
+		v = uint64(bigen.Uint32(d.b[4:]))
 	case 3:
-		v = d.r.readUint64()
+		d.r.readb(d.b[4:])
+		v = uint64(bigen.Uint32(d.b[4:]))
+	case 4,5,6:
+		lim := int(7-d.vs)
+		d.r.readb(d.b[lim:])
+		for i := 0; i < lim; i++ {
+			d.b[i] = 0
+		}
+		v = uint64(bigen.Uint64(d.b[:]))
+	case 7:
+		d.r.readb(d.b[:])
+		v = uint64(bigen.Uint64(d.b[:]))
 	default:
-		decErr("integers with greater than 64 bits of precision not supported")
+		decErr("unsigned integers with greater than 64 bits of precision not supported")
 	}
-	return
+	return 
 }
 
 func (d *bincDecoder) decIntAny() (i int64) {
@@ -403,6 +547,8 @@ func (d *bincDecoder) decodeFloat(chkOverflow32 bool) (f float64) {
 			return math.NaN()
 		case bincSpPosInf:
 			return math.Inf(1)
+		case bincSpZeroFloat:
+			return 
 		case bincSpNegInf:
 			return math.Inf(-1)
 		}
@@ -470,25 +616,67 @@ func (d *bincDecoder) decLen() int {
 	return int(d.vs - 4)
 }
 
-func (d *bincDecoder) decBytesLen() int {
-	//decode string from either bytearray or string
-	if d.vd != bincVdString && d.vd != bincVdByteArray {
-		decErr("Invalid d.vd for string. Expecting string: 0x%x or bytearray: 0x%x. Got: 0x%x", 
-			bincVdString, bincVdByteArray, d.vd)
-	}
-	return d.decLen()
-}
-
-func (d *bincDecoder) decodeString() (s string)  {
-	if length := d.decBytesLen(); length > 0 {
-		s = string(d.r.readn(length))
+func (d *bincDecoder) decodeString() (s string) {
+	switch d.vd {
+	case bincVdString, bincVdByteArray:
+		if length := d.decLen(); length > 0 {
+			s = string(d.r.readn(length))
+		}
+	case bincVdSymbol:
+		//from vs: extract numSymbolBytes, containsStringVal, strLenPrecision, 
+		//extract symbol
+		//if containsStringVal, read it and put in map
+		//else look in map for string value
+		var symbol uint16
+		vs := d.vs
+		//fmt.Printf(">>>> d.vs: 0b%b, & 0x8: %v, & 0x4: %v\n", d.vs, vs & 0x8, vs & 0x4)
+		if vs & 0x8 == 0 {
+			symbol = uint16(d.r.readn1())
+		} else {
+			symbol = d.r.readUint16()
+		}
+		//println(">>>> symbol: ", symbol)
+		if d.m == nil {
+			d.m = make(map[uint16]string, 16)
+		}
+		
+		if vs & 0x4 == 0 {
+			s = d.m[symbol]
+		} else {
+			//println(">>>> will store symbol")
+			var slen int
+			switch vs & 0x3 {
+			case 0:
+				slen = int(d.r.readn1())
+			case 1:
+				slen = int(d.r.readUint16())
+			case 2:
+				slen = int(d.r.readUint32())
+			case 3:
+				slen = int(d.r.readUint64())
+			}
+			//println(">>>> symbol len: ", slen)
+			s = string(d.r.readn(slen))
+			//println(">>>> storing symbol: ", symbol, ", string value: ", s)
+			d.m[symbol] = s
+		}
+	default:
+		decErr("Invalid d.vd for string. Expecting string:0x%x, bytearray:0x%x or symbol: 0x%x. Got: 0x%x", 
+			bincVdString, bincVdByteArray, bincVdSymbol, d.vd)
 	}
 	d.bdRead = false
 	return
 }
 
-func (d *bincDecoder) decodeStringBytes(bs []byte) (bsOut []byte, changed bool) {
-	clen := d.decBytesLen()
+func (d *bincDecoder) decodeBytes(bs []byte) (bsOut []byte, changed bool) {
+	var clen int 
+	switch d.vd {
+	case bincVdString, bincVdByteArray:
+		clen = d.decLen()
+	default:
+		decErr("Invalid d.vd for bytes. Expecting string:0x%x or bytearray:0x%x. Got: 0x%x", 
+			bincVdString, bincVdByteArray, d.vd)
+	}
 	if clen > 0 {
 		// if no contents in stream, don't update the passed byteslice	
 		if len(bs) != clen {
@@ -510,12 +698,12 @@ func (d *bincDecoder) decodeExt(tag byte) (xbs []byte) {
 	switch d.vd {
 	case bincVdCustomExt:
 		l := d.decLen()
-		if xtag := d.r.readUint8(); xtag != tag {
+		if xtag := d.r.readn1(); xtag != tag {
 			decErr("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
 		}
 		xbs = d.r.readn(l)
 	case bincVdByteArray:
-		xbs, _ = d.decodeStringBytes(nil)
+		xbs, _ = d.decodeBytes(nil)
 	default:
 		decErr("Invalid d.vd for extensions (Expecting extensions or byte array). Got: 0x%x", d.vd)
 	}
@@ -543,6 +731,8 @@ func (d *bincDecoder) decodeNaked(h decodeHandleI) (rv reflect.Value, ctx decode
 			v = math.Inf(1)
 		case bincSpNegInf:
 			v = math.Inf(-1)
+		case bincSpZeroFloat:
+			v = float64(0)
 		case bincSpZero:
 			v = int8(0)
 		case bincSpNegOne:
@@ -550,18 +740,20 @@ func (d *bincDecoder) decodeNaked(h decodeHandleI) (rv reflect.Value, ctx decode
 		default:
 			decErr("Unrecognized special value 0x%x", d.vs)
 		}
-	case bincVdUint:
-		v = d.decUintI()
-	case bincVdInt:
-		v = d.decIntI()
 	case bincVdSmallInt:
 		v = int8(d.vs) + 1
+	case bincVdUint:
+		v = d.decUint()
+	case bincVdInt:
+		v = d.decInt()
 	case bincVdFloat:
-		v = d.decFloatI()
+		v = d.decFloat()
+	case bincVdSymbol:
+		v = d.decodeString()
 	case bincVdString:
 		v = d.decodeString()
 	case bincVdByteArray:
-		v, _ = d.decodeStringBytes(nil)
+		v, _ = d.decodeBytes(nil)
 	case bincVdTimestamp:
 		tt, err := decodeTime(d.r.readn(int(d.vs)))
 		if err != nil {
@@ -571,7 +763,7 @@ func (d *bincDecoder) decodeNaked(h decodeHandleI) (rv reflect.Value, ctx decode
 	case bincVdCustomExt:
 		//ctx = dncExt
 		l := d.decLen()
-		xtag := d.r.readUint8()
+		xtag := d.r.readn1()
 		opts := h.(*BincHandle)
 		rt, bfn := opts.getDecodeExtForTag(xtag)
 		if rt == nil {
@@ -614,4 +806,3 @@ func (d *bincDecoder) decodeNaked(h decodeHandleI) (rv reflect.Value, ctx decode
 	return
 }
 
-

+ 174 - 55
codec/codecs_test.go

@@ -34,9 +34,17 @@ import (
 	"net"
 	"fmt"
 	"flag"
+	"math"
 	"encoding/gob"
 )
 
+type testVerifyArg int 
+const (
+	testVerifyMapTypeSame testVerifyArg = iota
+	testVerifyMapTypeStrIntf
+	testVerifyMapTypeIntfIntf
+)
+
 var (
 	testInitDebug bool
 	testUseIoEncDec bool
@@ -125,6 +133,88 @@ func (r *TestRpcInt) Update(n int, res *int) error { r.i = n; *res = r.i; return
 func (r *TestRpcInt) Square(ignore int, res *int) error { *res = r.i * r.i; return nil }
 func (r *TestRpcInt) Mult(n int, res *int) error { *res = r.i * n; return nil }
 
+func testVerifyVal(v interface{}, arg testVerifyArg) (v2 interface{}) {
+	switch iv := v.(type) {
+	case int8:
+		v2 = int64(iv)
+	case int16:
+		v2 = int64(iv)
+	case int32:
+		v2 = int64(iv)
+	case int64:
+		v2 = int64(iv)
+	case uint8:
+		v2 = uint64(iv)
+	case uint16:
+		v2 = uint64(iv)
+	case uint32:
+		v2 = uint64(iv)
+	case uint64:
+		v2 = uint64(iv)
+	case float32:
+		v2 = float64(iv)
+	case float64:
+		v2 = float64(iv)
+	case []interface{}:
+		m2 := make([]interface{}, len(iv))
+		for j, vj := range iv {
+			m2[j] = testVerifyVal(vj, arg)
+		}
+		v2 = m2
+	case map[string]bool: 
+		switch arg {
+		case testVerifyMapTypeSame:
+			m2 := make(map[string]bool)
+			for kj, kv := range iv {
+				m2[kj] = kv
+			}			
+			v2 = m2
+		case testVerifyMapTypeStrIntf:
+			m2 := make(map[string]interface{})
+			for kj, kv := range iv {
+				m2[kj] = kv
+			}
+			v2 = m2
+		case testVerifyMapTypeIntfIntf:
+			m2 := make(map[interface{}]interface{})
+			for kj, kv := range iv {
+				m2[kj] = kv
+			}
+			v2 = m2
+		}
+	case map[string]interface{}: 
+		switch arg {
+		case testVerifyMapTypeSame:
+			m2 := make(map[string]interface{})
+			for kj, kv := range iv {
+				m2[kj] = testVerifyVal(kv, arg)
+			}
+			v2 = m2
+		case testVerifyMapTypeStrIntf:
+			m2 := make(map[string]interface{})
+			for kj, kv := range iv {
+				m2[kj] = testVerifyVal(kv, arg)
+			}
+			v2 = m2
+		case testVerifyMapTypeIntfIntf:
+			m2 := make(map[interface{}]interface{})
+			for kj, kv := range iv {
+				m2[kj] = testVerifyVal(kv, arg)
+			}
+			v2 = m2
+		}
+	case map[interface{}]interface{}: 
+		m2 := make(map[interface{}]interface{})
+		for kj, kv := range iv {
+			m2[testVerifyVal(kj, arg)] = testVerifyVal(kv, arg)
+		}
+		v2 = m2
+	default:
+		v2 = v
+	}
+	return
+}
+
 func init() {
 	primitives := []interface{} {
 		int8(-8),
@@ -190,54 +280,69 @@ func init() {
 	table = append(table, primitives)       //20 is a list of primitives
 	table = append(table, mapsAndStrucs...) //21-24 are maps. 25 is a *struct
 
-	// we verify against the same table, but skip 23 
-	// because interface{} equality is not defined exact for exact objects or nil.
-	var a, b []interface{}
-	var c map[string]interface{}
-	a = make([]interface{}, len(table))
-	copy(a, table)
-	b = make([]interface{}, len(a[20].([]interface{})))
-	copy(b, a[20].([]interface{}))
-	// b[0], b[4], b[8], b[16], b[19] = int8(-8), int8(8), int8(8), 
-	// 	// []interface {}{int32(1328148122), int16(2000)}, "bytestring"
-	// 	timeToCompare, "bytestring"
-	//b[4], b[8] = int8(8), int8(8)
-	//b[4] = int8(8)
-	a[20] = b
-	a[23] = skipVerifyVal 
-	//a[25] = skipVerifyVal
-	tableVerify = a
+	tableVerify = make([]interface{}, len(table))
+	tableTestNilVerify = make([]interface{}, len(table))
+	tablePythonVerify = make([]interface{}, len(table))
+	
+	lp := len(primitives)
+	av := tableVerify
+	for i, v := range table {
+		if i == lp+3 {
+			av[i] = skipVerifyVal
+			continue
+		} 
+		switch v.(type) {
+		case []interface{}:
+			av[i] = testVerifyVal(v, testVerifyMapTypeSame)
+		case map[string]interface{}:
+			av[i] = testVerifyVal(v, testVerifyMapTypeSame)
+		case map[interface{}]interface{}:
+			av[i] = testVerifyVal(v, testVerifyMapTypeSame)
+		default:
+			av[i] = v
+		}				
+	}
+
+	av = tableTestNilVerify
+	for i, v := range table {
+		if i > lp+3 { 
+			av[i] = skipVerifyVal
+			continue
+		}
+		av[i] = testVerifyVal(v, testVerifyMapTypeStrIntf)
+	}
 	
-	//when decoding into nil, for testing, 
-	//we treat each []byte as string, and uint < 127 are decoded as int8.
-	a = make([]interface{}, len(tableVerify))
-	copy(a, tableVerify)
-	//a[0], a[4], a[8], a[16], a[19] = int8(-8), int8(8), int8(8), timeToCompare, "bytestring"
-	a[0], a[16], a[19] = int8(-8), timeToCompare, "bytestring"
-	a[21] = map[string]interface{}{"true":true, "false":false}
-	a[23] = table[23]
-	a[25] = skipVerifyVal
-	tableTestNilVerify = a
+	av = tablePythonVerify
+	for i, v := range table {
+		if i > lp+3 { 
+			av[i] = skipVerifyVal
+			continue
+		}
+		av[i] = testVerifyVal(v, testVerifyMapTypeStrIntf)
+		//msgpack python encodes large positive numbers as unsigned ints
+		switch i {
+		case 16:
+			av[i] = uint64(1328148122000002)
+		case 20:
+			//msgpack python doesn't understand time. So use number val.
+			m2 := av[i].([]interface{})
+			m3 := make([]interface{}, len(m2))
+			copy(m3, m2)
+			m3[16] = uint64(1328148122000002)
+			av[i] = m3
+		case 23:
+			m2 := make(map[string]interface{})
+			for k2, v2 := range av[i].(map[string]interface{}) { 
+				m2[k2] = v2
+			}
+			m2["int32"] = uint64(32323232)
+			m3 := m2["list"].([]interface{})
+			m3[0], m3[1], m3[3] = uint64(1616), uint64(32323232), float64(-3232.0)
+			av[i] = m2
+		}
+	}
 	
-	//python msgpack encodes large positive numbers as unsigned, and all floats as float64
-	a = make([]interface{}, len(tableTestNilVerify)-2)
-	copy(a, tableTestNilVerify)
-	a[23] = table[23]
-	a[9], a[11], a[16] = float64(-3232.0), float64(3232.0), uint64(1328148122000002)
-	b = make([]interface{}, len(a[20].([]interface{})))
-	copy(b, a[20].([]interface{}))
-	//b[4], b[8] = int8(8), int8(8)
-	b[9], b[11], b[16] = float64(-3232.0), float64(3232.0), uint64(1328148122000002)
-	a[20] = b
-	c = make(map[string]interface{})
-	for k, v := range a[23].(map[string]interface{}) { 
-		c[k] = v
-	}
-	a[23] = c
-	c["int32"] = uint32(32323232)
-	b = c["list"].([]interface{})
-	b[0], b[1], b[3] = uint16(1616), uint32(32323232), float64(-3232.0)
-	tablePythonVerify = a
+	tablePythonVerify = tablePythonVerify[:24]
 }
 
 func testUnmarshal(v interface{}, data []byte, h Handle) error {
@@ -263,9 +368,9 @@ func newTestStruc(depth int, bench bool) (ts *TestStruc) {
 	
 	ts = &TestStruc {
 		S: "some string",
-		I64: 64,
+		I64: math.MaxInt64 * 2 / 3, // 64,
 		I16: 16,
-		Ui64: 64,
+		Ui64: uint64(int64(math.MaxInt64 * 2 / 3)), // 64, //don't use MaxUint64, as bson can't write it
 		Ui8: 160,
 		B: true,
 		By: 5,
@@ -278,7 +383,7 @@ func newTestStruc(depth int, bench bool) (ts *TestStruc) {
 		Bslice: []bool{true, false, true, false},
 		Byslice: []byte{13, 14, 15},
 		
-		Islice: []interface{}{"true", true, "no", false, uint8(88), float64(0.4)},
+		Islice: []interface{}{"true", true, "no", false, uint64(88), float64(0.4)},
 		
 		Ms: map[string]interface{}{
 			"true": "true",
@@ -393,7 +498,7 @@ func testCodecTableOne(t *testing.T, h Handle) {
 		v.WriteExt = true
 	}
 	doTestCodecTableOne(t, false, h, table, tableVerify) 
-
+	//if true { panic("") }
 	switch v := h.(type) {
 	case *MsgpackHandle:
 		v.WriteExt = oldWriteExt
@@ -415,7 +520,7 @@ func testCodecTableOne(t *testing.T, h Handle) {
 	}
 	//skip #16 (time.Time), and #20 ([]interface{} containing time.Time)
 	doTestCodecTableOne(t, true, h, table[:16], tableTestNilVerify[:16]) 
-	doTestCodecTableOne(t, true, h, table[17:20], tableTestNilVerify[17:20]) 
+	doTestCodecTableOne(t, true, h, table[17:20], tableTestNilVerify[17:20])
 	doTestCodecTableOne(t, true, h, table[21:24], tableTestNilVerify[21:24]) 
 	
 	switch v := h.(type) {
@@ -466,7 +571,7 @@ func testCodecMiscOne(t *testing.T, h Handle) {
 	if err != nil {
 		logT(t, "------- Cannot Unmarshal pointer to struct. Error: %v", err)
 		t.FailNow()
-	} else if ts2.I64 != 64 {
+	} else if ts2.I64 != math.MaxInt64 * 2 / 3 {
 		logT(t, "------- Unmarshal wrong. Expect I64 = 64. Got: %v", ts2.I64)
 		t.FailNow()
 	}
@@ -710,16 +815,30 @@ func doTestMsgpackPythonGenStreams(t *testing.T) {
 	
 }
 
-func TestCodecs(t *testing.T) {
+func TestMsgpackCodecsTable(t *testing.T) {
 	testCodecTableOne(t, testMsgpackH)
+}
+
+func TestMsgpackCodecsMisc(t *testing.T) {
 	testCodecMiscOne(t, testMsgpackH)
+}
+
+func TestBincCodecsTable(t *testing.T) {
 	testCodecTableOne(t, testBincH)
+}
+
+func TestBincCodecsMisc(t *testing.T) {
 	testCodecMiscOne(t, testBincH)	
 }
 
-func TestRpcs(t *testing.T) {
-	doTestRpcOne(t, MsgpackSpecRpc, testMsgpackH, true, true, true)
+func TestMsgpackRpcGo(t *testing.T) {
 	doTestRpcOne(t, GoRpc, testMsgpackH, true, true, true)
+}
+func TestMsgpackRpcSpec(t *testing.T) {
+	doTestRpcOne(t, MsgpackSpecRpc, testMsgpackH, true, true, true)
+}
+
+func TestBincRpcGo(t *testing.T) {
 	doTestRpcOne(t, GoRpc, testBincH, true, true, true)
 }
 

+ 34 - 34
codec/decode.go

@@ -31,7 +31,7 @@ const (
 type decReader interface {
 	readn(n int) []byte
 	readb([]byte)
-	readUint8() uint8
+	readn1() uint8
 	readUint16() uint16
 	readUint32() uint32
 	readUint64() uint64
@@ -42,13 +42,15 @@ type decoder interface {
 	currentIsNil() bool
 	decodeBuiltinType(rt reflect.Type, rv reflect.Value) bool
 	//decodeNaked should completely handle extensions, builtins, primitives, etc.
+	//Numbers are decoded as int64, uint64, float64 only (no smaller sized number types).
 	decodeNaked(h decodeHandleI) (rv reflect.Value, ctx decodeNakedContext)
 	decodeInt(bitsize uint8) (i int64)
 	decodeUint(bitsize uint8) (ui uint64) 
 	decodeFloat(chkOverflow32 bool) (f float64)
 	decodeBool() (b bool) 
+	// decodeString can also decode symbols
 	decodeString() (s string) 
-	decodeStringBytes(bs []byte) (bsOut []byte, changed bool)
+	decodeBytes(bs []byte) (bsOut []byte, changed bool)
 	decodeExt(tag byte) []byte
 	readMapLen() int
 	readArrayLen() int 
@@ -65,9 +67,8 @@ type Decoder struct {
 
 // ioDecReader is a decReader that reads off an io.Reader
 type ioDecReader struct {
-	x [8]byte        //temp byte array re-used internally for efficiency
-	t01, t02, t04, t08 []byte // use these, so no need to constantly re-slice
 	r io.Reader
+	x [8]byte        //temp byte array re-used internally for efficiency
 }
 
 
@@ -182,7 +183,6 @@ func NewDecoder(r io.Reader, h Handle) (*Decoder) {
 	z := ioDecReader {
 		r: r,
 	}
-	z.t01, z.t02, z.t04, z.t08 = z.x[:1], z.x[:2], z.x[:4], z.x[:8]
 	return &Decoder{ r: &z, d: h.newDecoder(&z), h: h }
 }
 
@@ -427,13 +427,13 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 		// In places where the slice got from an array could be, we should guard with CanSet() calls.
 		
 		if rt == byteSliceTyp { // rawbytes 
-			if bs2, changed2 := dd.decodeStringBytes(rv.Bytes()); changed2 {
+			if bs2, changed2 := dd.decodeBytes(rv.Bytes()); changed2 {
 				rv.SetBytes(bs2)
 			}
 			if wasNilIntf && rv.IsNil() {
 				rv.SetBytes([]byte{})
 			}
-			break			
+			break
 		}
 		
 		containerLen := dd.readArrayLen()
@@ -540,24 +540,24 @@ func (z *ioDecReader) readb(bs []byte) {
 	}
 }
 
-func (z *ioDecReader) readUint8() uint8 {
-	z.readb(z.t01)
+func (z *ioDecReader) readn1() uint8 {
+	z.readb(z.x[:1])
 	return z.x[0]
 }
 
 func (z *ioDecReader) readUint16() uint16 {
-	z.readb(z.t02)
-	return binc.Uint16(z.t02)
+	z.readb(z.x[:2])
+	return bigen.Uint16(z.x[:2])
 }
 
 func (z *ioDecReader) readUint32() uint32 {
-	z.readb(z.t04)
-	return binc.Uint32(z.t04)
+	z.readb(z.x[:4])
+	return bigen.Uint32(z.x[:4])
 }
 
 func (z *ioDecReader) readUint64() uint64 {
-	z.readb(z.t08)
-	return binc.Uint64(z.t08)
+	z.readb(z.x[:8])
+	return bigen.Uint64(z.x[:8])
 }
 
 // ------------------------------------
@@ -586,7 +586,7 @@ func (z *bytesDecReader) readb(bs []byte) {
 	copy(bs, z.readn(len(bs)))
 }
 
-func (z *bytesDecReader) readUint8() uint8 {
+func (z *bytesDecReader) readn1() uint8 {
 	c0 := z.consume(1)
 	return z.b[c0]
 }
@@ -602,12 +602,12 @@ func (z *bytesDecReader) readUint16() uint16 {
 
 func (z *bytesDecReader) readUint32() uint32 {
 	c0 := z.consume(4)
-	return binc.Uint32(z.b[c0 : z.c])
+	return bigen.Uint32(z.b[c0 : z.c])
 }
 
 func (z *bytesDecReader) readUint64() uint64 {
 	c0 := z.consume(8)
-	return binc.Uint64(z.b[c0 : z.c])
+	return bigen.Uint64(z.b[c0 : z.c])
 }
 
 // ----------------------------------------
@@ -626,30 +626,30 @@ func decodeTime(bs []byte) (tt time.Time, err error) {
 	)
 	switch len(bs) {
 	case 4:		
-		tsec = int64(int32(binc.Uint32(bs)))
+		tsec = int64(int32(bigen.Uint32(bs)))
 	case 6:
-		tsec = int64(int32(binc.Uint32(bs)))
-		tz = (binc.Uint16(bs[4:]))
+		tsec = int64(int32(bigen.Uint32(bs)))
+		tz = (bigen.Uint16(bs[4:]))
 	case 8:
-		tsec = int64(int32(binc.Uint32(bs)))
-		tnsec = int32(binc.Uint32(bs[4:]))
+		tsec = int64(int32(bigen.Uint32(bs)))
+		tnsec = int32(bigen.Uint32(bs[4:]))
 	case 10:
-		tsec = int64(int32(binc.Uint32(bs)))
-		tnsec = int32(binc.Uint32(bs[4:]))
-		tz = (binc.Uint16(bs[8:]))
+		tsec = int64(int32(bigen.Uint32(bs)))
+		tnsec = int32(bigen.Uint32(bs[4:]))
+		tz = (bigen.Uint16(bs[8:]))
 
 	case 9:
-		tsec = int64(binc.Uint64(bs))
+		tsec = int64(bigen.Uint64(bs))
 	case 11:
-		tsec = int64(binc.Uint64(bs))
-		tz = (binc.Uint16(bs[8:]))
+		tsec = int64(bigen.Uint64(bs))
+		tz = (bigen.Uint16(bs[8:]))
 	case 12:
-		tsec = int64(binc.Uint64(bs))
-		tnsec = int32(binc.Uint32(bs[8:]))
+		tsec = int64(bigen.Uint64(bs))
+		tnsec = int32(bigen.Uint32(bs[8:]))
 	case 14:
-		tsec = int64(binc.Uint64(bs))
-		tnsec = int32(binc.Uint32(bs[8:]))
-		tz = (binc.Uint16(bs[12:]))
+		tsec = int64(bigen.Uint64(bs))
+		tnsec = int32(bigen.Uint32(bs[8:]))
+		tz = (bigen.Uint16(bs[12:]))
 	default:
 		err = fmt.Errorf("Error decoding bytes: %v as time.Time. Invalid length: %v", bs, len(bs))
 		return 

+ 43 - 20
codec/encode.go

@@ -9,10 +9,10 @@ import (
 	"reflect"
 	"math"
 	"time"
-	"fmt"
+	//"fmt"
 )
 
-var _ = fmt.Printf
+//var _ = fmt.Printf
 const (
 	// Some tagging information for error messages.
 	msgTagEnc = "codec.encoder"
@@ -30,6 +30,7 @@ type encWriter interface {
 	writen1(byte)
 	writen2(byte, byte)
 	writen3(byte, byte, byte)
+	writen4(byte, byte, byte, byte)
 	flush()
 }
 
@@ -45,6 +46,7 @@ type encoder interface {
 	encodeArrayPreamble(length int)
 	encodeMapPreamble(length int)
 	encodeString(c charEncoding, v string)
+	encodeSymbol(v string)
 	encodeStringBytes(c charEncoding, v []byte)
 	//TODO
 	//encBignum(f *big.Int) 
@@ -79,9 +81,7 @@ type ioEncWriterFlusher interface {
 // ioEncWriter implements encWriter and can write to an io.Writer implementation
 type ioEncWriter struct {
 	w ioEncWriterWriter
-	// temp byte array and slices used to prevent constant re-slicing while writing.
-	x [8]byte        
-	t01, t02, t04, t08 []byte 
+	x [8]byte // temp byte array re-used internally for efficiency
 }
 
 // bytesEncWriter implements encWriter and can write to an byte slice.
@@ -163,7 +163,6 @@ func NewEncoder(w io.Writer, h Handle) (*Encoder) {
 	z := ioEncWriter {
 		w: ww,
 	}
-	z.t01, z.t02, z.t04, z.t08 = z.x[:1], z.x[:2], z.x[:4], z.x[:8]
 	return &Encoder { w: &z, h: h, e: h.newEncoder(&z) }
 }
 
@@ -214,7 +213,11 @@ func NewEncoderBytes(out *[]byte, h Handle) (*Encoder) {
 //          Field4 bool     `codec:"f4,omitempty"` //use key "f4". Omit if empty.
 //          ...
 //      }
-//    
+// 
+// Note: 
+//   - Encode will treat struct field names and keys in map[string]XXX as symbols.
+//     Some formats support symbols (e.g. binc) and will properly encode the string
+//     only once in the stream, and use a tag to refer to it thereafter.
 func (e *Encoder) Encode(v interface{}) (err error) {
 	defer panicToErr(&err) 
 	e.encode(v)
@@ -361,10 +364,15 @@ func (e *Encoder) encodeValue(rv reflect.Value) {
 		if l == 0 {
 			break
 		}
+		keyTypeIsString := rt.Key().Kind() == reflect.String 
 		mks := rv.MapKeys()
 		// for j, lmks := 0, len(mks); j < lmks; j++ {
 		for j := range mks {
-			e.encodeValue(mks[j])
+			if keyTypeIsString {
+				ee.encodeSymbol(mks[j].String())
+			} else {
+				e.encodeValue(mks[j])
+			}
 		 	e.encodeValue(rv.MapIndex(mks[j]))
 		}
 	case reflect.Struct:
@@ -419,7 +427,7 @@ func (e *Encoder) encStruct(rt reflect.Type, rv reflect.Value) {
 	ee.encodeMapPreamble(newlen)
 	for j := 0; j < newlen; j++ {
 		//e.encString(sis[sivals[j]].encName)
-		ee.encodeString(c_UTF8, encnames[j])
+		ee.encodeSymbol(encnames[j])
 		e.encodeValue(rvals[j])
 	}
 }
@@ -427,18 +435,18 @@ func (e *Encoder) encStruct(rt reflect.Type, rv reflect.Value) {
 // ----------------------------------------
 
 func (z *ioEncWriter) writeUint16(v uint16) {
-	binc.PutUint16(z.t02, v)
-	z.writeb(z.t02)
+	bigen.PutUint16(z.x[:2], v)
+	z.writeb(z.x[:2])
 }
 
 func (z *ioEncWriter) writeUint32(v uint32) {
-	binc.PutUint32(z.t04, v)
-	z.writeb(z.t04)
+	bigen.PutUint32(z.x[:4], v)
+	z.writeb(z.x[:4])
 }
 
 func (z *ioEncWriter) writeUint64(v uint64) {
-	binc.PutUint64(z.t08, v)
-	z.writeb(z.t08)
+	bigen.PutUint64(z.x[:8], v)
+	z.writeb(z.x[:8])
 }
 
 func (z *ioEncWriter) writeb(bs []byte) {
@@ -472,10 +480,17 @@ func (z *ioEncWriter) writen2(b1 byte, b2 byte) {
 	z.writen1(b2)
 }
 
-func (z *ioEncWriter) writen3(b1 byte, b2 byte, b3 byte) {
+func (z *ioEncWriter) writen3(b1, b2, b3 byte) {
+	z.writen1(b1)
+	z.writen1(b2)
+	z.writen1(b3)
+}
+
+func (z *ioEncWriter) writen4(b1, b2, b3, b4 byte) {
 	z.writen1(b1)
 	z.writen1(b2)
 	z.writen1(b3)
+	z.writen1(b4)
 }
 
 func (z *ioEncWriter) flush() {
@@ -542,6 +557,14 @@ func (z *bytesEncWriter) writen3(b1 byte, b2 byte, b3 byte) {
 	z.b[c + 2] = b3
 }
 
+func (z *bytesEncWriter) writen4(b1 byte, b2 byte, b3 byte, b4 byte) {
+	c := z.grow(4)
+	z.b[c] = b1
+	z.b[c + 1] = b2
+	z.b[c + 2] = b3
+	z.b[c + 3] = b4
+}
+
 func (z *bytesEncWriter) flush() { 
 	*(z.out) = z.b[:z.c]
 }
@@ -581,15 +604,15 @@ func encodeTime(t time.Time) ([]byte) {
 		l = nil
 	}
 	if tsecs > math.MinInt32 && tsecs < math.MaxInt32 {
-		binc.PutUint32(bs[i:], uint32(int32(tsecs)))
+		bigen.PutUint32(bs[i:], uint32(int32(tsecs)))
 		i = i + 4
 	} else {
-		binc.PutUint64(bs[i:], uint64(tsecs))
+		bigen.PutUint64(bs[i:], uint64(tsecs))
 		i = i + 8
 		padzero = (tnsecs == 0)
 	}
 	if tnsecs != 0 {
-		binc.PutUint32(bs[i:], uint32(tnsecs))
+		bigen.PutUint32(bs[i:], uint32(tnsecs))
 		i = i + 4
 	}
 	if l != nil {
@@ -607,7 +630,7 @@ func encodeTime(t time.Time) ([]byte) {
 			z |= 1 << 15 //set sign bit
 		}
 		//fmt.Printf(">>>>>> ENC: z: %b\n", z)
-		binc.PutUint16(bs[i:], z)
+		bigen.PutUint16(bs[i:], z)
 		i = i + 2
 	}
 	if padzero {

+ 1 - 1
codec/helper.go

@@ -36,7 +36,7 @@ const (
 )
 
 var (
-	binc = binary.BigEndian
+	bigen = binary.BigEndian
 	structInfoFieldName = "_struct"
 	
 	cachedStructFieldInfos = make(map[reflect.Type]structFieldInfos, 4)

+ 25 - 21
codec/msgpack.go

@@ -227,6 +227,10 @@ func (e *msgpackEncoder) encodeString(c charEncoding, s string) {
 	}
 }
 
+func (e *msgpackEncoder) encodeSymbol(v string) { 
+	e.encodeString(c_UTF8, v)
+}
+
 func (e *msgpackEncoder) encodeStringBytes(c charEncoding, bs []byte) {
 	//ignore charEncoding. 
 	e.writeContainerLen(msgpackContainerRawBytes, len(bs))
@@ -275,36 +279,36 @@ func (d *msgpackDecoder) decodeNaked(h decodeHandleI) (rv reflect.Value, ctx dec
 		v = true
 
 	case mpFloat:
-		v = math.Float32frombits(d.r.readUint32())
+		v = float64(math.Float32frombits(d.r.readUint32()))
 	case mpDouble:
 		v = math.Float64frombits(d.r.readUint64())
 		
 	case mpUint8:
-		v = d.r.readUint8()
+		v = uint64(d.r.readn1())
 	case mpUint16:
-		v = d.r.readUint16()
+		v = uint64(d.r.readUint16())
 	case mpUint32:
-		v = d.r.readUint32()
+		v = uint64(d.r.readUint32())
 	case mpUint64:
-		v = d.r.readUint64()
+		v = uint64(d.r.readUint64())
 		
 	case mpInt8:
-		v = int8(d.r.readUint8())
+		v = int64(int8(d.r.readn1()))
 	case mpInt16:
-		v = int16(d.r.readUint16())
+		v = int64(int16(d.r.readUint16()))
 	case mpInt32:
-		v = int32(d.r.readUint32())
+		v = int64(int32(d.r.readUint32()))
 	case mpInt64:
-		v = int64(d.r.readUint64())
+		v = int64(int64(d.r.readUint64()))
 		
 	default:
 		switch {
 		case bd >= mpPosFixNumMin && bd <= mpPosFixNumMax:
 			// positive fixnum (always signed)
-			v = int8(bd)
+			v = int64(int8(bd))
 		case bd >= mpNegFixNumMin && bd <= mpNegFixNumMax:
 			// negative fixnum
-			v = int8(bd)		
+			v = int64(int8(bd))		
 		case bd == mpRaw16, bd == mpRaw32, bd >= mpFixRawMin && bd <= mpFixRawMax:
 			ctx = dncContainer
 			// v = containerRawBytes
@@ -335,7 +339,7 @@ func (d *msgpackDecoder) decodeNaked(h decodeHandleI) (rv reflect.Value, ctx dec
 			}
 		case bd >= mpXv4Fixext0 && bd <= mpXv4Fixext5, bd >= mpXv4Ext8m && bd <= mpXv4Ext32:
 			//ctx = dncExt
-			xtag := d.r.readUint8()
+			xtag := d.r.readn1()
 			opts := h.(*MsgpackHandle)
 			rt, bfn := opts.getDecodeExtForTag(xtag)
 			if rt == nil {
@@ -366,7 +370,7 @@ func (d *msgpackDecoder) decodeNaked(h decodeHandleI) (rv reflect.Value, ctx dec
 func (d *msgpackDecoder) decodeInt(bitsize uint8) (i int64) {
 	switch d.bd {
 	case mpUint8:
-		i = int64(uint64(d.r.readUint8()))
+		i = int64(uint64(d.r.readn1()))
 	case mpUint16:
 		i = int64(uint64(d.r.readUint16()))
 	case mpUint32:
@@ -374,7 +378,7 @@ func (d *msgpackDecoder) decodeInt(bitsize uint8) (i int64) {
 	case mpUint64:
 		i = int64(d.r.readUint64())
 	case mpInt8:
-		i = int64(int8(d.r.readUint8()))
+		i = int64(int8(d.r.readn1()))
 	case mpInt16:
 		i = int64(int16(d.r.readUint16()))
 	case mpInt32:
@@ -406,7 +410,7 @@ func (d *msgpackDecoder) decodeInt(bitsize uint8) (i int64) {
 func (d *msgpackDecoder) decodeUint(bitsize uint8) (ui uint64) {
 	switch d.bd {
 	case mpUint8:
-		ui = uint64(d.r.readUint8())
+		ui = uint64(d.r.readn1())
 	case mpUint16:
 		ui = uint64(d.r.readUint16())
 	case mpUint32:
@@ -414,7 +418,7 @@ func (d *msgpackDecoder) decodeUint(bitsize uint8) (ui uint64) {
 	case mpUint64:
 		ui = d.r.readUint64()
 	case mpInt8:
-		if i := int64(int8(d.r.readUint8())); i >= 0 {
+		if i := int64(int8(d.r.readn1())); i >= 0 {
 			ui = uint64(i)
 		} else {
 			decErr("Assigning negative signed value: %v, to unsigned type", i)
@@ -505,7 +509,7 @@ func (d *msgpackDecoder) decodeString() (s string) {
 }
 
 // Callers must check if changed=true (to decide whether to replace the one they have)
-func (d *msgpackDecoder) decodeStringBytes(bs []byte) (bsOut []byte, changed bool) {
+func (d *msgpackDecoder) decodeBytes(bs []byte) (bsOut []byte, changed bool) {
 	clen := d.readContainerLen(msgpackContainerRawBytes)
 	// if clen < 0 {
 	// 	changed = true
@@ -534,7 +538,7 @@ func (d *msgpackDecoder) initReadNext() {
 	if d.bdRead {
 		return
 	}
-	d.bd = d.r.readUint8()
+	d.bd = d.r.readn1()
 	d.bdRead = true
 }
 
@@ -577,7 +581,7 @@ func (d *msgpackDecoder) readExtLen() (clen int) {
 	case mpNil:
 		clen = -1 // to represent nil
 	case mpXv4Fixext5:
-		clen = int(d.r.readUint8())
+		clen = int(d.r.readn1())
 	case mpXv4Ext16:
 		clen = int(d.r.readUint16())
 	case mpXv4Ext32:
@@ -600,12 +604,12 @@ func (d *msgpackDecoder) decodeExt(tag byte) (xbs []byte) {
 	xbd := d.bd
 	switch {
 	case xbd >= mpXv4Fixext0 && xbd <= mpXv4Fixext5, xbd >= mpXv4Ext8m && xbd <= mpXv4Ext32:
-		if xtag := d.r.readUint8(); xtag != tag {
+		if xtag := d.r.readn1(); xtag != tag {
 			decErr("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
 		}
 		xbs = d.r.readn(d.readExtLen())
 	case xbd == mpRaw16, xbd == mpRaw32, xbd >= mpFixRawMin && xbd <= mpFixRawMax:
-		xbs, _ = d.decodeStringBytes(nil)
+		xbs, _ = d.decodeBytes(nil)
 	default:
 		decErr("Wrong byte descriptor (Expecting extensions or raw bytes). Got: 0x%x", xbd)
 	}