Ver código fonte

codec: fully support encoding/decoding from/to chan type.

Also, fix issue where codegeneration skips multiple types defined in a single type ( ... ) block.
Ugorji Nwoke 11 anos atrás
pai
commit
b9bc36280d

+ 1 - 1
README.md

@@ -10,7 +10,7 @@ a High Performance and Feature-Rich Idiomatic encode/decode and rpc library for
 
 For more information:
 
-  - [see the Readme on github](https://github.com/ugorji/go/tree/master/codec#readme)
+  - [see the codec/Readme for quick usage information](https://github.com/ugorji/go/tree/master/codec#readme)
   - [view the API on godoc](http://godoc.org/github.com/ugorji/go/codec)
   - [read the detailed usage/how-to primer](http://ugorji.net/blog/go-codec-primer)
 

+ 3 - 1
codec/0doc.go

@@ -56,11 +56,13 @@ Rich Feature Set includes:
   - Fast (no-reflection) encoding/decoding of common maps and slices
   - Code-generation for faster performance.
   - Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
-  - Support indefinite-length formats to enable true streaming
+  - Support indefinite-length formats to enable true streaming 
+    (for formats which support it e.g. json, cbor)
   - NIL in data stream decoded as zero value
   - Never silently skip data when decoding.
     User decides whether to return an error or silently skip data when keys or indexes
     in the data stream do not map to fields in the struct.
+  - Encode/Decode from/to chan types (for iterative streaming support)
   - Provides a RPC Server and Client Codec for net/rpc communication protocol.
   - Handle unique idiosynchracies of codecs e.g. 
     - For messagepack, configure how ambiguities in handling raw bytes are resolved 

+ 3 - 1
codec/README.md

@@ -57,11 +57,13 @@ Rich Feature Set includes:
   - Fast (no-reflection) encoding/decoding of common maps and slices
   - Code-generation for faster performance.
   - Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
-  - Support indefinite-length formats to enable true streaming
+  - Support indefinite-length formats to enable true streaming 
+    (for formats which support it e.g. json, cbor)
   - NIL in data stream decoded as zero value
   - Never silently skip data when decoding.
     User decides whether to return an error or silently skip data when keys or indexes
     in the data stream do not map to fields in the struct.
+  - Encode/Decode from/to chan types (for iterative streaming support)
   - Provides a RPC Server and Client Codec for net/rpc communication protocol.
   - Handle unique idiosynchracies of codecs e.g. 
     - For messagepack, configure how ambiguities in handling raw bytes are resolved 

+ 45 - 0
codec/codec_test.go

@@ -711,6 +711,43 @@ func testCodecUnderlyingType(t *testing.T, h Handle) {
 	}
 }
 
+func testCodecChan(t *testing.T, h Handle) {
+	// - send a slice []*int64 (sl1) into an chan (ch1) with cap > len(s1)
+	// - encode ch1 as a stream array
+	// - decode a chan (ch2), with cap > len(s1) from the stream array
+	// - receive from ch2 into slice sl2
+	// - compare sl1 and sl2
+	// - do this for codecs: json, cbor (covers all types)
+	sl1 := make([]*int64, 4)
+	for i := range sl1 {
+		var j int64 = int64(i)
+		sl1[i] = &j
+	}
+	ch1 := make(chan *int64, 4)
+	for _, j := range sl1 {
+		ch1 <- j
+	}
+	var bs []byte
+	NewEncoderBytes(&bs, h).MustEncode(ch1)
+	// if !h.isBinary() {
+	// 	fmt.Printf("before: len(ch1): %v, bs: %s\n", len(ch1), bs)
+	// }
+	// var ch2 chan *int64 // this will block if json, etc.
+	ch2 := make(chan *int64, 8)
+	NewDecoderBytes(bs, h).MustDecode(&ch2)
+	// logT(t, "Len(ch2): %v", len(ch2))
+	// fmt.Printf("after:  len(ch2): %v, ch2: %v\n", len(ch2), ch2)
+	close(ch2)
+	var sl2 []*int64
+	for j := range ch2 {
+		sl2 = append(sl2, j)
+	}
+	if err := deepEqual(sl1, sl2); err != nil {
+		logT(t, "Not Match: %v; len: %v, %v", err, len(sl1), len(sl2))
+		failT(t)
+	}
+}
+
 func testCodecRpcOne(t *testing.T, rr Rpc, h Handle, doRequest bool, exitSleepMs time.Duration,
 ) (port int) {
 	testOnce.Do(testInitAll)
@@ -1012,6 +1049,14 @@ func TestJsonCodecsEmbeddedPointer(t *testing.T) {
 	testCodecEmbeddedPointer(t, testJsonH)
 }
 
+func TestJsonCodecChan(t *testing.T) {
+	testCodecChan(t, testJsonH)
+}
+
+func TestCborCodecChan(t *testing.T) {
+	testCodecChan(t, testCborH)
+}
+
 // ----- RPC -----
 
 func TestBincRpcGo(t *testing.T) {

+ 5 - 4
codec/codecgen/gen.go

@@ -134,8 +134,8 @@ func Generate(outfile, buildTag, codecPkgPath string, useUnsafe bool, goRunTag s
 	for _, f := range astfiles {
 		for _, d := range f.Decls {
 			if gd, ok := d.(*ast.GenDecl); ok {
-				if len(gd.Specs) == 1 {
-					if td, ok := gd.Specs[0].(*ast.TypeSpec); ok {
+				for _, dd := range gd.Specs {
+					if td, ok := dd.(*ast.TypeSpec); ok {
 						if len(td.Name.Name) == 0 || td.Name.Name[0] > 'Z' || td.Name.Name[0] < 'A' {
 							continue
 						}
@@ -145,10 +145,11 @@ func Generate(outfile, buildTag, codecPkgPath string, useUnsafe bool, goRunTag s
 						//   primitives (numbers, bool, string): Ident
 						//   map: MapType
 						//   slice, array: ArrayType
+						//   chan: ChanType
 						// do not generate:
-						//   ChanType, FuncType, InterfaceType, StarExpr (ptr), etc
+						//   FuncType, InterfaceType, StarExpr (ptr), etc
 						switch td.Type.(type) {
-						case *ast.StructType, *ast.Ident, *ast.MapType, *ast.ArrayType:
+						case *ast.StructType, *ast.Ident, *ast.MapType, *ast.ArrayType, *ast.ChanType:
 							if regexName.FindStringIndex(td.Name.Name) != nil {
 								tv.Types = append(tv.Types, td.Name.Name)
 							}

+ 75 - 43
codec/decode.go

@@ -300,7 +300,7 @@ type decFnInfoX struct {
 	ti    *typeInfo
 	xfFn  Ext
 	xfTag uint64
-	array bool
+	seq   seqType
 }
 
 // decFnInfo has methods for handling decoding of a specific type
@@ -651,35 +651,52 @@ func (f decFnInfo) kSlice(rv reflect.Value) {
 	d := f.d
 	if f.dd.IsContainerType(valueTypeBytes) || f.dd.IsContainerType(valueTypeString) {
 		if ti.rtid == uint8SliceTypId || ti.rt.Elem().Kind() == reflect.Uint8 {
-			rvbs := rv.Bytes()
-			bs2 := f.dd.DecodeBytes(rvbs, false, false)
-			if rvbs == nil && bs2 != nil || rvbs != nil && bs2 == nil || len(bs2) != len(rvbs) {
-				if rv.CanSet() {
-					rv.SetBytes(bs2)
-				} else {
-					copy(rvbs, bs2)
+			if f.seq == seqTypeChan {
+				bs2 := f.dd.DecodeBytes(nil, false, true)
+				ch := rv.Interface().(chan<- byte)
+				for _, b := range bs2 {
+					ch <- b
+				}
+			} else {
+				rvbs := rv.Bytes()
+				bs2 := f.dd.DecodeBytes(rvbs, false, false)
+				if rvbs == nil && bs2 != nil || rvbs != nil && bs2 == nil || len(bs2) != len(rvbs) {
+					if rv.CanSet() {
+						rv.SetBytes(bs2)
+					} else {
+						copy(rvbs, bs2)
+					}
 				}
 			}
 			return
 		}
 	}
 
-	array := f.array
+	// array := f.seq == seqTypeChan
 
 	slh, containerLenS := d.decSliceHelperStart()
 
 	// an array can never return a nil slice. so no need to check f.array here.
 	if rv.IsNil() {
-		if containerLenS <= 0 {
-			rv.Set(reflect.MakeSlice(ti.rt, 0, 0))
-		} else {
-			rv.Set(reflect.MakeSlice(ti.rt, containerLenS, containerLenS))
+		// either chan or slice
+		if f.seq == seqTypeSlice {
+			if containerLenS <= 0 {
+				rv.Set(reflect.MakeSlice(ti.rt, 0, 0))
+			} else {
+				rv.Set(reflect.MakeSlice(ti.rt, containerLenS, containerLenS))
+			}
+		} else if f.seq == seqTypeChan {
+			if containerLenS <= 0 {
+				rv.Set(reflect.MakeChan(ti.rt, 0))
+			} else {
+				rv.Set(reflect.MakeChan(ti.rt, containerLenS))
+			}
 		}
 	}
 
 	rvlen := rv.Len()
 	if containerLenS == 0 {
-		if !array && rvlen != 0 {
+		if f.seq == seqTypeSlice && rvlen != 0 {
 			rv.SetLen(0)
 		}
 		// slh.End() // f.dd.ReadArrayEnd()
@@ -702,32 +719,41 @@ func (f decFnInfo) kSlice(rv reflect.Value) {
 
 	hasLen := containerLenS >= 0
 	if hasLen {
-		numToRead := containerLenS
-		if containerLenS > rvcap {
-			if array {
-				d.arrayCannotExpand(rv.Len(), containerLenS)
-				numToRead = rvlen
-			} else {
-				rv = reflect.MakeSlice(ti.rt, containerLenS, containerLenS)
-				if rvlen > 0 && !isMutableKind(ti.rt.Kind()) {
-					rv1 := rv0
-					rv1.SetLen(rvcap)
-					reflect.Copy(rv, rv1)
+		if f.seq == seqTypeChan {
+			// handle chan specially:
+			for j := 0; j < containerLenS; j++ {
+				rv0 := reflect.New(rtelem0).Elem()
+				d.decodeValue(rv0, fn)
+				rv.Send(rv0)
+			}
+		} else {
+			numToRead := containerLenS
+			if containerLenS > rvcap {
+				if f.seq == seqTypeArray {
+					d.arrayCannotExpand(rv.Len(), containerLenS)
+					numToRead = rvlen
+				} else {
+					rv = reflect.MakeSlice(ti.rt, containerLenS, containerLenS)
+					if rvlen > 0 && !isMutableKind(ti.rt.Kind()) {
+						rv1 := rv0
+						rv1.SetLen(rvcap)
+						reflect.Copy(rv, rv1)
+					}
+					rvChanged = true
+					rvlen = containerLenS
 				}
-				rvChanged = true
+			} else if containerLenS != rvlen {
+				rv.SetLen(containerLenS)
 				rvlen = containerLenS
 			}
-		} else if containerLenS != rvlen {
-			rv.SetLen(containerLenS)
-			rvlen = containerLenS
-		}
-		j := 0
-		for ; j < numToRead; j++ {
-			d.decodeValue(rv.Index(j), fn)
-		}
-		if array {
-			for ; j < containerLenS; j++ {
-				d.swallow()
+			j := 0
+			for ; j < numToRead; j++ {
+				d.decodeValue(rv.Index(j), fn)
+			}
+			if f.seq == seqTypeArray {
+				for ; j < containerLenS; j++ {
+					d.swallow()
+				}
 			}
 		}
 	} else {
@@ -735,10 +761,10 @@ func (f decFnInfo) kSlice(rv reflect.Value) {
 			var decodeIntoBlank bool
 			// if indefinite, etc, then expand the slice if necessary
 			if j >= rvlen {
-				if array {
+				if f.seq == seqTypeArray {
 					d.arrayCannotExpand(rvlen, j+1)
 					decodeIntoBlank = true
-				} else {
+				} else if f.seq == seqTypeSlice {
 					rv = reflect.Append(rv, reflect.Zero(rtelem0))
 					rvlen++
 					rvChanged = true
@@ -747,7 +773,11 @@ func (f decFnInfo) kSlice(rv reflect.Value) {
 			if j > 0 {
 				slh.Sep(j)
 			}
-			if decodeIntoBlank {
+			if f.seq == seqTypeChan {
+				rv0 := reflect.New(rtelem0).Elem()
+				d.decodeValue(rv0, fn)
+				rv.Send(rv0)
+			} else if decodeIntoBlank {
 				d.swallow()
 			} else {
 				d.decodeValue(rv.Index(j), fn)
@@ -1305,13 +1335,15 @@ func (d *Decoder) getDecFn(rt reflect.Type, checkAll bool) (fn decFn) {
 			case reflect.Struct:
 				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
 				fn.f = (decFnInfo).kStruct
+			case reflect.Chan:
+				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti, seq: seqTypeChan}
+				fn.f = (decFnInfo).kSlice
 			case reflect.Slice:
-				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
+				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti, seq: seqTypeSlice}
 				fn.f = (decFnInfo).kSlice
 			case reflect.Array:
 				// fi.decFnInfoX = &decFnInfoX{array: true}
-				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
-				fi.array = true
+				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti, seq: seqTypeArray}
 				fn.f = (decFnInfo).kArray
 			case reflect.Map:
 				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}

+ 44 - 11
codec/encode.go

@@ -252,7 +252,7 @@ type encFnInfoX struct {
 	ti    *typeInfo
 	xfFn  Ext
 	xfTag uint64
-	array bool
+	seq   seqType
 }
 
 type encFnInfo struct {
@@ -374,7 +374,7 @@ func (f encFnInfo) kSlice(rv reflect.Value) {
 	//   (don't call rv.Bytes, rv.Slice, etc).
 	// E.g. type struct S{B [2]byte};
 	//   Encode(S{}) will bomb on "panic: slice of unaddressable array".
-	if !f.array {
+	if f.seq != seqTypeArray {
 		if rv.IsNil() {
 			f.ee.EncodeNil()
 			return
@@ -389,12 +389,18 @@ func (f encFnInfo) kSlice(rv reflect.Value) {
 	rtelem := ti.rt.Elem()
 	l := rv.Len()
 	if rtelem.Kind() == reflect.Uint8 {
-		if f.array {
+		switch f.seq {
+		case seqTypeArray:
 			// if l == 0 { f.ee.encodeStringBytes(c_RAW, nil) } else
 			if rv.CanAddr() {
 				f.ee.EncodeStringBytes(c_RAW, rv.Slice(0, l).Bytes())
 			} else {
-				bs := make([]byte, l)
+				var bs []byte
+				if l <= cap(f.e.b) {
+					bs = f.e.b[:l]
+				} else {
+					bs = make([]byte, l)
+				}
 				reflect.Copy(reflect.ValueOf(bs), rv)
 				// TODO: Test that reflect.Copy works instead of manual one-by-one
 				// for i := 0; i < l; i++ {
@@ -402,8 +408,20 @@ func (f encFnInfo) kSlice(rv reflect.Value) {
 				// }
 				f.ee.EncodeStringBytes(c_RAW, bs)
 			}
-		} else {
+		case seqTypeSlice:
 			f.ee.EncodeStringBytes(c_RAW, rv.Bytes())
+		case seqTypeChan:
+			bs := f.e.b[:0]
+			// do not use range, so that the number of elements encoded
+			// does not change, and encoding does not hang waiting on someone to close chan.
+			// for b := range rv.Interface().(<-chan byte) {
+			// 	bs = append(bs, b)
+			// }
+			ch := rv.Interface().(<-chan byte)
+			for i := 0; i < l; i++ {
+				bs = append(bs, <-ch)
+			}
+			f.ee.EncodeStringBytes(c_RAW, bs)
 		}
 		return
 	}
@@ -446,11 +464,23 @@ func (f encFnInfo) kSlice(rv reflect.Value) {
 						f.ee.EncodeArrayEntrySeparator()
 					}
 				}
-				e.encodeValue(rv.Index(j), fn)
+				if f.seq == seqTypeChan {
+					if rv2, ok2 := rv.Recv(); ok2 {
+						e.encodeValue(rv2, fn)
+					}
+				} else {
+					e.encodeValue(rv.Index(j), fn)
+				}
 			}
 		} else {
 			for j := 0; j < l; j++ {
-				e.encodeValue(rv.Index(j), fn)
+				if f.seq == seqTypeChan {
+					if rv2, ok2 := rv.Recv(); ok2 {
+						e.encodeValue(rv2, fn)
+					}
+				} else {
+					e.encodeValue(rv.Index(j), fn)
+				}
 			}
 		}
 	}
@@ -739,6 +769,7 @@ type Encoder struct {
 
 	hh Handle
 	f  map[uintptr]encFn
+	b  [scratchByteArrayLen]byte
 }
 
 // NewEncoder returns an Encoder for encoding into an io.Writer.
@@ -970,7 +1001,7 @@ LOOP:
 				e.e.EncodeNil()
 				return
 			}
-		case reflect.Invalid, reflect.Chan, reflect.Func:
+		case reflect.Invalid, reflect.Func:
 			e.e.EncodeNil()
 			return
 		}
@@ -1077,12 +1108,14 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkAll bool) (fn enc
 				fn.f = (encFnInfo).kUint
 			case reflect.Invalid:
 				fn.f = (encFnInfo).kInvalid
+			case reflect.Chan:
+				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti, seq: seqTypeChan}
+				fn.f = (encFnInfo).kSlice
 			case reflect.Slice:
-				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti}
+				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti, seq: seqTypeSlice}
 				fn.f = (encFnInfo).kSlice
 			case reflect.Array:
-				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti, array: true}
-				// fi.array = true
+				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti, seq: seqTypeArray}
 				fn.f = (encFnInfo).kSlice
 			case reflect.Struct:
 				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti}

+ 14 - 14
codec/fast-path.generated.go

@@ -10918,7 +10918,7 @@ func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
 // -- -- fast path functions
 
 func (f decFnInfo) fastpathDecSliceIntfR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]interface{})
 		v, changed := fastpathTV.DecSliceIntfV(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11019,7 +11019,7 @@ func (_ fastpathT) DecSliceIntfV(v []interface{}, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceStringR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]string)
 		v, changed := fastpathTV.DecSliceStringV(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11119,7 +11119,7 @@ func (_ fastpathT) DecSliceStringV(v []string, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceFloat32R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]float32)
 		v, changed := fastpathTV.DecSliceFloat32V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11219,7 +11219,7 @@ func (_ fastpathT) DecSliceFloat32V(v []float32, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceFloat64R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]float64)
 		v, changed := fastpathTV.DecSliceFloat64V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11319,7 +11319,7 @@ func (_ fastpathT) DecSliceFloat64V(v []float64, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceUintR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]uint)
 		v, changed := fastpathTV.DecSliceUintV(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11419,7 +11419,7 @@ func (_ fastpathT) DecSliceUintV(v []uint, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceUint16R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]uint16)
 		v, changed := fastpathTV.DecSliceUint16V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11519,7 +11519,7 @@ func (_ fastpathT) DecSliceUint16V(v []uint16, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceUint32R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]uint32)
 		v, changed := fastpathTV.DecSliceUint32V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11619,7 +11619,7 @@ func (_ fastpathT) DecSliceUint32V(v []uint32, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceUint64R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]uint64)
 		v, changed := fastpathTV.DecSliceUint64V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11719,7 +11719,7 @@ func (_ fastpathT) DecSliceUint64V(v []uint64, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceIntR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int)
 		v, changed := fastpathTV.DecSliceIntV(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11819,7 +11819,7 @@ func (_ fastpathT) DecSliceIntV(v []int, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceInt8R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int8)
 		v, changed := fastpathTV.DecSliceInt8V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -11919,7 +11919,7 @@ func (_ fastpathT) DecSliceInt8V(v []int8, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceInt16R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int16)
 		v, changed := fastpathTV.DecSliceInt16V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -12019,7 +12019,7 @@ func (_ fastpathT) DecSliceInt16V(v []int16, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceInt32R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int32)
 		v, changed := fastpathTV.DecSliceInt32V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -12119,7 +12119,7 @@ func (_ fastpathT) DecSliceInt32V(v []int32, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceInt64R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int64)
 		v, changed := fastpathTV.DecSliceInt64V(*vp, fastpathCheckNilFalse, !array, f.d)
@@ -12219,7 +12219,7 @@ func (_ fastpathT) DecSliceInt64V(v []int64, checkNil bool, canChange bool,
 }
 
 func (f decFnInfo) fastpathDecSliceBoolR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]bool)
 		v, changed := fastpathTV.DecSliceBoolV(*vp, fastpathCheckNilFalse, !array, f.d)

+ 1 - 1
codec/fast-path.go.tmpl

@@ -225,7 +225,7 @@ Slices can change if they
 - are settable (e.g. contained in an interface{})
 */}}
 func (f decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) { 
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported 
 		vp := rv.Addr().Interface().(*[]{{ .Elem }})
 		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, !array, f.d)

+ 23 - 16
codec/gen-dec-array.go.tmpl

@@ -1,26 +1,31 @@
-const {{var "Arr"}} bool = {{ .Array }}
 {{var "v"}} := *{{ .Varname }}
 {{var "h"}}, {{var "l"}} := z.DecSliceHelperStart()
 
 var {{var "c"}} bool 
-{{ if not .Array }}if {{var "v"}} == nil {
+{{ if not isArray }}if {{var "v"}} == nil {
 	if {{var "l"}} <= 0 {
-		{{var "v"}} = []{{ .Typ }}{}
+        {{var "v"}} = make({{ .CTyp }}, 0)
 	} else {
-		{{var "v"}} = make([]{{ .Typ }}, {{var "l"}}, {{var "l"}})
+		{{var "v"}} = make({{ .CTyp }}, {{var "l"}})
 	}
 	{{var "c"}} = true 
 } 
 {{ end }}
-if {{var "l"}} == 0 { {{ if not .Array }}
+if {{var "l"}} == 0 { {{ if isSlice }}
 	if len({{var "v"}}) != 0 { 
 		{{var "v"}} = {{var "v"}}[:0] 
 		{{var "c"}} = true 
 	} {{ end }}
 } else if {{var "l"}} > 0 {
+	{{ if isChan }}
+	for {{var "r"}} := 0; {{var "r"}} < {{var "l"}}; {{var "r"}}++ {
+		var {{var "t"}} {{ .Typ }}
+		{{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
+		{{var "v"}} <- {{var "t"}} 
+	{{ else }} 
 	{{var "n"}} := {{var "l"}} 
 	if {{var "l"}} > cap({{var "v"}}) {
-		{{ if .Array }}r.ReadArrayCannotExpand(len({{var "v"}}), {{var "l"}})
+		{{ if isArray }}r.ReadArrayCannotExpand(len({{var "v"}}), {{var "l"}})
 		{{var "n"}} = len({{var "v"}})
 		{{ else }}{{ if .Immutable }}
 		{{var "v2"}} := {{var "v"}}
@@ -38,30 +43,32 @@ if {{var "l"}} == 0 { {{ if not .Array }}
 	{{var "j"}} := 0
 	for ; {{var "j"}} < {{var "n"}} ; {{var "j"}}++ {
 		{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
-	} {{ if .Array }}
+	} {{ if isArray }}
 	for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
 		z.DecSwallow()
 	}{{ end }}
+	{{ end }}{{/* closing if not chan */}}
 } else {
 	for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
 		if {{var "j"}} >= len({{var "v"}}) {
-			{{ if .Array }}r.ReadArrayCannotExpand(len({{var "v"}}), {{var "j"}}+1)
-			{{ else }}{{var "v"}} = append({{var "v"}}, {{zero}})// var {{var "z"}} {{ .Typ }}
+			{{ if isArray }}r.ReadArrayCannotExpand(len({{var "v"}}), {{var "j"}}+1)
+			{{ else if isSlice}}{{var "v"}} = append({{var "v"}}, {{zero}})// var {{var "z"}} {{ .Typ }}
 			{{var "c"}} = true {{ end }}
 		}
 		if {{var "j"}} > 0 {
 			{{var "h"}}.Sep({{var "j"}})
 		}
+		{{ if isChan}}
+		var {{var "t"}} {{ .Typ }}
+		{{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
+		{{var "v"}} <- {{var "t"}} 
+		{{ else }}
 		if {{var "j"}} < len({{var "v"}}) {
-			{{/* .TempVar }}t{{ .Rand }} := &{{var "v"}}[{{var "j"}}]{{ decLine "t" }} 
-			*/}}{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
+			{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
 		} else {
-			{{/* 
-			var {{var "z"}} {{ .Typ }}
-				{{var "t"}} := &{{var "z"}}{{ decLine "t" }}
-			{{ $x := printf "%vz%v" .TempVar .Rand }}{{ decLineVar $x }} 
-			*/}}z.DecSwallow()
+			z.DecSwallow()
 		}
+		{{ end }}
 	}
 	{{var "h"}}.End()
 }

+ 23 - 16
codec/gen.generated.go

@@ -55,29 +55,34 @@ r.ReadMapEnd()
 `
 
 const genDecListTmpl = `
-const {{var "Arr"}} bool = {{ .Array }}
 {{var "v"}} := *{{ .Varname }}
 {{var "h"}}, {{var "l"}} := z.DecSliceHelperStart()
 
 var {{var "c"}} bool 
-{{ if not .Array }}if {{var "v"}} == nil {
+{{ if not isArray }}if {{var "v"}} == nil {
 	if {{var "l"}} <= 0 {
-		{{var "v"}} = []{{ .Typ }}{}
+        {{var "v"}} = make({{ .CTyp }}, 0)
 	} else {
-		{{var "v"}} = make([]{{ .Typ }}, {{var "l"}}, {{var "l"}})
+		{{var "v"}} = make({{ .CTyp }}, {{var "l"}})
 	}
 	{{var "c"}} = true 
 } 
 {{ end }}
-if {{var "l"}} == 0 { {{ if not .Array }}
+if {{var "l"}} == 0 { {{ if isSlice }}
 	if len({{var "v"}}) != 0 { 
 		{{var "v"}} = {{var "v"}}[:0] 
 		{{var "c"}} = true 
 	} {{ end }}
 } else if {{var "l"}} > 0 {
+	{{ if isChan }}
+	for {{var "r"}} := 0; {{var "r"}} < {{var "l"}}; {{var "r"}}++ {
+		var {{var "t"}} {{ .Typ }}
+		{{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
+		{{var "v"}} <- {{var "t"}} 
+	{{ else }} 
 	{{var "n"}} := {{var "l"}} 
 	if {{var "l"}} > cap({{var "v"}}) {
-		{{ if .Array }}r.ReadArrayCannotExpand(len({{var "v"}}), {{var "l"}})
+		{{ if isArray }}r.ReadArrayCannotExpand(len({{var "v"}}), {{var "l"}})
 		{{var "n"}} = len({{var "v"}})
 		{{ else }}{{ if .Immutable }}
 		{{var "v2"}} := {{var "v"}}
@@ -95,30 +100,32 @@ if {{var "l"}} == 0 { {{ if not .Array }}
 	{{var "j"}} := 0
 	for ; {{var "j"}} < {{var "n"}} ; {{var "j"}}++ {
 		{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
-	} {{ if .Array }}
+	} {{ if isArray }}
 	for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
 		z.DecSwallow()
 	}{{ end }}
+	{{ end }}{{/* closing if not chan */}}
 } else {
 	for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
 		if {{var "j"}} >= len({{var "v"}}) {
-			{{ if .Array }}r.ReadArrayCannotExpand(len({{var "v"}}), {{var "j"}}+1)
-			{{ else }}{{var "v"}} = append({{var "v"}}, {{zero}})// var {{var "z"}} {{ .Typ }}
+			{{ if isArray }}r.ReadArrayCannotExpand(len({{var "v"}}), {{var "j"}}+1)
+			{{ else if isSlice}}{{var "v"}} = append({{var "v"}}, {{zero}})// var {{var "z"}} {{ .Typ }}
 			{{var "c"}} = true {{ end }}
 		}
 		if {{var "j"}} > 0 {
 			{{var "h"}}.Sep({{var "j"}})
 		}
+		{{ if isChan}}
+		var {{var "t"}} {{ .Typ }}
+		{{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
+		{{var "v"}} <- {{var "t"}} 
+		{{ else }}
 		if {{var "j"}} < len({{var "v"}}) {
-			{{/* .TempVar }}t{{ .Rand }} := &{{var "v"}}[{{var "j"}}]{{ decLine "t" }} 
-			*/}}{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
+			{{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
 		} else {
-			{{/* 
-			var {{var "z"}} {{ .Typ }}
-				{{var "t"}} := &{{var "z"}}{{ decLine "t" }}
-			{{ $x := printf "%vz%v" .TempVar .Rand }}{{ decLineVar $x }} 
-			*/}}z.DecSwallow()
+			z.DecSwallow()
 		}
+		{{ end }}
 	}
 	{{var "h"}}.End()
 }

+ 46 - 16
codec/gen.go

@@ -241,7 +241,7 @@ func Gen(w io.Writer, buildTags, pkgName string, useUnsafe bool, typ ...reflect.
 		x.linef("func (x %s) enc%s(v %s, e *%sEncoder) {", x.hn, x.genMethodNameT(t), x.genTypeName(t), x.cpfx)
 		x.genRequiredMethodVars(true)
 		switch t.Kind() {
-		case reflect.Array, reflect.Slice:
+		case reflect.Array, reflect.Slice, reflect.Chan:
 			x.encListFallback("v", rtid, t)
 		case reflect.Map:
 			x.encMapFallback("v", rtid, t)
@@ -255,10 +255,8 @@ func Gen(w io.Writer, buildTags, pkgName string, useUnsafe bool, typ ...reflect.
 		x.linef("func (x %s) dec%s(v *%s, d *%sDecoder) {", x.hn, x.genMethodNameT(t), x.genTypeName(t), x.cpfx)
 		x.genRequiredMethodVars(false)
 		switch t.Kind() {
-		case reflect.Array:
-			x.decListFallback("v", rtid, true, t)
-		case reflect.Slice:
-			x.decListFallback("v", rtid, false, t)
+		case reflect.Array, reflect.Slice, reflect.Chan:
+			x.decListFallback("v", rtid, t)
 		case reflect.Map:
 			x.decMapFallback("v", rtid, t)
 		default:
@@ -292,7 +290,7 @@ func (x *genRunner) genRefPkgs(t reflect.Type) {
 		x.im[tpkg] = t
 	}
 	switch t.Kind() {
-	case reflect.Array, reflect.Slice, reflect.Ptr:
+	case reflect.Array, reflect.Slice, reflect.Ptr, reflect.Chan:
 		x.genRefPkgs(t.Elem())
 	case reflect.Map:
 		x.genRefPkgs(t.Elem())
@@ -425,7 +423,7 @@ func (x *genRunner) xtraSM(varname string, encode bool, t reflect.Type) {
 func (x *genRunner) encVar(varname string, t reflect.Type) {
 	var checkNil bool
 	switch t.Kind() {
-	case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map:
+	case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map, reflect.Chan:
 		checkNil = true
 	}
 	if checkNil {
@@ -500,7 +498,7 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
 		x.line("r.EncodeBool(bool(" + varname + "))")
 	case reflect.String:
 		x.line("r.EncodeString(codecSelferC_UTF8" + x.xs + ", string(" + varname + "))")
-	case reflect.Array:
+	case reflect.Array, reflect.Chan:
 		x.xtraSM(varname, true, t)
 		// x.encListFallback(varname, rtid, t)
 	case reflect.Slice:
@@ -617,7 +615,7 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 		switch t2.Type.Kind() {
 		case reflect.Struct:
 			omitline += " true"
-		case reflect.Map, reflect.Slice, reflect.Array:
+		case reflect.Map, reflect.Slice, reflect.Array, reflect.Chan:
 			omitline += "len(" + varname + "." + t2.Name + ") != 0"
 		default:
 			omitline += varname + "." + t2.Name + " != " + genZeroValueR(t2.Type, x.tc)
@@ -738,16 +736,27 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 
 func (x *genRunner) encListFallback(varname string, rtid uintptr, t reflect.Type) {
 	i := x.varsfx()
+	g := genTempVarPfx
 	x.line("r.EncodeArrayStart(len(" + varname + "))")
 	x.line(genTempVarPfx + "s" + i + " := !z.EncBinary()")
 	x.line("if " + genTempVarPfx + "s" + i + " {")
-	x.linef("for %si%s, %sv%s := range %s {", genTempVarPfx, i, genTempVarPfx, i, varname)
+	if t.Kind() == reflect.Chan {
+		x.linef("for %si%s, %si2%s := 0, len(%s); %si%s < %si2%s; %si%s++ {", g, i, g, i, varname, g, i, g, i, g, i)
+		x.linef("%sv%s := <-%s", g, i, varname)
+	} else {
+		x.linef("for %si%s, %sv%s := range %s {", genTempVarPfx, i, genTempVarPfx, i, varname)
+	}
 	x.line("if " + genTempVarPfx + "i" + i + " > 0 { r.EncodeArrayEntrySeparator() }")
 	x.encVar(genTempVarPfx+"v"+i, t.Elem())
 	x.line("}")
 	x.line("r.EncodeArrayEnd()")
 	x.line("} else {")
-	x.line("for _, " + genTempVarPfx + "v" + i + " := range " + varname + " {")
+	if t.Kind() == reflect.Chan {
+		x.linef("for %si%s, %si2%s := 0, len(%s); %si%s < %si2%s; %si%s++ {", g, i, g, i, varname, g, i, g, i, g, i)
+		x.linef("%sv%s := <-%s", g, i, varname)
+	} else {
+		x.line("for _, " + genTempVarPfx + "v" + i + " := range " + varname + " {")
+	}
 	x.encVar(genTempVarPfx+"v"+i, t.Elem())
 	x.line("}")
 	x.line("}")
@@ -932,7 +941,7 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 	case reflect.String:
 		x.line("*((*string)(" + varname + ")) = r.DecodeString()")
 		// x.line("z.DecString((*string)(" + varname + "))")
-	case reflect.Array:
+	case reflect.Array, reflect.Chan:
 		x.xtraSM(varname, false, t)
 		// x.decListFallback(varname, rtid, true, t)
 	case reflect.Slice:
@@ -1019,18 +1028,17 @@ func (x *genRunner) decTryAssignPrimitive(varname string, t reflect.Type) (tryAs
 	return
 }
 
-func (x *genRunner) decListFallback(varname string, rtid uintptr, array bool, t reflect.Type) {
+func (x *genRunner) decListFallback(varname string, rtid uintptr, t reflect.Type) {
 	type tstruc struct {
 		TempVar   string
 		Rand      string
 		Varname   string
+		CTyp      string
 		Typ       string
-		Array     bool
 		Immutable bool
 	}
 	telem := t.Elem()
-	ts := tstruc{genTempVarPfx, x.varsfx(), varname, x.genTypeName(telem),
-		array, genIsImmutable(telem)}
+	ts := tstruc{genTempVarPfx, x.varsfx(), varname, x.genTypeName(t), x.genTypeName(telem), genIsImmutable(telem)}
 
 	funcs := make(template.FuncMap)
 	funcs["decLineVar"] = func(varname string) string {
@@ -1047,6 +1055,15 @@ func (x *genRunner) decListFallback(varname string, rtid uintptr, array bool, t
 	funcs["zero"] = func() string {
 		return genZeroValueR(telem, x.tc)
 	}
+	funcs["isArray"] = func() bool {
+		return t.Kind() == reflect.Array
+	}
+	funcs["isSlice"] = func() bool {
+		return t.Kind() == reflect.Slice
+	}
+	funcs["isChan"] = func() bool {
+		return t.Kind() == reflect.Chan
+	}
 	tm, err := template.New("").Funcs(funcs).Parse(genDecListTmpl)
 	if err != nil {
 		panic(err)
@@ -1344,6 +1361,8 @@ func genTypeName(t reflect.Type, tRef reflect.Type) (n string) {
 		return ptrPfx + "[]" + genTypeName(t.Elem(), tRef)
 	case reflect.Array:
 		return ptrPfx + "[" + strconv.FormatInt(int64(t.Len()), 10) + "]" + genTypeName(t.Elem(), tRef)
+	case reflect.Chan:
+		return ptrPfx + t.ChanDir().String() + " " + genTypeName(t.Elem(), tRef)
 	default:
 		if t == intfTyp {
 			return ptrPfx + "interface{}"
@@ -1370,6 +1389,17 @@ func genMethodNameT(t reflect.Type, tRef reflect.Type) (n string) {
 		return ptrPfx + "Slice" + genMethodNameT(t.Elem(), tRef)
 	case reflect.Array:
 		return ptrPfx + "Array" + strconv.FormatInt(int64(t.Len()), 10) + genMethodNameT(t.Elem(), tRef)
+	case reflect.Chan:
+		var cx string
+		switch t.ChanDir() {
+		case reflect.SendDir:
+			cx = "ChanSend"
+		case reflect.RecvDir:
+			cx = "ChanRecv"
+		default:
+			cx = "Chan"
+		}
+		return ptrPfx + cx + genMethodNameT(t.Elem(), tRef)
 	default:
 		if t == intfTyp {
 			return ptrPfx + "Interface"

+ 11 - 2
codec/helper.go

@@ -183,6 +183,15 @@ const (
 	// valueTypeInvalid = 0xff
 )
 
+type seqType uint8
+
+const (
+	_ seqType = iota
+	seqTypeArray
+	seqTypeSlice
+	seqTypeChan
+)
+
 var (
 	bigen               = binary.BigEndian
 	structInfoFieldName = "_struct"
@@ -692,8 +701,8 @@ func rgetTypeInfo(rt reflect.Type, indexstack []int, fnameToHastag map[string]bo
 ) {
 	for j := 0; j < rt.NumField(); j++ {
 		f := rt.Field(j)
-		// chan and func types are skipped.
-		if tk := f.Type.Kind(); tk == reflect.Chan || tk == reflect.Func {
+		// func types are skipped.
+		if tk := f.Type.Kind(); tk == reflect.Func {
 			continue
 		}
 		stag := f.Tag.Get(structTagName)

+ 5 - 92
codec/json.go

@@ -191,7 +191,7 @@ func (e *jsonEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
 		e.w.writen1('"')
 	} else {
 		// e.EncodeString(c, string(v))
-		e.quoteBytes(v)
+		e.quoteStr(stringView(v))
 	}
 }
 
@@ -224,9 +224,10 @@ func (e *jsonEncDriver) quoteStr(s string) {
 			case '\t':
 				w.writen2('\\', 't')
 			default:
+				// encode all bytes < 0x20 (except \r, \n).
+				// also encode < > & to prevent security holes when served to some browsers.
 				w.writestr(`\u00`)
-				w.writen1(hex[b>>4])
-				w.writen1(hex[b&0xF])
+				w.writen2(hex[b>>4], hex[b&0xF])
 			}
 			i++
 			start = i
@@ -262,75 +263,6 @@ func (e *jsonEncDriver) quoteStr(s string) {
 	w.writen1('"')
 }
 
-// keep this in sync with quoteStr above. Needed to elide the str->[]byte allocation.
-// This may be automatically called from EncodeStringBytes, called by MarshalText implementers.
-func (e *jsonEncDriver) quoteBytes(s []byte) {
-	// adapted from std pkg encoding/json
-	const hex = "0123456789abcdef"
-	w := e.w
-	w.writen1('"')
-	start := 0
-	for i := 0; i < len(s); {
-		if b := s[i]; b < utf8.RuneSelf {
-			if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
-				i++
-				continue
-			}
-			if start < i {
-				w.writeb(s[start:i])
-			}
-			switch b {
-			case '\\', '"':
-				w.writen2('\\', b)
-			case '\n':
-				w.writen2('\\', 'n')
-			case '\r':
-				w.writen2('\\', 'r')
-			case '\b':
-				w.writen2('\\', 'b')
-			case '\f':
-				w.writen2('\\', 'f')
-			case '\t':
-				w.writen2('\\', 't')
-			default:
-				w.writestr(`\u00`)
-				w.writen1(hex[b>>4])
-				w.writen1(hex[b&0xF])
-			}
-			i++
-			start = i
-			continue
-		}
-		c, size := utf8.DecodeRune(s[i:])
-		if c == utf8.RuneError && size == 1 {
-			if start < i {
-				w.writeb(s[start:i])
-			}
-			w.writestr(`\ufffd`)
-			i += size
-			start = i
-			continue
-		}
-		// U+2028 is LINE SEPARATOR. U+2029 is PARAGRAPH SEPARATOR.
-		// Both technically valid JSON, but bomb on JSONP, so fix here.
-		if c == '\u2028' || c == '\u2029' {
-			if start < i {
-				w.writeb(s[start:i])
-			}
-			w.writestr(`\u202`)
-			w.writen1(hex[c&0xF])
-			i += size
-			start = i
-			continue
-		}
-		i += size
-	}
-	if start < len(s) {
-		w.writeb(s[start:])
-	}
-	w.writen1('"')
-}
-
 //--------------------------------
 
 type jsonNum struct {
@@ -456,32 +388,13 @@ func (d *jsonDecDriver) CheckBreak() bool {
 	return b == '}' || b == ']'
 }
 
-// func (d *jsonDecDriver) readStr(s []byte) {
-// 	if jsonValidateSymbols {
-// 		bs := d.b[:len(s)]
-// 		d.r.readb(bs)
-// 		if !bytes.Equal(bs, s) {
-// 			d.d.errorf("json: expecting %s: got %s", s, bs)
-//			return
-// 		}
-// 	} else {
-// 		d.r.readx(len(s))
-// 	}
-// 	if jsonTrackSkipWhitespace {
-// 		d.wsSkipped = false
-// 	}
-// }
-
 func (d *jsonDecDriver) readStrIdx(fromIdx, toIdx uint8) {
+	bs := d.r.readx(int(toIdx - fromIdx))
 	if jsonValidateSymbols {
-		bs := d.b[:(toIdx - fromIdx)]
-		d.r.readb(bs)
 		if !bytes.Equal(bs, jsonLiterals[fromIdx:toIdx]) {
 			d.d.errorf("json: expecting %s: got %s", jsonLiterals[fromIdx:toIdx], bs)
 			return
 		}
-	} else {
-		d.r.readx(int(toIdx - fromIdx))
 	}
 	if jsonTrackSkipWhitespace {
 		d.wsSkipped = false

+ 2 - 4
codec/prebuild.sh

@@ -29,14 +29,12 @@ _needgen() {
 # generated files and put stubs in place, before calling "go run" again
 # to recreate them.
 _build() {
-    if [[ "${zforce}" == "1" ||
+    if ! [[ "${zforce}" == "1" ||
                 "1" == $( _needgen "fast-path.generated.go" ) ||
                 "1" == $( _needgen "gen-helper.generated.go" ) ||
                 "1" == $( _needgen "gen.generated.go" ) ||
                 1 == 0 ]]
     then
-        true  # continue. do not return
-    else 
         return 0
     fi 
 
@@ -51,7 +49,7 @@ _build() {
         # [ -e "safe${_gg}" ] && mv safe${_gg} safe${_gg}__${_zts}.bak
         # [ -e "unsafe${_gg}" ] && mv unsafe${_gg} unsafe${_gg}__${_zts}.bak
     else 
-        rm -f fast-path.generated.go gen.generated.go gen-helper.generated.go *safe.generated.go
+        rm -f fast-path.generated.go gen.generated.go gen-helper.generated.go *safe.generated.go *_generated_test.go
     fi
 
     cat > gen.generated.go <<EOF