Browse Source

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 years ago
parent
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:
 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)
   - [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)
   - [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
   - Fast (no-reflection) encoding/decoding of common maps and slices
   - Code-generation for faster performance.
   - Code-generation for faster performance.
   - Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
   - 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
   - NIL in data stream decoded as zero value
   - Never silently skip data when decoding.
   - Never silently skip data when decoding.
     User decides whether to return an error or silently skip data when keys or indexes
     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.
     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.
   - Provides a RPC Server and Client Codec for net/rpc communication protocol.
   - Handle unique idiosynchracies of codecs e.g. 
   - Handle unique idiosynchracies of codecs e.g. 
     - For messagepack, configure how ambiguities in handling raw bytes are resolved 
     - 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
   - Fast (no-reflection) encoding/decoding of common maps and slices
   - Code-generation for faster performance.
   - Code-generation for faster performance.
   - Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
   - 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
   - NIL in data stream decoded as zero value
   - Never silently skip data when decoding.
   - Never silently skip data when decoding.
     User decides whether to return an error or silently skip data when keys or indexes
     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.
     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.
   - Provides a RPC Server and Client Codec for net/rpc communication protocol.
   - Handle unique idiosynchracies of codecs e.g. 
   - Handle unique idiosynchracies of codecs e.g. 
     - For messagepack, configure how ambiguities in handling raw bytes are resolved 
     - 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,
 func testCodecRpcOne(t *testing.T, rr Rpc, h Handle, doRequest bool, exitSleepMs time.Duration,
 ) (port int) {
 ) (port int) {
 	testOnce.Do(testInitAll)
 	testOnce.Do(testInitAll)
@@ -1012,6 +1049,14 @@ func TestJsonCodecsEmbeddedPointer(t *testing.T) {
 	testCodecEmbeddedPointer(t, testJsonH)
 	testCodecEmbeddedPointer(t, testJsonH)
 }
 }
 
 
+func TestJsonCodecChan(t *testing.T) {
+	testCodecChan(t, testJsonH)
+}
+
+func TestCborCodecChan(t *testing.T) {
+	testCodecChan(t, testCborH)
+}
+
 // ----- RPC -----
 // ----- RPC -----
 
 
 func TestBincRpcGo(t *testing.T) {
 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 _, f := range astfiles {
 		for _, d := range f.Decls {
 		for _, d := range f.Decls {
 			if gd, ok := d.(*ast.GenDecl); ok {
 			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' {
 						if len(td.Name.Name) == 0 || td.Name.Name[0] > 'Z' || td.Name.Name[0] < 'A' {
 							continue
 							continue
 						}
 						}
@@ -145,10 +145,11 @@ func Generate(outfile, buildTag, codecPkgPath string, useUnsafe bool, goRunTag s
 						//   primitives (numbers, bool, string): Ident
 						//   primitives (numbers, bool, string): Ident
 						//   map: MapType
 						//   map: MapType
 						//   slice, array: ArrayType
 						//   slice, array: ArrayType
+						//   chan: ChanType
 						// do not generate:
 						// do not generate:
-						//   ChanType, FuncType, InterfaceType, StarExpr (ptr), etc
+						//   FuncType, InterfaceType, StarExpr (ptr), etc
 						switch td.Type.(type) {
 						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 {
 							if regexName.FindStringIndex(td.Name.Name) != nil {
 								tv.Types = append(tv.Types, td.Name.Name)
 								tv.Types = append(tv.Types, td.Name.Name)
 							}
 							}

+ 75 - 43
codec/decode.go

@@ -300,7 +300,7 @@ type decFnInfoX struct {
 	ti    *typeInfo
 	ti    *typeInfo
 	xfFn  Ext
 	xfFn  Ext
 	xfTag uint64
 	xfTag uint64
-	array bool
+	seq   seqType
 }
 }
 
 
 // decFnInfo has methods for handling decoding of a specific type
 // decFnInfo has methods for handling decoding of a specific type
@@ -651,35 +651,52 @@ func (f decFnInfo) kSlice(rv reflect.Value) {
 	d := f.d
 	d := f.d
 	if f.dd.IsContainerType(valueTypeBytes) || f.dd.IsContainerType(valueTypeString) {
 	if f.dd.IsContainerType(valueTypeBytes) || f.dd.IsContainerType(valueTypeString) {
 		if ti.rtid == uint8SliceTypId || ti.rt.Elem().Kind() == reflect.Uint8 {
 		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
 			return
 		}
 		}
 	}
 	}
 
 
-	array := f.array
+	// array := f.seq == seqTypeChan
 
 
 	slh, containerLenS := d.decSliceHelperStart()
 	slh, containerLenS := d.decSliceHelperStart()
 
 
 	// an array can never return a nil slice. so no need to check f.array here.
 	// an array can never return a nil slice. so no need to check f.array here.
 	if rv.IsNil() {
 	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()
 	rvlen := rv.Len()
 	if containerLenS == 0 {
 	if containerLenS == 0 {
-		if !array && rvlen != 0 {
+		if f.seq == seqTypeSlice && rvlen != 0 {
 			rv.SetLen(0)
 			rv.SetLen(0)
 		}
 		}
 		// slh.End() // f.dd.ReadArrayEnd()
 		// slh.End() // f.dd.ReadArrayEnd()
@@ -702,32 +719,41 @@ func (f decFnInfo) kSlice(rv reflect.Value) {
 
 
 	hasLen := containerLenS >= 0
 	hasLen := containerLenS >= 0
 	if hasLen {
 	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
 				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 {
 	} else {
@@ -735,10 +761,10 @@ func (f decFnInfo) kSlice(rv reflect.Value) {
 			var decodeIntoBlank bool
 			var decodeIntoBlank bool
 			// if indefinite, etc, then expand the slice if necessary
 			// if indefinite, etc, then expand the slice if necessary
 			if j >= rvlen {
 			if j >= rvlen {
-				if array {
+				if f.seq == seqTypeArray {
 					d.arrayCannotExpand(rvlen, j+1)
 					d.arrayCannotExpand(rvlen, j+1)
 					decodeIntoBlank = true
 					decodeIntoBlank = true
-				} else {
+				} else if f.seq == seqTypeSlice {
 					rv = reflect.Append(rv, reflect.Zero(rtelem0))
 					rv = reflect.Append(rv, reflect.Zero(rtelem0))
 					rvlen++
 					rvlen++
 					rvChanged = true
 					rvChanged = true
@@ -747,7 +773,11 @@ func (f decFnInfo) kSlice(rv reflect.Value) {
 			if j > 0 {
 			if j > 0 {
 				slh.Sep(j)
 				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()
 				d.swallow()
 			} else {
 			} else {
 				d.decodeValue(rv.Index(j), fn)
 				d.decodeValue(rv.Index(j), fn)
@@ -1305,13 +1335,15 @@ func (d *Decoder) getDecFn(rt reflect.Type, checkAll bool) (fn decFn) {
 			case reflect.Struct:
 			case reflect.Struct:
 				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
 				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
 				fn.f = (decFnInfo).kStruct
 				fn.f = (decFnInfo).kStruct
+			case reflect.Chan:
+				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti, seq: seqTypeChan}
+				fn.f = (decFnInfo).kSlice
 			case reflect.Slice:
 			case reflect.Slice:
-				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
+				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti, seq: seqTypeSlice}
 				fn.f = (decFnInfo).kSlice
 				fn.f = (decFnInfo).kSlice
 			case reflect.Array:
 			case reflect.Array:
 				// fi.decFnInfoX = &decFnInfoX{array: true}
 				// 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
 				fn.f = (decFnInfo).kArray
 			case reflect.Map:
 			case reflect.Map:
 				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}
 				fi.decFnInfoX = &decFnInfoX{d: d, ti: ti}

+ 44 - 11
codec/encode.go

@@ -252,7 +252,7 @@ type encFnInfoX struct {
 	ti    *typeInfo
 	ti    *typeInfo
 	xfFn  Ext
 	xfFn  Ext
 	xfTag uint64
 	xfTag uint64
-	array bool
+	seq   seqType
 }
 }
 
 
 type encFnInfo struct {
 type encFnInfo struct {
@@ -374,7 +374,7 @@ func (f encFnInfo) kSlice(rv reflect.Value) {
 	//   (don't call rv.Bytes, rv.Slice, etc).
 	//   (don't call rv.Bytes, rv.Slice, etc).
 	// E.g. type struct S{B [2]byte};
 	// E.g. type struct S{B [2]byte};
 	//   Encode(S{}) will bomb on "panic: slice of unaddressable array".
 	//   Encode(S{}) will bomb on "panic: slice of unaddressable array".
-	if !f.array {
+	if f.seq != seqTypeArray {
 		if rv.IsNil() {
 		if rv.IsNil() {
 			f.ee.EncodeNil()
 			f.ee.EncodeNil()
 			return
 			return
@@ -389,12 +389,18 @@ func (f encFnInfo) kSlice(rv reflect.Value) {
 	rtelem := ti.rt.Elem()
 	rtelem := ti.rt.Elem()
 	l := rv.Len()
 	l := rv.Len()
 	if rtelem.Kind() == reflect.Uint8 {
 	if rtelem.Kind() == reflect.Uint8 {
-		if f.array {
+		switch f.seq {
+		case seqTypeArray:
 			// if l == 0 { f.ee.encodeStringBytes(c_RAW, nil) } else
 			// if l == 0 { f.ee.encodeStringBytes(c_RAW, nil) } else
 			if rv.CanAddr() {
 			if rv.CanAddr() {
 				f.ee.EncodeStringBytes(c_RAW, rv.Slice(0, l).Bytes())
 				f.ee.EncodeStringBytes(c_RAW, rv.Slice(0, l).Bytes())
 			} else {
 			} 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)
 				reflect.Copy(reflect.ValueOf(bs), rv)
 				// TODO: Test that reflect.Copy works instead of manual one-by-one
 				// TODO: Test that reflect.Copy works instead of manual one-by-one
 				// for i := 0; i < l; i++ {
 				// for i := 0; i < l; i++ {
@@ -402,8 +408,20 @@ func (f encFnInfo) kSlice(rv reflect.Value) {
 				// }
 				// }
 				f.ee.EncodeStringBytes(c_RAW, bs)
 				f.ee.EncodeStringBytes(c_RAW, bs)
 			}
 			}
-		} else {
+		case seqTypeSlice:
 			f.ee.EncodeStringBytes(c_RAW, rv.Bytes())
 			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
 		return
 	}
 	}
@@ -446,11 +464,23 @@ func (f encFnInfo) kSlice(rv reflect.Value) {
 						f.ee.EncodeArrayEntrySeparator()
 						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 {
 		} else {
 			for j := 0; j < l; j++ {
 			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
 	hh Handle
 	f  map[uintptr]encFn
 	f  map[uintptr]encFn
+	b  [scratchByteArrayLen]byte
 }
 }
 
 
 // NewEncoder returns an Encoder for encoding into an io.Writer.
 // NewEncoder returns an Encoder for encoding into an io.Writer.
@@ -970,7 +1001,7 @@ LOOP:
 				e.e.EncodeNil()
 				e.e.EncodeNil()
 				return
 				return
 			}
 			}
-		case reflect.Invalid, reflect.Chan, reflect.Func:
+		case reflect.Invalid, reflect.Func:
 			e.e.EncodeNil()
 			e.e.EncodeNil()
 			return
 			return
 		}
 		}
@@ -1077,12 +1108,14 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkAll bool) (fn enc
 				fn.f = (encFnInfo).kUint
 				fn.f = (encFnInfo).kUint
 			case reflect.Invalid:
 			case reflect.Invalid:
 				fn.f = (encFnInfo).kInvalid
 				fn.f = (encFnInfo).kInvalid
+			case reflect.Chan:
+				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti, seq: seqTypeChan}
+				fn.f = (encFnInfo).kSlice
 			case reflect.Slice:
 			case reflect.Slice:
-				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti}
+				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti, seq: seqTypeSlice}
 				fn.f = (encFnInfo).kSlice
 				fn.f = (encFnInfo).kSlice
 			case reflect.Array:
 			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
 				fn.f = (encFnInfo).kSlice
 			case reflect.Struct:
 			case reflect.Struct:
 				fi.encFnInfoX = &encFnInfoX{e: e, ti: ti}
 				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
 // -- -- fast path functions
 
 
 func (f decFnInfo) fastpathDecSliceIntfR(rv reflect.Value) {
 func (f decFnInfo) fastpathDecSliceIntfR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]interface{})
 		vp := rv.Addr().Interface().(*[]interface{})
 		v, changed := fastpathTV.DecSliceIntfV(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceStringR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]string)
 		vp := rv.Addr().Interface().(*[]string)
 		v, changed := fastpathTV.DecSliceStringV(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceFloat32R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]float32)
 		vp := rv.Addr().Interface().(*[]float32)
 		v, changed := fastpathTV.DecSliceFloat32V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceFloat64R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]float64)
 		vp := rv.Addr().Interface().(*[]float64)
 		v, changed := fastpathTV.DecSliceFloat64V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceUintR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]uint)
 		vp := rv.Addr().Interface().(*[]uint)
 		v, changed := fastpathTV.DecSliceUintV(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceUint16R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]uint16)
 		vp := rv.Addr().Interface().(*[]uint16)
 		v, changed := fastpathTV.DecSliceUint16V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceUint32R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]uint32)
 		vp := rv.Addr().Interface().(*[]uint32)
 		v, changed := fastpathTV.DecSliceUint32V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceUint64R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]uint64)
 		vp := rv.Addr().Interface().(*[]uint64)
 		v, changed := fastpathTV.DecSliceUint64V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceIntR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int)
 		vp := rv.Addr().Interface().(*[]int)
 		v, changed := fastpathTV.DecSliceIntV(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceInt8R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int8)
 		vp := rv.Addr().Interface().(*[]int8)
 		v, changed := fastpathTV.DecSliceInt8V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceInt16R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int16)
 		vp := rv.Addr().Interface().(*[]int16)
 		v, changed := fastpathTV.DecSliceInt16V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceInt32R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int32)
 		vp := rv.Addr().Interface().(*[]int32)
 		v, changed := fastpathTV.DecSliceInt32V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceInt64R(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]int64)
 		vp := rv.Addr().Interface().(*[]int64)
 		v, changed := fastpathTV.DecSliceInt64V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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) {
 func (f decFnInfo) fastpathDecSliceBoolR(rv reflect.Value) {
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported
 		vp := rv.Addr().Interface().(*[]bool)
 		vp := rv.Addr().Interface().(*[]bool)
 		v, changed := fastpathTV.DecSliceBoolV(*vp, fastpathCheckNilFalse, !array, f.d)
 		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{})
 - are settable (e.g. contained in an interface{})
 */}}
 */}}
 func (f decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) { 
 func (f decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) { 
-	array := f.array
+	array := f.seq == seqTypeArray
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported 
 	if !array && rv.CanAddr() { // CanSet => CanAddr + Exported 
 		vp := rv.Addr().Interface().(*[]{{ .Elem }})
 		vp := rv.Addr().Interface().(*[]{{ .Elem }})
 		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, !array, f.d)
 		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 "v"}} := *{{ .Varname }}
 {{var "h"}}, {{var "l"}} := z.DecSliceHelperStart()
 {{var "h"}}, {{var "l"}} := z.DecSliceHelperStart()
 
 
 var {{var "c"}} bool 
 var {{var "c"}} bool 
