Browse Source

codec: fix encode/decode struct to array. Other misc fixes.

- Use pointers now that size of structFieldInfos has grown.
- Use unsorted field names consistently.
- Fix up test initialization

Update #13 .
Ugorji Nwoke 12 years ago
parent
commit
5765c90963
6 changed files with 56 additions and 36 deletions
  1. 4 7
      codec/bench_test.go
  2. 11 4
      codec/codecs_test.go
  3. 11 5
      codec/decode.go
  4. 6 6
      codec/encode.go
  5. 18 12
      codec/helper.go
  6. 6 2
      codec/z_helper_test.go

+ 4 - 7
codec/bench_test.go

@@ -31,7 +31,6 @@ var (
 	//For depth>1, we likely trigger stack growth for encoders, making benchmarking unreliable.
 	//For depth>1, we likely trigger stack growth for encoders, making benchmarking unreliable.
 	benchDepth     int
 	benchDepth     int
 	benchInitDebug bool
 	benchInitDebug bool
-	benchInitChan  = make(chan bool, 1)
 	benchCheckers  []benchChecker
 	benchCheckers  []benchChecker
 )
 )
 
 
@@ -43,14 +42,15 @@ type benchChecker struct {
 	decodefn benchDecFn
 	decodefn benchDecFn
 }
 }
 
 
-func init() {
+func benchInitFlags() {
 	flag.BoolVar(&benchInitDebug, "bdbg", false, "Bench Debug")
 	flag.BoolVar(&benchInitDebug, "bdbg", false, "Bench Debug")
 	flag.IntVar(&benchDepth, "bd", 1, "Bench Depth: If >1, potential unreliable results due to stack growth")
 	flag.IntVar(&benchDepth, "bd", 1, "Bench Depth: If >1, potential unreliable results due to stack growth")
 	flag.BoolVar(&benchDoInitBench, "bi", false, "Run Bench Init")
 	flag.BoolVar(&benchDoInitBench, "bi", false, "Run Bench Init")
 	flag.BoolVar(&benchVerify, "bv", false, "Verify Decoded Value during Benchmark")
 	flag.BoolVar(&benchVerify, "bv", false, "Verify Decoded Value during Benchmark")
 	flag.BoolVar(&benchUnscientificRes, "bu", false, "Show Unscientific Results during Benchmark")
 	flag.BoolVar(&benchUnscientificRes, "bu", false, "Show Unscientific Results during Benchmark")
-	flag.Parse()
+}
 
 
+func benchInit() {	
 	benchTs = newTestStruc(benchDepth, true)
 	benchTs = newTestStruc(benchDepth, true)
 	approxSize = approxDataSize(reflect.ValueOf(benchTs))
 	approxSize = approxDataSize(reflect.ValueOf(benchTs))
 	bytesLen := 1024 * 4 * (benchDepth + 1) * (benchDepth + 1)
 	bytesLen := 1024 * 4 * (benchDepth + 1) * (benchDepth + 1)
@@ -65,10 +65,7 @@ func init() {
 		benchChecker{"json", fnJsonEncodeFn, fnJsonDecodeFn},
 		benchChecker{"json", fnJsonEncodeFn, fnJsonDecodeFn},
 	)
 	)
 	if benchDoInitBench {
 	if benchDoInitBench {
-		go func() {
-			<-benchInitChan
-			runBenchInit()
-		}()
+		runBenchInit()
 	}
 	}
 }
 }
 
 

+ 11 - 4
codec/codecs_test.go

