Browse Source

codec: Fixes and tests for decoding time before unix epoch, and decoding zero-length list/map in stream into nil slice/map.

This fixes issues #5 and #7 .
Ugorji Nwoke 12 years ago
parent
commit
70872afeb4
5 changed files with 128 additions and 100 deletions
  1. 68 45
      codec/codecs_test.go
  2. 19 15
      codec/decode.go
  3. 9 6
      codec/msgpack.go
  4. 22 34
      codec/msgpack_test.py
  5. 10 0
      codec/time.go

+ 68 - 45
codec/codecs_test.go

@@ -44,6 +44,7 @@ const (
 	testVerifyMapTypeSame testVerifyArg = iota
 	testVerifyMapTypeSame testVerifyArg = iota
 	testVerifyMapTypeStrIntf
 	testVerifyMapTypeStrIntf
 	testVerifyMapTypeIntfIntf
 	testVerifyMapTypeIntfIntf
+	testVerifyForPython
 )
 )
 
 
 var (
 var (
@@ -51,10 +52,12 @@ var (
 	testUseIoEncDec bool
 	testUseIoEncDec bool
 	_                           = fmt.Printf
 	_                           = fmt.Printf
 	skipVerifyVal   interface{} = &(struct{}{})
 	skipVerifyVal   interface{} = &(struct{}{})
-	timeLoc                     = time.FixedZone("UTC-08:00", -8*60*60)         //time.UTC
-	timeToCompare               = time.Date(2012, 2, 2, 2, 2, 2, 2000, timeLoc) //time.Time{}
-	//"2012-02-02T02:02:02.000002000Z" //1328148122000002
-	timeToCompareAs    interface{}   = timeToCompare.UnixNano()
+	timeLoc                     = time.FixedZone("UTC-08:00", -8*60*60)         //time.UTC-8
+	timeToCompare1              = time.Date(2012, 2, 2, 2, 2, 2, 2000, timeLoc) 
+	timeToCompare2              = time.Date(1900, 2, 2, 2, 2, 2, 2000, timeLoc) 
+	timeToCompare3              = time.Unix(0, 0).UTC()
+	timeToCompare4              = time.Time{}.UTC()
+
 	table              []interface{} // main items we encode
 	table              []interface{} // main items we encode
 	tableVerify        []interface{} // we verify encoded things against this after decode
 	tableVerify        []interface{} // we verify encoded things against this after decode
 	tableTestNilVerify []interface{} // for nil interface, use this to verify (rules are different)
 	tableTestNilVerify []interface{} // for nil interface, use this to verify (rules are different)
@@ -135,15 +138,34 @@ func (r *TestRpcInt) Square(ignore int, res *int) error { *res = r.i * r.i; retu
 func (r *TestRpcInt) Mult(n int, res *int) error        { *res = r.i * n; 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{}) {
 func testVerifyVal(v interface{}, arg testVerifyArg) (v2 interface{}) {
+	//for python msgpack, 
+	//  - all positive integers are unsigned 64-bit ints
+	//  - all floats are float64
 	switch iv := v.(type) {
 	switch iv := v.(type) {
 	case int8:
 	case int8:
-		v2 = int64(iv)
+		if arg == testVerifyForPython && iv > 0 {
+			v2 = uint64(iv)
+		} else {
+			v2 = int64(iv)
+		}
 	case int16:
 	case int16:
-		v2 = int64(iv)
+		if arg == testVerifyForPython && iv > 0 {
+			v2 = uint64(iv)
+		} else {
+			v2 = int64(iv)
+		}
 	case int32:
 	case int32:
-		v2 = int64(iv)
+		if arg == testVerifyForPython && iv > 0 {
+			v2 = uint64(iv)
+		} else {
+			v2 = int64(iv)
+		}
 	case int64:
 	case int64:
-		v2 = int64(iv)
+		if arg == testVerifyForPython && iv > 0 {
+			v2 = uint64(iv)
+		} else {
+			v2 = int64(iv)
+		}
 	case uint8:
 	case uint8:
 		v2 = uint64(iv)
 		v2 = uint64(iv)
 	case uint16:
 	case uint16:
@@ -170,7 +192,7 @@ func testVerifyVal(v interface{}, arg testVerifyArg) (v2 interface{}) {
 				m2[kj] = kv
 				m2[kj] = kv
 			}
 			}
 			v2 = m2
 			v2 = m2
-		case testVerifyMapTypeStrIntf:
+		case testVerifyMapTypeStrIntf, testVerifyForPython:
 			m2 := make(map[string]interface{})
 			m2 := make(map[string]interface{})
 			for kj, kv := range iv {
 			for kj, kv := range iv {
 				m2[kj] = kv
 				m2[kj] = kv
@@ -191,7 +213,7 @@ func testVerifyVal(v interface{}, arg testVerifyArg) (v2 interface{}) {
 				m2[kj] = testVerifyVal(kv, arg)
 				m2[kj] = testVerifyVal(kv, arg)
 			}
 			}
 			v2 = m2
 			v2 = m2
-		case testVerifyMapTypeStrIntf:
+		case testVerifyMapTypeStrIntf, testVerifyForPython:
 			m2 := make(map[string]interface{})
 			m2 := make(map[string]interface{})
 			for kj, kv := range iv {
 			for kj, kv := range iv {
 				m2[kj] = testVerifyVal(kv, arg)
 				m2[kj] = testVerifyVal(kv, arg)
@@ -210,6 +232,17 @@ func testVerifyVal(v interface{}, arg testVerifyArg) (v2 interface{}) {
 			m2[testVerifyVal(kj, arg)] = testVerifyVal(kv, arg)
 			m2[testVerifyVal(kj, arg)] = testVerifyVal(kv, arg)
 		}
 		}
 		v2 = m2
 		v2 = m2
+	case time.Time:
+		switch arg {
+		case testVerifyForPython:
+			if iv2 := iv.UnixNano(); iv2 > 0 {
+				v2 = uint64(iv2)
+			} else {
+				v2 = int64(iv2)
+			}
+		default:
+			v2 = v
+		}
 	default:
 	default:
 		v2 = v
 		v2 = v
 	}
 	}
@@ -234,10 +267,13 @@ func init() {
 		false,
 		false,
 		true,
 		true,
 		nil,
 		nil,
-		timeToCompare,
 		"someday",
 		"someday",
 		"",
 		"",
 		"bytestring",
 		"bytestring",
+		timeToCompare1,
+		timeToCompare2,
+		timeToCompare3,
+		timeToCompare4,
 	}
 	}
 	mapsAndStrucs := []interface{}{
 	mapsAndStrucs := []interface{}{
 		map[string]bool{
 		map[string]bool{
@@ -292,6 +328,7 @@ func init() {
 			av[i] = skipVerifyVal
 			av[i] = skipVerifyVal
 			continue
 			continue
 		}
 		}
+		//av[i] = testVerifyVal(v, testVerifyMapTypeSame)
 		switch v.(type) {
 		switch v.(type) {
 		case []interface{}:
 		case []interface{}:
 			av[i] = testVerifyVal(v, testVerifyMapTypeSame)
 			av[i] = testVerifyVal(v, testVerifyMapTypeSame)
@@ -319,28 +356,7 @@ func init() {
 			av[i] = skipVerifyVal
 			av[i] = skipVerifyVal
 			continue
 			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
-		}
+		av[i] = testVerifyVal(v, testVerifyForPython)
 	}
 	}
 
 
 	tablePythonVerify = tablePythonVerify[:24]
 	tablePythonVerify = tablePythonVerify[:24]
@@ -394,7 +410,7 @@ func newTestStruc(depth int, bench bool) (ts *TestStruc) {
 			"one": 1,
 			"one": 1,
 			"two": 2,
 			"two": 2,
 		},
 		},
-		T: timeToCompare,
+		T: timeToCompare1,
 		AnonInTestStruc: AnonInTestStruc{
 		AnonInTestStruc: AnonInTestStruc{
 			AS:        "A-String",
 			AS:        "A-String",
 			AI64:      64,
 			AI64:      64,
@@ -504,10 +520,12 @@ func testCodecTableOne(t *testing.T, h Handle) {
 		v.WriteExt = oldWriteExt
 		v.WriteExt = oldWriteExt
 	}
 	}
 	// func TestMsgpackAll(t *testing.T) {
 	// func TestMsgpackAll(t *testing.T) {
-
-	doTestCodecTableOne(t, false, h, table[:20], tableVerify[:20])
-	doTestCodecTableOne(t, false, h, table[21:], tableVerify[21:])
-
+	
+	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) {
 	// func TestMsgpackNilStringMap(t *testing.T) {
 	var oldMapType reflect.Type
 	var oldMapType reflect.Type
 	switch v := h.(type) {
 	switch v := h.(type) {
@@ -518,10 +536,9 @@ func testCodecTableOne(t *testing.T, h Handle) {
 		oldMapType = v.MapType
 		oldMapType = v.MapType
 		v.MapType = mapStringIntfTyp
 		v.MapType = mapStringIntfTyp
 	}
 	}
-	//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[21:24], tableTestNilVerify[21:24])
+	//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) {
 	switch v := h.(type) {
 	case *MsgpackHandle:
 	case *MsgpackHandle:
@@ -531,9 +548,11 @@ func testCodecTableOne(t *testing.T, h Handle) {
 	}
 	}
 
 
 	// func TestMsgpackNilIntf(t *testing.T) {
 	// func TestMsgpackNilIntf(t *testing.T) {
-	doTestCodecTableOne(t, true, h, table[24:], tableTestNilVerify[24:])
-
-	doTestCodecTableOne(t, true, h, table[17:18], tableTestNilVerify[17:18])
+	
+	//do newTestStruc and last element of map
+	doTestCodecTableOne(t, true, h, table[numPrim+numMap:], tableTestNilVerify[numPrim+numMap:])
+	//TODO? What is this one? 
+	//doTestCodecTableOne(t, true, h, table[17:18], tableTestNilVerify[17:18])
 }
 }
 
 
 func testCodecMiscOne(t *testing.T, h Handle) {
 func testCodecMiscOne(t *testing.T, h Handle) {
@@ -840,3 +859,7 @@ func TestMsgpackRpcSpec(t *testing.T) {
 func TestBincRpcGo(t *testing.T) {
 func TestBincRpcGo(t *testing.T) {
 	doTestRpcOne(t, GoRpc, testBincH, true, true, true)
 	doTestRpcOne(t, GoRpc, testBincH, true, true, true)
 }
 }
+
+//TODO: 
+//  - Add test for decoding empty list/map in stream into a nil slice/map
+

+ 19 - 15
codec/decode.go

@@ -14,7 +14,6 @@ import (
 var (
 var (
 	msgTagDec  = "codec.decoder"
 	msgTagDec  = "codec.decoder"
 	msgBadDesc = "Unrecognized descriptor byte"
 	msgBadDesc = "Unrecognized descriptor byte"
-	digits     = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
 )
 )
 
 
 type decodeNakedContext uint8
 type decodeNakedContext uint8
@@ -210,6 +209,15 @@ func NewDecoderBytes(in []byte, h Handle) *Decoder {
 //   dec := codec.NewDecoder(r, handle)
 //   dec := codec.NewDecoder(r, handle)
 //   err = dec.Decode(&v)
 //   err = dec.Decode(&v)
 //
 //
+// There are some special rules when decoding into containers (slice/array/map/struct).
+// Decode will typically use the stream contents to UPDATE the container. 
+//   - This means that for a struct or map, we just update matching fields or keys.
+//   - For a slice/array, we just update the first n elements, where n is length of the stream.
+//   - However, if decoding into a nil map/slice and the length of the stream is 0,
+//     we reset the destination map/slice to be a zero-length non-nil map/slice.
+//   - Also, if the encoded value is Nil in the stream, then we try to set
+//     the container to its "zero" value (e.g. nil for slice/map).
+// 
 func (d *Decoder) Decode(v interface{}) (err error) {
 func (d *Decoder) Decode(v interface{}) (err error) {
 	defer panicToErr(&err)
 	defer panicToErr(&err)
 	d.decode(v)
 	d.decode(v)
@@ -329,14 +337,7 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 		return
 		return
 	}
 	}
 
 
-	// Note: In decoding into containers, we just use the stream to UPDATE the container.
-	// This means that for a struct or map, we just update matching fields or keys.
-	// For a slice/array, we just update the first n elements, where n is the length of the
-	// stream.
-	// However, if the encoded value is Nil in the stream, then we try to set
-	// to nil, or a "zero" value.
-	//
-	// Also, we must ensure that, if decoding into a nil interface{}, we return a non-nil
+	// NOTE: if decoding into a nil interface{}, we return a non-nil
 	// value except even if the container registers a length of 0.
 	// value except even if the container registers a length of 0.
 	//
 	//
 	// NOTE: Do not make blocks for struct, slice, map, etc individual methods.
 	// NOTE: Do not make blocks for struct, slice, map, etc individual methods.
@@ -437,14 +438,15 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 			rv = reflect.MakeSlice(rt, containerLen, containerLen)
 			rv = reflect.MakeSlice(rt, containerLen, containerLen)
 		}
 		}
 		if containerLen == 0 {
 		if containerLen == 0 {
+			if rv.IsNil() {
+				rv.Set(reflect.MakeSlice(rt, containerLen, containerLen))
+			} 
 			break
 			break
 		}
 		}
 
 
 		if rv.IsNil() {
 		if rv.IsNil() {
 			// wasNilIntf only applies if rv is nil (since that's what we did earlier)
 			// wasNilIntf only applies if rv is nil (since that's what we did earlier)
-			if containerLen > 0 {
-				rv.Set(reflect.MakeSlice(rt, containerLen, containerLen))
-			}
+			rv.Set(reflect.MakeSlice(rt, containerLen, containerLen))
 		} else {
 		} else {
 			// if we need to reset rv but it cannot be set, we should err out.
 			// if we need to reset rv but it cannot be set, we should err out.
 			// for example, if slice is got from unaddressable array, CanSet = false
 			// for example, if slice is got from unaddressable array, CanSet = false
@@ -470,13 +472,14 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 	case reflect.Map:
 	case reflect.Map:
 		containerLen := dd.readMapLen()
 		containerLen := dd.readMapLen()
 
 
+		if rv.IsNil() {
+			rv.Set(reflect.MakeMap(rt))
+		}
+		
 		if containerLen == 0 {
 		if containerLen == 0 {
 			break
 			break
 		}
 		}
 
 
-		if rv.IsNil() {
-			rv.Set(reflect.MakeMap(rt))
-		}
 		ktype, vtype := rt.Key(), rt.Elem()
 		ktype, vtype := rt.Key(), rt.Elem()
 		for j := 0; j < containerLen; j++ {
 		for j := 0; j < containerLen; j++ {
 			rvk := reflect.New(ktype).Elem()
 			rvk := reflect.New(ktype).Elem()
@@ -610,3 +613,4 @@ func (z *bytesDecReader) readUint64() uint64 {
 func decErr(format string, params ...interface{}) {
 func decErr(format string, params ...interface{}) {
 	doPanic(msgTagDec, format, params...)
 	doPanic(msgTagDec, format, params...)
 }
 }
+

+ 9 - 6
codec/msgpack.go

@@ -516,7 +516,7 @@ func (d *msgpackDecDriver) decodeBytes(bs []byte) (bsOut []byte, changed bool) {
 	if clen > 0 {
 	if clen > 0 {
 		// if no contents in stream, don't update the passed byteslice
 		// if no contents in stream, don't update the passed byteslice
 		if len(bs) != clen {
 		if len(bs) != clen {
-			// Return changed=true if length of passed slice is different from length of bytes in the stream.
+			// Return changed=true if length of passed slice diff from length of bytes in stream
 			if len(bs) > clen {
 			if len(bs) > clen {
 				bs = bs[:clen]
 				bs = bs[:clen]
 			} else {
 			} else {
@@ -597,7 +597,8 @@ func (d *msgpackDecDriver) readExtLen() (clen int) {
 }
 }
 
 
 func (d *msgpackDecDriver) decodeExt(tag byte) (xbs []byte) {
 func (d *msgpackDecDriver) decodeExt(tag byte) (xbs []byte) {
-	// if (d.bd >= mpXv4Fixext0 && d.bd <= mpXv4Fixext5) || (d.bd >= mpXv4Ext8m && d.bd <= mpXv4Ext32) {
+	// if (d.bd >= mpXv4Fixext0 && d.bd <= mpXv4Fixext5) || 
+	// (d.bd >= mpXv4Ext8m && d.bd <= mpXv4Ext32) {
 	xbd := d.bd
 	xbd := d.bd
 	switch {
 	switch {
 	case xbd >= mpXv4Fixext0 && xbd <= mpXv4Fixext5, xbd >= mpXv4Ext8m && xbd <= mpXv4Ext32:
 	case xbd >= mpXv4Fixext0 && xbd <= mpXv4Fixext5, xbd >= mpXv4Ext8m && xbd <= mpXv4Ext32:
@@ -688,7 +689,7 @@ func (c msgpackSpecRpcCodec) writeCustomBody(typeByte byte, msgid uint64, method
 
 
 //--------------------------------------------------
 //--------------------------------------------------
 
 
-// EncodeBinaryExt returns the underlying bytes of this value AS-IS.
+// BinaryEncodeExt returns the underlying bytes of this value AS-IS.
 // Configure this to support the Binary Extension using tag 0.
 // Configure this to support the Binary Extension using tag 0.
 func (_ *MsgpackHandle) BinaryEncodeExt(rv reflect.Value) ([]byte, error) {
 func (_ *MsgpackHandle) BinaryEncodeExt(rv reflect.Value) ([]byte, error) {
 	if rv.IsNil() {
 	if rv.IsNil() {
@@ -697,20 +698,22 @@ func (_ *MsgpackHandle) BinaryEncodeExt(rv reflect.Value) ([]byte, error) {
 	return rv.Bytes(), nil
 	return rv.Bytes(), nil
 }
 }
 
 
-// DecodeBinaryExt sets passed byte array AS-IS into the reflect Value.
+// BinaryDecodeExt sets passed byte slice AS-IS into the reflect Value.
 // Configure this to support the Binary Extension using tag 0.
 // Configure this to support the Binary Extension using tag 0.
 func (_ *MsgpackHandle) BinaryDecodeExt(rv reflect.Value, bs []byte) (err error) {
 func (_ *MsgpackHandle) BinaryDecodeExt(rv reflect.Value, bs []byte) (err error) {
 	rv.SetBytes(bs)
 	rv.SetBytes(bs)
 	return
 	return
 }
 }
 
 
-// EncodeBinaryExt returns the underlying bytes of this value AS-IS.
-// Configure this to support the Binary Extension using tag 0.
+// TimeEncodeExt encodes a time.Time as a byte slice.
+// Configure this to support the Time Extension, e.g. using tag 1.
 func (_ *MsgpackHandle) TimeEncodeExt(rv reflect.Value) (bs []byte, err error) {
 func (_ *MsgpackHandle) TimeEncodeExt(rv reflect.Value) (bs []byte, err error) {
 	bs = encodeTime(rv.Interface().(time.Time))
 	bs = encodeTime(rv.Interface().(time.Time))
 	return
 	return
 }
 }
 
 
+// TimeDecodeExt decodes a time.Time from the byte slice parameter, and sets it into the reflect value.
+// Configure this to support the Time Extension, e.g. using tag 1.
 func (_ *MsgpackHandle) TimeDecodeExt(rv reflect.Value, bs []byte) (err error) {
 func (_ *MsgpackHandle) TimeDecodeExt(rv reflect.Value, bs []byte) (err error) {
 	tt, err := decodeTime(bs)
 	tt, err := decodeTime(bs)
 	if err == nil {
 	if err == nil {

+ 22 - 34
codec/msgpack_test.py

@@ -8,7 +8,7 @@ import msgpack, sys, os
 
 
 def get_test_data_list():
 def get_test_data_list():
     # get list with all primitive types, and a combo type
     # get list with all primitive types, and a combo type
-    l = [ 
+    l0 = [ 
         -8,
         -8,
          -1616,
          -1616,
          -32323232,
          -32323232,
@@ -25,43 +25,31 @@ def get_test_data_list():
          False,
          False,
          True,
          True,
          None,
          None,
-         1328148122000002,
          "someday",
          "someday",
          "",
          "",
          "bytestring",
          "bytestring",
-         [ 
-            -8,
-             -1616,
-             -32323232,
-             -6464646464646464,
-             192,
-             1616,
-             32323232,
-             6464646464646464,
-             192,
-             -3232.0,
-             -6464646464.0,
-             3232.0,
-             6464646464.0,
-             False,
-             True,
-             None,
-             1328148122000002,
-             "someday",
-             "",
-             "bytestring" 
-             ],
-         { "true": True,
-           "false": False },
-         { "true": "True",
-           "false": False,
-           "uint16(1616)": 1616 },
-         { "list": [1616, 32323232, True, -3232.0, {"TRUE":True, "FALSE":False}, [True, False] ],
-           "int32":32323232, "bool": True, 
-           "LONG STRING": "123456789012345678901234567890123456789012345678901234567890",
-           "SHORT STRING": "1234567890" },	
-	 { True: "true", 8: False, "false": 0 }
+         1328176922000002000,
+         -2206187877999998000,
+         0,
+         -6795364578871345152
          ]
          ]
+    l1 = [
+        { "true": True,
+          "false": False },
+        { "true": "True",
+          "false": False,
+          "uint16(1616)": 1616 },
+        { "list": [1616, 32323232, True, -3232.0, {"TRUE":True, "FALSE":False}, [True, False] ],
+          "int32":32323232, "bool": True, 
+          "LONG STRING": "123456789012345678901234567890123456789012345678901234567890",
+          "SHORT STRING": "1234567890" },	
+        { True: "true", 8: False, "false": 0 }
+        ]
+    
+    l = []
+    l.extend(l0)
+    l.append(l0)
+    l.extend(l1)
     return l
     return l
 
 
 def build_test_data(destdir):
 def build_test_data(destdir):

+ 10 - 0
codec/time.go

@@ -7,6 +7,11 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+var (
+	timeBs0xff = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+	digits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
+)
+
 // encodeTime encodes a time.Time as a []byte, including
 // encodeTime encodes a time.Time as a []byte, including
 // information on the instant in time and UTC offset.
 // information on the instant in time and UTC offset.
 func encodeTime(t time.Time) []byte {
 func encodeTime(t time.Time) []byte {
@@ -72,6 +77,11 @@ func decodeTime(bs []byte) (tt time.Time, err error) {
 		n = ((bd >> 2) & 0x7) + 1
 		n = ((bd >> 2) & 0x7) + 1
 		i2 = i + n
 		i2 = i + n
 		copy(btmp[8-n:], bs[i:i2])
 		copy(btmp[8-n:], bs[i:i2])
+		//if first bit of bs[i] is set, then fill btmp[0..8-n] with 0xff (ie sign extend it)
+		if bs[i] & (1 << 7) != 0 {
+			copy(btmp[0:8-n], timeBs0xff)
+			//for j,k := byte(0), 8-n; j < k; j++ {	btmp[j] = 0xff }
+		}
 		i = i2
 		i = i2
 		tsec = int64(bigen.Uint64(btmp[:]))
 		tsec = int64(bigen.Uint64(btmp[:]))
 	}
 	}