-{{ if not .Array }}if {{var "v"}} == nil {
+{{ if not isArray }}if {{var "v"}} == nil {
 	if {{var "l"}} <= 0 {
 	if {{var "l"}} <= 0 {
-		{{var "v"}} = []{{ .Typ }}{}
+        {{var "v"}} = make({{ .CTyp }}, 0)
 	} else {
 	} else {
-		{{var "v"}} = make([]{{ .Typ }}, {{var "l"}}, {{var "l"}})
+		{{var "v"}} = make({{ .CTyp }}, {{var "l"}})
 	}
 	}
 	{{var "c"}} = true 
 	{{var "c"}} = true 
 } 
 } 
 {{ end }}
 {{ end }}
-if {{var "l"}} == 0 { {{ if not .Array }}
+if {{var "l"}} == 0 { {{ if isSlice }}
 	if len({{var "v"}}) != 0 { 
 	if len({{var "v"}}) != 0 { 
 		{{var "v"}} = {{var "v"}}[:0] 
 		{{var "v"}} = {{var "v"}}[:0] 
 		{{var "c"}} = true 
 		{{var "c"}} = true 
 	} {{ end }}
 	} {{ end }}
 } else if {{var "l"}} > 0 {
 } 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"}} 
 	{{var "n"}} := {{var "l"}} 
 	if {{var "l"}} > cap({{var "v"}}) {
 	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"}})
 		{{var "n"}} = len({{var "v"}})
 		{{ else }}{{ if .Immutable }}
 		{{ else }}{{ if .Immutable }}
 		{{var "v2"}} := {{var "v"}}
 		{{var "v2"}} := {{var "v"}}
@@ -38,30 +43,32 @@ if {{var "l"}} == 0 { {{ if not .Array }}
 	{{var "j"}} := 0
 	{{var "j"}} := 0
 	for ; {{var "j"}} < {{var "n"}} ; {{var "j"}}++ {
 	for ; {{var "j"}} < {{var "n"}} ; {{var "j"}}++ {
 		{{ $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 }}
-	} {{ if .Array }}
+	} {{ if isArray }}
 	for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
 	for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
 		z.DecSwallow()
 		z.DecSwallow()
 	}{{ end }}
 	}{{ end }}
+	{{ end }}{{/* closing if not chan */}}
 } else {
 } else {
 	for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
 	for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
 		if {{var "j"}} >= len({{var "v"}}) {
 		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 }}
 			{{var "c"}} = true {{ end }}
 		}
 		}
 		if {{var "j"}} > 0 {
 		if {{var "j"}} > 0 {
 			{{var "h"}}.Sep({{var "j"}})
 			{{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"}}) {
 		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 {
 		} 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()
 	{{var "h"}}.End()
 }
 }

+ 23 - 16
codec/gen.generated.go

@@ -55,29 +55,34 @@ r.ReadMapEnd()
 `
 `
 
 
 const genDecListTmpl = `
 const genDecListTmpl = `
-const {{var "Arr"}} bool = {{ .Array }}
 {{var "v"}} := *{{ .Varname }}
 {{var "v"}} := *{{ .Varname }}
 {{var "h"}}, {{var "l"}} := z.DecSliceHelperStart()
 {{var "h"}}, {{var "l"}} := z.DecSliceHelperStart()
 
 
 var {{var "c"}} bool 
 var {{var "c"}} bool 
-{{ if not .Array }}if {{var "v"}} == nil {
+{{ if not isArray }}if {{var "v"}} == nil {
 	if {{var "l"}} <= 0 {
 	if {{var "l"}} <= 0 {
-		{{var "v"}} = []{{ .Typ }}{}
+        {{var "v"}} = make({{ .CTyp }}, 0)
 	} else {
 	} else {
-		{{var "v"}} = make([]{{ .Typ }}, {{var "l"}}, {{var "l"}})
+		{{var "v"}} = make({{ .CTyp }}, {{var "l"}})
 	}
 	}
 	{{var "c"}} = true 
 	{{var "c"}} = true 
 } 
 } 
 {{ end }}
 {{ end }}
-if {{var "l"}} == 0 { {{ if not .Array }}
+if {{var "l"}} == 0 { {{ if isSlice }}
 	if len({{var "v"}}) != 0 { 
 	if len({{var "v"}}) != 0 { 
 		{{var "v"}} = {{var "v"}}[:0] 
 		{{var "v"}} = {{var "v"}}[:0] 
 		{{var "c"}} = true 
 		{{var "c"}} = true 
 	} {{ end }}
 	} {{ end }}
 } else if {{var "l"}} > 0 {
 } 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"}} 
 	{{var "n"}} := {{var "l"}} 
 	if {{var "l"}} > cap({{var "v"}}) {
 	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"}})
 		{{var "n"}} = len({{var "v"}})
 		{{ else }}{{ if .Immutable }}
 		{{ else }}{{ if .Immutable }}
 		{{var "v2"}} := {{var "v"}}
 		{{var "v2"}} := {{var "v"}}
@@ -95,30 +100,32 @@ if {{var "l"}} == 0 { {{ if not .Array }}
 	{{var "j"}} := 0
 	{{var "j"}} := 0
 	for ; {{var "j"}} < {{var "n"}} ; {{var "j"}}++ {
 	for ; {{var "j"}} < {{var "n"}} ; {{var "j"}}++ {
 		{{ $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 }}
-	} {{ if .Array }}
+	} {{ if isArray }}
 	for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
 	for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
 		z.DecSwallow()
 		z.DecSwallow()
 	}{{ end }}
 	}{{ end }}
+	{{ end }}{{/* closing if not chan */}}
 } else {
 } else {
 	for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
 	for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
 		if {{var "j"}} >= len({{var "v"}}) {
 		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 }}
 			{{var "c"}} = true {{ end }}
 		}
 		}
 		if {{var "j"}} > 0 {
 		if {{var "j"}} > 0 {
 			{{var "h"}}.Sep({{var "j"}})
 			{{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"}}) {
 		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 {
 		} 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()
 	{{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.linef("func (x %s) enc%s(v %s, e *%sEncoder) {", x.hn, x.genMethodNameT(t), x.genTypeName(t), x.cpfx)
 		x.genRequiredMethodVars(true)
 		x.genRequiredMethodVars(true)
 		switch t.Kind() {
 		switch t.Kind() {
-		case reflect.Array, reflect.Slice:
+		case reflect.Array, reflect.Slice, reflect.Chan:
 			x.encListFallback("v", rtid, t)
 			x.encListFallback("v", rtid, t)
 		case reflect.Map:
 		case reflect.Map:
 			x.encMapFallback("v", rtid, t)
 			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.linef("func (x %s) dec%s(v *%s, d *%sDecoder) {", x.hn, x.genMethodNameT(t), x.genTypeName(t), x.cpfx)
 		x.genRequiredMethodVars(false)
 		x.genRequiredMethodVars(false)
 		switch t.Kind() {
 		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:
 		case reflect.Map:
 			x.decMapFallback("v", rtid, t)
 			x.decMapFallback("v", rtid, t)
 		default:
 		default:
@@ -292,7 +290,7 @@ func (x *genRunner) genRefPkgs(t reflect.Type) {
 		x.im[tpkg] = t
 		x.im[tpkg] = t
 	}
 	}
 	switch t.Kind() {
 	switch t.Kind() {
-	case reflect.Array, reflect.Slice, reflect.Ptr:
+	case reflect.Array, reflect.Slice, reflect.Ptr, reflect.Chan:
 		x.genRefPkgs(t.Elem())
 		x.genRefPkgs(t.Elem())
 	case reflect.Map:
 	case reflect.Map:
 		x.genRefPkgs(t.Elem())
 		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) {
 func (x *genRunner) encVar(varname string, t reflect.Type) {
 	var checkNil bool
 	var checkNil bool
 	switch t.Kind() {
 	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
 		checkNil = true
 	}
 	}
 	if checkNil {
 	if checkNil {
@@ -500,7 +498,7 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
 		x.line("r.EncodeBool(bool(" + varname + "))")
 		x.line("r.EncodeBool(bool(" + varname + "))")
 	case reflect.String:
 	case reflect.String:
 		x.line("r.EncodeString(codecSelferC_UTF8" + x.xs + ", string(" + varname + "))")
 		x.line("r.EncodeString(codecSelferC_UTF8" + x.xs + ", string(" + varname + "))")
-	case reflect.Array:
+	case reflect.Array, reflect.Chan:
 		x.xtraSM(varname, true, t)
 		x.xtraSM(varname, true, t)
 		// x.encListFallback(varname, rtid, t)
 		// x.encListFallback(varname, rtid, t)
 	case reflect.Slice:
 	case reflect.Slice:
@@ -617,7 +615,7 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 		switch t2.Type.Kind() {
 		switch t2.Type.Kind() {
 		case reflect.Struct:
 		case reflect.Struct:
 			omitline += " true"
 			omitline += " true"
-		case reflect.Map, reflect.Slice, reflect.Array:
+		case reflect.Map, reflect.Slice, reflect.Array, reflect.Chan:
 			omitline += "len(" + varname + "." + t2.Name + ") != 0"
 			omitline += "len(" + varname + "." + t2.Name + ") != 0"
 		default:
 		default:
 			omitline += varname + "." + t2.Name + " != " + genZeroValueR(t2.Type, x.tc)
 			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) {
 func (x *genRunner) encListFallback(varname string, rtid uintptr, t reflect.Type) {
 	i := x.varsfx()
 	i := x.varsfx()
+	g := genTempVarPfx
 	x.line("r.EncodeArrayStart(len(" + varname + "))")
 	x.line("r.EncodeArrayStart(len(" + varname + "))")
 	x.line(genTempVarPfx + "s" + i + " := !z.EncBinary()")
 	x.line(genTempVarPfx + "s" + i + " := !z.EncBinary()")
 	x.line("if " + genTempVarPfx + "s" + i + " {")
 	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.line("if " + genTempVarPfx + "i" + i + " > 0 { r.EncodeArrayEntrySeparator() }")
 	x.encVar(genTempVarPfx+"v"+i, t.Elem())
 	x.encVar(genTempVarPfx+"v"+i, t.Elem())
 	x.line("}")
 	x.line("}")
 	x.line("r.EncodeArrayEnd()")
 	x.line("r.EncodeArrayEnd()")
 	x.line("} else {")
 	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.encVar(genTempVarPfx+"v"+i, t.Elem())
 	x.line("}")
 	x.line("}")
 	x.line("}")
 	x.line("}")
@@ -932,7 +941,7 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 	case reflect.String:
 	case reflect.String:
 		x.line("*((*string)(" + varname + ")) = r.DecodeString()")
 		x.line("*((*string)(" + varname + ")) = r.DecodeString()")
 		// x.line("z.DecString((*string)(" + varname + "))")
 		// x.line("z.DecString((*string)(" + varname + "))")
-	case reflect.Array:
+	case reflect.Array, reflect.Chan:
 		x.xtraSM(varname, false, t)
 		x.xtraSM(varname, false, t)
 		// x.decListFallback(varname, rtid, true, t)
 		// x.decListFallback(varname, rtid, true, t)
 	case reflect.Slice:
 	case reflect.Slice:
@@ -1019,18 +1028,17 @@ func (x *genRunner) decTryAssignPrimitive(varname string, t reflect.Type) (tryAs
 	return
 	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 {
 	type tstruc struct {
 		TempVar   string
 		TempVar   string
 		Rand      string
 		Rand      string
 		Varname   string
 		Varname   string
+		CTyp      string
 		Typ       string
 		Typ       string
-		Array     bool
 		Immutable bool
 		Immutable bool
 	}
 	}
 	telem := t.Elem()
 	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 := make(template.FuncMap)
 	funcs["decLineVar"] = func(varname string) string {
 	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 {
 	funcs["zero"] = func() string {
 		return genZeroValueR(telem, x.tc)
 		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)
 	tm, err := template.New("").Funcs(funcs).Parse(genDecListTmpl)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
@@ -1344,6 +1361,8 @@ func genTypeName(t reflect.Type, tRef reflect.Type) (n string) {
 		return ptrPfx + "[]" + genTypeName(t.Elem(), tRef)
 		return ptrPfx + "[]" + genTypeName(t.Elem(), tRef)
 	case reflect.Array:
 	case reflect.Array:
 		return ptrPfx + "[" + strconv.FormatInt(int64(t.Len()), 10) + "]" + genTypeName(t.Elem(), tRef)
 		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:
 	default:
 		if t == intfTyp {
 		if t == intfTyp {
 			return ptrPfx + "interface{}"
 			return ptrPfx + "interface{}"
@@ -1370,6 +1389,17 @@ func genMethodNameT(t reflect.Type, tRef reflect.Type) (n string) {
 		return ptrPfx + "Slice" + genMethodNameT(t.Elem(), tRef)
 		return ptrPfx + "Slice" + genMethodNameT(t.Elem(), tRef)
 	case reflect.Array:
 	case reflect.Array:
 		return ptrPfx + "Array" + strconv.FormatInt(int64(t.Len()), 10) + genMethodNameT(t.Elem(), tRef)
 		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:
 	default:
 		if t == intfTyp {
 		if t == intfTyp {
 			return ptrPfx + "Interface"
 			return ptrPfx + "Interface"

+ 11 - 2
codec/helper.go

@@ -183,6 +183,15 @@ const (
 	// valueTypeInvalid = 0xff
 	// valueTypeInvalid = 0xff
 )
 )
 
 
+type seqType uint8
+
+const (
+	_ seqType = iota
+	seqTypeArray
+	seqTypeSlice
+	seqTypeChan
+)
+
 var (
 var (
 	bigen               = binary.BigEndian
 	bigen               = binary.BigEndian
 	structInfoFieldName = "_struct"
 	structInfoFieldName = "_struct"
@@ -692,8 +701,8 @@ func rgetTypeInfo(rt reflect.Type, indexstack []int, fnameToHastag map[string]bo
 ) {
 ) {
 	for j := 0; j < rt.NumField(); j++ {
 	for j := 0; j < rt.NumField(); j++ {
 		f := rt.Field(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
 			continue
 		}
 		}
 		stag := f.Tag.Get(structTagName)
 		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('"')
 		e.w.writen1('"')
 	} else {
 	} else {
 		// e.EncodeString(c, string(v))
 		// e.EncodeString(c, string(v))
-		e.quoteBytes(v)
+		e.quoteStr(stringView(v))
 	}
 	}
 }
 }
 
 
@@ -224,9 +224,10 @@ func (e *jsonEncDriver) quoteStr(s string) {
 			case '\t':
 			case '\t':
 				w.writen2('\\', 't')
 				w.writen2('\\', 't')
 			default:
 			default:
+				// encode all bytes < 0x20 (except \r, \n).
+				// also encode < > & to prevent security holes when served to some browsers.
 				w.writestr(`\u00`)
 				w.writestr(`\u00`)
-				w.writen1(hex[b>>4])
-				w.writen1(hex[b&0xF])
+				w.writen2(hex[b>>4], hex[b&0xF])
 			}
 			}
 			i++
 			i++
 			start = i
 			start = i
@@ -262,75 +263,6 @@ func (e *jsonEncDriver) quoteStr(s string) {
 	w.writen1('"')
 	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 {
 type jsonNum struct {
@@ -456,32 +388,13 @@ func (d *jsonDecDriver) CheckBreak() bool {
 	return b == '}' || b == ']'
 	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) {
 func (d *jsonDecDriver) readStrIdx(fromIdx, toIdx uint8) {
+	bs := d.r.readx(int(toIdx - fromIdx))
 	if jsonValidateSymbols {
 	if jsonValidateSymbols {
-		bs := d.b[:(toIdx - fromIdx)]
-		d.r.readb(bs)
 		if !bytes.Equal(bs, jsonLiterals[fromIdx:toIdx]) {
 		if !bytes.Equal(bs, jsonLiterals[fromIdx:toIdx]) {
 			d.d.errorf("json: expecting %s: got %s", jsonLiterals[fromIdx:toIdx], bs)
 			d.d.errorf("json: expecting %s: got %s", jsonLiterals[fromIdx:toIdx], bs)
 			return
 			return
 		}
 		}
-	} else {
-		d.r.readx(int(toIdx - fromIdx))
 	}
 	}
 	if jsonTrackSkipWhitespace {
 	if jsonTrackSkipWhitespace {
 		d.wsSkipped = false
 		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
 # generated files and put stubs in place, before calling "go run" again
 # to recreate them.
 # to recreate them.
 _build() {
 _build() {
-    if [[ "${zforce}" == "1" ||
+    if ! [[ "${zforce}" == "1" ||
                 "1" == $( _needgen "fast-path.generated.go" ) ||
                 "1" == $( _needgen "fast-path.generated.go" ) ||
                 "1" == $( _needgen "gen-helper.generated.go" ) ||
                 "1" == $( _needgen "gen-helper.generated.go" ) ||
                 "1" == $( _needgen "gen.generated.go" ) ||
                 "1" == $( _needgen "gen.generated.go" ) ||
                 1 == 0 ]]
                 1 == 0 ]]
     then
     then
-        true  # continue. do not return
-    else 
         return 0
         return 0
     fi 
     fi 
 
 
@@ -51,7 +49,7 @@ _build() {
         # [ -e "safe${_gg}" ] && mv safe${_gg} safe${_gg}__${_zts}.bak
         # [ -e "safe${_gg}" ] && mv safe${_gg} safe${_gg}__${_zts}.bak
         # [ -e "unsafe${_gg}" ] && mv unsafe${_gg} unsafe${_gg}__${_zts}.bak
         # [ -e "unsafe${_gg}" ] && mv unsafe${_gg} unsafe${_gg}__${_zts}.bak
     else 
     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
     fi
 
 
     cat > gen.generated.go <<EOF
     cat > gen.generated.go <<EOF