@@ -48,8 +48,9 @@ const (
 )
 )
 
 
 var (
 var (
-	testInitDebug   bool
-	testUseIoEncDec bool
+	testInitDebug     bool
+	testUseIoEncDec   bool
+	testStructToArray bool
 	_                           = fmt.Printf
 	_                           = fmt.Printf
 	skipVerifyVal   interface{} = &(struct{}{})
 	skipVerifyVal   interface{} = &(struct{}{})
 	timeLoc                     = time.FixedZone("UTC-08:00", -8*60*60)         //time.UTC-8
 	timeLoc                     = time.FixedZone("UTC-08:00", -8*60*60)         //time.UTC-8
@@ -68,17 +69,22 @@ var (
 	testBincH    = &BincHandle{}
 	testBincH    = &BincHandle{}
 )
 )
 
 
-func init() {
+func testInitFlags() {
 	// delete(testDecOpts.ExtFuncs, timeTyp)
 	// delete(testDecOpts.ExtFuncs, timeTyp)
 	flag.BoolVar(&testInitDebug, "tdbg", false, "Test Debug")
 	flag.BoolVar(&testInitDebug, "tdbg", false, "Test Debug")
 	flag.BoolVar(&testUseIoEncDec, "tio", false, "Use IO Reader/Writer for Marshal/Unmarshal")
 	flag.BoolVar(&testUseIoEncDec, "tio", false, "Use IO Reader/Writer for Marshal/Unmarshal")
-	flag.Parse()
+	flag.BoolVar(&testStructToArray, "ts2a", false, "Set StructToArray option")
+}	
+
+func testInit() {	
 	gob.Register(new(TestStruc))
 	gob.Register(new(TestStruc))
 	if testInitDebug {
 	if testInitDebug {
 		ts0 := newTestStruc(2, false)
 		ts0 := newTestStruc(2, false)
 		fmt.Printf("====> depth: %v, ts: %#v\n", 2, ts0)
 		fmt.Printf("====> depth: %v, ts: %#v\n", 2, ts0)
 	}
 	}
 
 
+	testBincH.StructToArray = testStructToArray
+	testMsgpackH.StructToArray = testStructToArray
 	testMsgpackH.RawToString = true 
 	testMsgpackH.RawToString = true 
 	//testMsgpackH.AddExt(byteSliceTyp, 0, testMsgpackH.BinaryEncodeExt, testMsgpackH.BinaryDecodeExt)
 	//testMsgpackH.AddExt(byteSliceTyp, 0, testMsgpackH.BinaryEncodeExt, testMsgpackH.BinaryDecodeExt)
 	testMsgpackH.AddExt(timeTyp, 1, testMsgpackH.TimeEncodeExt, testMsgpackH.TimeDecodeExt)
 	testMsgpackH.AddExt(timeTyp, 1, testMsgpackH.TimeEncodeExt, testMsgpackH.TimeDecodeExt)
@@ -851,6 +857,7 @@ func TestBincCodecsMisc(t *testing.T) {
 func TestMsgpackRpcGo(t *testing.T) {
 func TestMsgpackRpcGo(t *testing.T) {
 	doTestRpcOne(t, GoRpc, testMsgpackH, true, true, true)
 	doTestRpcOne(t, GoRpc, testMsgpackH, true, true, true)
 }
 }
+
 func TestMsgpackRpcSpec(t *testing.T) {
 func TestMsgpackRpcSpec(t *testing.T) {
 	doTestRpcOne(t, MsgpackSpecRpc, testMsgpackH, true, true, true)
 	doTestRpcOne(t, MsgpackSpecRpc, testMsgpackH, true, true, true)
 }
 }

+ 11 - 5
codec/decode.go

@@ -425,6 +425,7 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 				break
 				break
 			}
 			}
 			sfi := getStructFieldInfos(rtid, rt)
 			sfi := getStructFieldInfos(rtid, rt)
+			sissis := sfi.sis 
 			for j := 0; j < containerLen; j++ {
 			for j := 0; j < containerLen; j++ {
 				// var rvkencname string
 				// var rvkencname string
 				// ddecode(&rvkencname)
 				// ddecode(&rvkencname)
@@ -432,8 +433,8 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 				rvkencname := dd.decodeString()
 				rvkencname := dd.decodeString()
 				// rvksi := sfi.getForEncName(rvkencname)
 				// rvksi := sfi.getForEncName(rvkencname)
 				if k := sfi.indexForEncName(rvkencname); k > -1 {
 				if k := sfi.indexForEncName(rvkencname); k > -1 {
-					sfik := sfi.sis[k]
-					if sfik.i > -1 {
+					sfik := sissis[k]
+					if sfik.i != -1 {
 						d.decodeValue(rv.Field(int(sfik.i)))
 						d.decodeValue(rv.Field(int(sfik.i)))
 					} else {
 					} else {
 						d.decodeValue(rv.FieldByIndex(sfik.is))
 						d.decodeValue(rv.FieldByIndex(sfik.is))
@@ -449,12 +450,17 @@ func (d *Decoder) decodeValue(rv reflect.Value) {
 				}
 				}
 			}
 			}
 		} else if currEncodedType == detArray {
 		} else if currEncodedType == detArray {
-			containerLen := dd.readMapLen()
+			containerLen := dd.readArrayLen()
 			if containerLen == 0 {
 			if containerLen == 0 {
 				break
 				break
 			}
 			}
-			for j := 0; j < containerLen; j++ {
-				d.decodeValue(rv.Field(j))
+			sfi := getStructFieldInfos(rtid, rt)
+			for _, si := range sfi.sisp {
+				if si.i != -1 {
+					d.decodeValue(rv.Field(int(si.i)))
+				} else {
+					d.decodeValue(rv.FieldByIndex(si.is))
+				}
 			}
 			}
 		} else {
 		} else {
 			decErr("Only encoded map or array can be decoded into a struct")
 			decErr("Only encoded map or array can be decoded into a struct")

+ 6 - 6
codec/encode.go

@@ -447,19 +447,19 @@ func (e *Encoder) encodeValue(rv reflect.Value) {
 	return
 	return
 }
 }
 
 
-func (e *Encoder) encStruct(sis structFieldInfos, rv reflect.Value) {
+func (e *Encoder) encStruct(sis *structFieldInfos, rv reflect.Value) {
 	newlen := len(sis.sis)
 	newlen := len(sis.sis)
 	rvals := make([]reflect.Value, newlen)
 	rvals := make([]reflect.Value, newlen)
 	var encnames []string
 	var encnames []string
 	toMap := !(sis.toArray || e.h.structToArray())
 	toMap := !(sis.toArray || e.h.structToArray())
+	sissis := sis.sisp
+	// if toMap, use the sorted array. If toArray, use unsorted array (to match sequence in struct)
 	if toMap {
 	if toMap {
+		sissis = sis.sis
 		encnames = make([]string, newlen)
 		encnames = make([]string, newlen)
 	}
 	}
 	newlen = 0
 	newlen = 0
-	// var rv0 reflect.Value
-	// for i := 0; i < l; i++ {
-	// 	si := sis[i]
-	for _, si := range sis.sis {
+	for _, si := range sissis {
 		if si.i != -1 {
 		if si.i != -1 {
 			rvals[newlen] = rv.Field(int(si.i))
 			rvals[newlen] = rv.Field(int(si.i))
 		} else {
 		} else {
@@ -472,7 +472,7 @@ func (e *Encoder) encStruct(sis structFieldInfos, rv reflect.Value) {
 			encnames[newlen] = si.encName
 			encnames[newlen] = si.encName
 		} else {
 		} else {
 			if si.omitEmpty && isEmptyValue(rvals[newlen]) {
 			if si.omitEmpty && isEmptyValue(rvals[newlen]) {
-				rvals[newlen] = reflect.Value{}
+				rvals[newlen] = reflect.Value{} //encode as nil
 			}
 			}
 		}
 		}
 		newlen++
 		newlen++

+ 18 - 12
codec/helper.go

@@ -41,7 +41,7 @@ var (
 	bigen               = binary.BigEndian
 	bigen               = binary.BigEndian
 	structInfoFieldName = "_struct"
 	structInfoFieldName = "_struct"
 
 
-	cachedStructFieldInfos      = make(map[uintptr]structFieldInfos, 4)
+	cachedStructFieldInfos      = make(map[uintptr]*structFieldInfos, 4)
 	cachedStructFieldInfosMutex sync.RWMutex
 	cachedStructFieldInfosMutex sync.RWMutex
 
 
 	nilIntfSlice     = []interface{}(nil)
 	nilIntfSlice     = []interface{}(nil)
@@ -107,7 +107,8 @@ type structFieldInfo struct {
 
 
 type structFieldInfos struct {
 type structFieldInfos struct {
 	toArray bool 
 	toArray bool 
-	sis     []structFieldInfo
+	sis     []structFieldInfo // sorted. Used when enc/dec struct to map.
+	sisp    []structFieldInfo // unsorted. Used when enc/dec struct to array.
 }
 }
 
 
 type sfiSortedByEncName []*structFieldInfo
 type sfiSortedByEncName []*structFieldInfo
@@ -124,12 +125,13 @@ func (p sfiSortedByEncName) Swap(i, j int) {
 	p[i], p[j] = p[j], p[i]
 	p[i], p[j] = p[j], p[i]
 }
 }
 
 
-func (sis structFieldInfos) indexForEncName(name string) int {
-	sislen := len(sis.sis)
+func (sis *structFieldInfos) indexForEncName(name string) int {
+	sissis := sis.sis 
+	sislen := len(sissis)
 	if sislen < binarySearchThreshold {
 	if sislen < binarySearchThreshold {
 		// linear search. faster than binary search in my testing up to 16-field structs.
 		// linear search. faster than binary search in my testing up to 16-field structs.
 		for i := 0; i < sislen; i++ {
 		for i := 0; i < sislen; i++ {
-			if sis.sis[i].encName == name {
+			if sissis[i].encName == name {
 				return i
 				return i
 			}
 			}
 		}
 		}
@@ -139,20 +141,20 @@ func (sis structFieldInfos) indexForEncName(name string) int {
 		for i < j {
 		for i < j {
 			h = i + (j-i)/2
 			h = i + (j-i)/2
 			// i ≤ h < j
 			// i ≤ h < j
-			if sis.sis[h].encName < name {
+			if sissis[h].encName < name {
 				i = h + 1 // preserves f(i-1) == false
 				i = h + 1 // preserves f(i-1) == false
 			} else {
 			} else {
 				j = h // preserves f(j) == true
 				j = h // preserves f(j) == true
 			}
 			}
 		}
 		}
-		if i < sislen && sis.sis[i].encName == name {
+		if i < sislen && sissis[i].encName == name {
 			return i
 			return i
 		}
 		}
 	}
 	}
 	return -1
 	return -1
 }
 }
 
 
-func getStructFieldInfos(rtid uintptr, rt reflect.Type) (sis structFieldInfos) {
+func getStructFieldInfos(rtid uintptr, rt reflect.Type) (sis *structFieldInfos) {
 	var ok bool
 	var ok bool
 	cachedStructFieldInfosMutex.RLock()
 	cachedStructFieldInfosMutex.RLock()
 	sis, ok = cachedStructFieldInfos[rtid]
 	sis, ok = cachedStructFieldInfos[rtid]
@@ -167,6 +169,7 @@ func getStructFieldInfos(rtid uintptr, rt reflect.Type) (sis structFieldInfos) {
 		return
 		return
 	}
 	}
 
 
+	sis = new(structFieldInfos)
 	var siInfo *structFieldInfo
 	var siInfo *structFieldInfo
 	if f, ok := rt.FieldByName(structInfoFieldName); ok {
 	if f, ok := rt.FieldByName(structInfoFieldName); ok {
 		siInfo = parseStructFieldInfo(structInfoFieldName, f.Tag.Get(structTagName))
 		siInfo = parseStructFieldInfo(structInfoFieldName, f.Tag.Get(structTagName))
@@ -174,11 +177,14 @@ func getStructFieldInfos(rtid uintptr, rt reflect.Type) (sis structFieldInfos) {
 	}
 	}
 	sisp := make([]*structFieldInfo, 0, rt.NumField())
 	sisp := make([]*structFieldInfo, 0, rt.NumField())
 	rgetStructFieldInfos(rt, nil, make(map[string]bool), &sisp, siInfo)
 	rgetStructFieldInfos(rt, nil, make(map[string]bool), &sisp, siInfo)
+	sis.sisp = make([]structFieldInfo, len(sisp))
+	sis.sis = make([]structFieldInfo, len(sisp))
+	for i := 0; i < len(sisp); i++ {
+		sis.sisp[i] = *sisp[i]
+	}
+	
 	sort.Sort(sfiSortedByEncName(sisp))
 	sort.Sort(sfiSortedByEncName(sisp))
-
-	lsis := len(sisp)	
-	sis.sis = make([]structFieldInfo, lsis)
-	for i := 0; i < lsis; i++ {
+	for i := 0; i < len(sisp); i++ {
 		sis.sis[i] = *sisp[i]
 		sis.sis[i] = *sisp[i]
 	}
 	}
 	// sis = sisp
 	// sis = sisp

+ 6 - 2
codec/z_helper_test.go

@@ -12,6 +12,7 @@ package codec
 import (
 import (
 	"errors"
 	"errors"
 	"reflect"
 	"reflect"
+	"flag"
 	"testing"
 	"testing"
 )
 )
 
 
@@ -20,9 +21,12 @@ var (
 	failNowOnFail = true
 	failNowOnFail = true
 )
 )
 
 
-// does final initialization
 func init() {
 func init() {
-	close(benchInitChan)
+	testInitFlags()
+	benchInitFlags()
+	flag.Parse()
+	testInit()
+	benchInit()
 }
 }
 
 
 func checkErrT(t *testing.T, err error) {
 func checkErrT(t *testing.T, err error) {