浏览代码

codec: streamline panic/recover error handling, manage compatibility with codecgen and support structs where fields are encoded as numbers

Add diagnostic information (encode format, num bytes read, etc) to error on panic-recover.
This required the following changes:
- errors are either exposed as a string or an error, and used as the panic value
- on recover (in Encoder.Encode or Decoder.Decode), we add diagnostics to the panic value and return as error

encDriver for a handle should not check for overflow32 - instead, just expose a DecodeFloat64() method

We currently keep some deprecated methods alive in (en|de)cDriver, so that codecgen will not need to be re-done.
Instead, for compatibility in codecgen, make the r variable a struct wrapping (en|de)cDriver.
Thereafter, add the compatibility functions, so they can be removed from (en|de)cDriver.

Make clear that float overflow doesn't mean that the number maintains its precision.

In JSON, always encode and decode as float64.
If not, we lose precision when we encode as float32 and decode back as float64 (e.g. for math.MaxFloat32).

Support encoding/decoding structs where the encoded stream should use number codes for the field names (instead of strings).
This is now supported by configuring the key type on the _struct field,
and supported in codecgen and in the reflection-based general support.
See documentation on (*Encoder).Encode.
Ugorji Nwoke 8 年之前
父节点
当前提交
4d084a8659

+ 4 - 5
codec/binc.go

@@ -554,7 +554,7 @@ func (d *bincDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
 	return
 }
 
-func (d *bincDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+func (d *bincDecDriver) DecodeFloat64() (f float64) {
 	if !d.bdRead {
 		d.readNextBd()
 	}
@@ -578,10 +578,6 @@ func (d *bincDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
 	} else {
 		f = float64(d.DecodeInt(64))
 	}
-	if chkOverflow32 && chkOvf.Float32(f) {
-		d.d.errorf("binc: float32 overflow: %v", f)
-		return
-	}
 	d.bdRead = false
 	return
 }
@@ -938,6 +934,9 @@ type BincHandle struct {
 	noElemSeparators
 }
 
+// Name returns the name of the handle: binc
+func (h *BincHandle) Name() string { return "binc" }
+
 // SetBytesExt sets an extension
 func (h *BincHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
 	return h.SetExt(rt, tag, &setExtWrapper{b: ext})

+ 9 - 13
codec/cbor.go

@@ -389,7 +389,7 @@ func (d *cborDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
 	return
 }
 
-func (d *cborDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+func (d *cborDecDriver) DecodeFloat64() (f float64) {
 	if !d.bdRead {
 		d.readNextBd()
 	}
@@ -405,10 +405,6 @@ func (d *cborDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
 		d.d.errorf("Float only valid from float16/32/64: Invalid descriptor: %v", bd)
 		return
 	}
-	if chkOverflow32 && chkOvf.Float32(f) {
-		d.d.errorf("cbor: float32 overflow: %v", f)
-		return
-	}
 	d.bdRead = false
 	return
 }
@@ -544,17 +540,17 @@ func (d *cborDecDriver) decodeTime(xtag uint64) (t time.Time) {
 	case 0:
 		var err error
 		if t, err = time.Parse(time.RFC3339, stringView(d.DecodeStringAsBytes())); err != nil {
-			d.d.error(err)
+			d.d.errorv(err)
 		}
 	case 1:
 		// decode an int64 or a float, and infer time.Time from there.
 		// for floats, round to microseconds, as that is what is guaranteed to fit well.
 		switch {
 		case d.bd == cborBdFloat16, d.bd == cborBdFloat32:
-			f1, f2 := math.Modf(d.DecodeFloat(true))
+			f1, f2 := math.Modf(d.DecodeFloat64())
 			t = time.Unix(int64(f1), int64(f2*1e9))
 		case d.bd == cborBdFloat64:
-			f1, f2 := math.Modf(d.DecodeFloat(false))
+			f1, f2 := math.Modf(d.DecodeFloat64())
 			t = time.Unix(int64(f1), int64(f2*1e9))
 		case d.bd >= cborBaseUint && d.bd < cborBaseNegInt, d.bd >= cborBaseNegInt && d.bd < cborBaseBytes:
 			t = time.Unix(d.DecodeInt(64), 0)
@@ -608,12 +604,9 @@ func (d *cborDecDriver) DecodeNaked() {
 	case cborBdTrue:
 		n.v = valueTypeBool
 		n.b = true
-	case cborBdFloat16, cborBdFloat32:
+	case cborBdFloat16, cborBdFloat32, cborBdFloat64:
 		n.v = valueTypeFloat
-		n.f = d.DecodeFloat(true)
-	case cborBdFloat64:
-		n.v = valueTypeFloat
-		n.f = d.DecodeFloat(false)
+		n.f = d.DecodeFloat64()
 	case cborBdIndefiniteBytes:
 		n.v = valueTypeBytes
 		n.l = d.DecodeBytes(nil, false)
@@ -705,6 +698,9 @@ type CborHandle struct {
 	TimeRFC3339 bool
 }
 
+// Name returns the name of the handle: cbor
+func (h *CborHandle) Name() string { return "cbor" }
+
 // SetInterfaceExt sets an extension
 func (h *CborHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
 	return h.SetExt(rt, tag, &setExtWrapper{i: ext})

+ 2 - 2
codec/codec_test.go

@@ -366,12 +366,12 @@ func testInit() {
 	// and use on some places for testSimpleH e.g. for time.Time and wrapInt64
 	var (
 		myExtEncFn = func(x BytesExt, rv reflect.Value) (bs []byte, err error) {
-			defer panicToErr(&err)
+			defer panicToErr(errstrDecoratorDef{}, &err)
 			bs = x.WriteExt(rv.Interface())
 			return
 		}
 		myExtDecFn = func(x BytesExt, rv reflect.Value, bs []byte) (err error) {
-			defer panicToErr(&err)
+			defer panicToErr(errstrDecoratorDef{}, &err)
 			x.ReadExt(rv.Interface(), bs)
 			return
 		}

+ 58 - 59
codec/decode.go

@@ -9,6 +9,7 @@ import (
 	"fmt"
 	"io"
 	"reflect"
+	"strconv"
 	"sync"
 	"time"
 )
@@ -20,8 +21,11 @@ const (
 )
 
 var (
-	errOnlyMapOrArrayCanDecodeIntoStruct = errors.New("only encoded map or array can be decoded into a struct")
-	errCannotDecodeIntoNil               = errors.New("cannot decode into nil")
+	errstrOnlyMapOrArrayCanDecodeIntoStruct = "only encoded map or array can be decoded into a struct"
+	errstrCannotDecodeIntoNil               = "cannot decode into nil"
+
+	errmsgExpandSliceOverflow     = "expand slice: slice overflow"
+	errmsgExpandSliceCannotChange = "expand slice: cannot change"
 
 	errDecUnreadByteNothingToRead   = errors.New("cannot unread - nothing has been read")
 	errDecUnreadByteLastByteNotRead = errors.New("cannot unread - last byte has not been read")
@@ -63,9 +67,6 @@ type decDriver interface {
 	ContainerType() (vt valueType)
 	// IsBuiltinType(rt uintptr) bool
 
-	// Deprecated: left here for now so that old codecgen'ed filed will work. TODO: remove.
-	DecodeBuiltin(rt uintptr, v interface{})
-
 	// DecodeNaked will decode primitives (number, bool, string, []byte) and RawExt.
 	// For maps and arrays, it will not do the decoding in-band, but will signal
 	// the decoder, so that is done later, by setting the decNaked.valueType field.
@@ -78,11 +79,12 @@ type decDriver interface {
 	// extensions should also use readx to decode them, for efficiency.
 	// kInterface will extract the detached byte slice if it has to pass it outside its realm.
 	DecodeNaked()
+
 	DecodeInt(bitsize uint8) (i int64)
 	DecodeUint(bitsize uint8) (ui uint64)
-	DecodeFloat(chkOverflow32 bool) (f float64)
+
+	DecodeFloat64() (f float64)
 	DecodeBool() (b bool)
-	DecodeTime() (t time.Time)
 	// DecodeString can also decode symbols.
 	// It looks redundant as DecodeBytes is available.
 	// However, some codecs (e.g. binc) support symbols and can
@@ -99,6 +101,9 @@ type decDriver interface {
 	// decodeExt will decode into a *RawExt or into an extension.
 	DecodeExt(v interface{}, xtag uint64, ext Ext) (realxtag uint64)
 	// decodeExt(verifyTag bool, tag byte) (xtag byte, xbs []byte)
+
+	DecodeTime() (t time.Time)
+
 	ReadArrayStart() int
 	ReadArrayElem()
 	ReadArrayEnd()
@@ -1125,6 +1130,22 @@ func (d *Decoder) kInterface(f *codecFnInfo, rv reflect.Value) {
 	rv.Set(rvn2)
 }
 
+func decStructFieldKey(dd decDriver, keyType valueType, b *[scratchByteArrayLen]byte) (rvkencname []byte) {
+	switch keyType {
+	case valueTypeString:
+		rvkencname = (dd.DecodeStringAsBytes())
+	case valueTypeInt:
+		rvkencname = (strconv.AppendInt(b[:0], dd.DecodeInt(64), 10))
+	case valueTypeUint:
+		rvkencname = (strconv.AppendUint(b[:0], dd.DecodeUint(64), 10))
+	case valueTypeFloat:
+		rvkencname = (strconv.AppendFloat(b[:0], dd.DecodeFloat64(), 'f', -1, 64))
+	default: // string
+		rvkencname = (dd.DecodeStringAsBytes())
+	}
+	return
+}
+
 func (d *Decoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 	fti := f.ti
 	dd := d.d
@@ -1140,14 +1161,12 @@ func (d *Decoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 		tisfi := fti.sfi
 		hasLen := containerLen >= 0
 
+		var rvkencname string
 		for j := 0; (hasLen && j < containerLen) || !(hasLen || dd.CheckBreak()); j++ {
-			// rvkencname := dd.DecodeString()
 			if elemsep {
 				dd.ReadMapElemKey()
 			}
-			rvkencnameB := dd.DecodeStringAsBytes()
-			rvkencname := stringView(rvkencnameB)
-			// rvksi := ti.getForEncName(rvkencname)
+			rvkencname = stringView(decStructFieldKey(dd, fti.keyType, &d.b))
 			if elemsep {
 				dd.ReadMapElemValue()
 			}
@@ -1197,7 +1216,7 @@ func (d *Decoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 		}
 		dd.ReadArrayEnd()
 	} else {
-		d.error(errOnlyMapOrArrayCanDecodeIntoStruct)
+		d.errorstr(errstrOnlyMapOrArrayCanDecodeIntoStruct)
 		return
 	}
 }
@@ -1365,7 +1384,11 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 				} else { // if f.seq == seqTypeSlice
 					// rv = reflect.Append(rv, reflect.Zero(rtelem0)) // uses append logic, plus varargs
 					var rvcap2 int
-					rv9, rvcap2, rvChanged = d.expandSliceRV(rv, ti.rt, rvCanset, rtelem0Size, 1, rvlen, rvcap)
+					var rvErrmsg2 string
+					rv9, rvcap2, rvChanged, rvErrmsg2 = expandSliceRV(rv, ti.rt, rvCanset, rtelem0Size, 1, rvlen, rvcap)
+					if rvErrmsg2 != "" {
+						d.errorf(rvErrmsg2)
+					}
 					rvlen++
 					if rvChanged {
 						rv = rv9
@@ -1765,6 +1788,7 @@ type decReaderSwitch struct {
 
 // A Decoder reads and decodes an object from an input stream in the codec format.
 type Decoder struct {
+	panicHdl
 	// hopefully, reduce derefencing cost by laying the decReader inside the Decoder.
 	// Try to put things that go together to fit within a cache line (8 words).
 
@@ -1961,7 +1985,7 @@ func (d *Decoder) ResetBytes(in []byte) {
 // Note: we allow nil values in the stream anywhere except for map keys.
 // A nil value in the encoded stream where a map key is expected is treated as an error.
 func (d *Decoder) Decode(v interface{}) (err error) {
-	defer panicToErrs2(&d.err, &err)
+	defer panicToErrs2(d, &d.err, &err)
 	d.MustDecode(v)
 	return
 }
@@ -2109,7 +2133,7 @@ func (d *Decoder) decode(iv interface{}) {
 	// check nil and interfaces explicitly,
 	// so that type switches just have a run of constant non-interface types.
 	if iv == nil {
-		d.error(errCannotDecodeIntoNil)
+		d.errorstr(errstrCannotDecodeIntoNil)
 		return
 	}
 	if v, ok := iv.(Selfer); ok {
@@ -2150,9 +2174,13 @@ func (d *Decoder) decode(iv interface{}) {
 	case *uint64:
 		*v = d.d.DecodeUint(64)
 	case *float32:
-		*v = float32(d.d.DecodeFloat(true))
+		f64 := d.d.DecodeFloat64()
+		if chkOvf.Float32(f64) {
+			d.errorf("float32 overflow: %v", f64)
+		}
+		*v = float32(f64)
 	case *float64:
-		*v = d.d.DecodeFloat(false)
+		*v = d.d.DecodeFloat64()
 	case *[]uint8:
 		*v = d.d.DecodeBytes(*v, false)
 	case []uint8:
@@ -2266,7 +2294,7 @@ func (d *Decoder) ensureDecodeable(rv reflect.Value) (rv2 reflect.Value) {
 		return
 	}
 	if !rv.IsValid() {
-		d.error(errCannotDecodeIntoNil)
+		d.errorstr(errstrCannotDecodeIntoNil)
 		return
 	}
 	if !rv.CanInterface() {
@@ -2279,42 +2307,6 @@ func (d *Decoder) ensureDecodeable(rv reflect.Value) (rv2 reflect.Value) {
 	return
 }
 
-// func (d *Decoder) chkPtrValue(rv reflect.Value) {
-// 	// We can only decode into a non-nil pointer
-// 	if rv.Kind() == reflect.Ptr && !rv.IsNil() {
-// 		return
-// 	}
-// 	d.errNotValidPtrValue(rv)
-// }
-
-// func (d *Decoder) errNotValidPtrValue(rv reflect.Value) {
-// 	if !rv.IsValid() {
-// 		d.error(errCannotDecodeIntoNil)
-// 		return
-// 	}
-// 	if !rv.CanInterface() {
-// 		d.errorf("cannot decode into a value without an interface: %v", rv)
-// 		return
-// 	}
-// 	rvi := rv2i(rv)
-// 	d.errorf("cannot decode into non-pointer or nil pointer. Got: %v, %T, %v", rv.Kind(), rvi, rvi)
-// }
-
-func (d *Decoder) error(err error) {
-	panic(err)
-}
-
-func (d *Decoder) errorvf(format string, params ...interface{}) (err error) {
-	params2 := make([]interface{}, len(params)+1)
-	params2[0] = d.r.numread()
-	copy(params2[1:], params)
-	return fmt.Errorf("[pos %d]: "+format, params2...)
-}
-
-func (d *Decoder) errorf(format string, params ...interface{}) {
-	panic(d.errorvf(format, params...))
-}
-
 // Possibly get an interned version of a string
 //
 // This should mostly be used for map keys, where the key type is string.
@@ -2349,6 +2341,10 @@ func (d *Decoder) rawBytes() []byte {
 	return bs2
 }
 
+func (d *Decoder) wrapErrstr(v interface{}, err *error) {
+	*err = fmt.Errorf("%s decode error [pos %d]: %v", d.hh.Name(), d.r.numread(), v)
+}
+
 // --------------------------------------------------
 
 // decSliceHelper assists when decoding into a slice, from a map or an array in the stream.
@@ -2469,11 +2465,12 @@ func decInferLen(clen, maxlen, unit int) (rvlen int) {
 	return
 }
 
-func (d *Decoder) expandSliceRV(s reflect.Value, st reflect.Type, canChange bool, stElemSize, num, slen, scap int) (
-	s2 reflect.Value, scap2 int, changed bool) {
+func expandSliceRV(s reflect.Value, st reflect.Type, canChange bool, stElemSize, num, slen, scap int) (
+	s2 reflect.Value, scap2 int, changed bool, err string) {
 	l1 := slen + num // new slice length
 	if l1 < slen {
-		d.errorf("expand slice: slice overflow")
+		err = errmsgExpandSliceOverflow
+		return
 	}
 	if l1 <= scap {
 		if s.CanSet() {
@@ -2483,12 +2480,14 @@ func (d *Decoder) expandSliceRV(s reflect.Value, st reflect.Type, canChange bool
 			scap2 = scap
 			changed = true
 		} else {
-			d.errorf("expand slice: cannot change")
+			err = errmsgExpandSliceCannotChange
+			return
 		}
 		return
 	}
 	if !canChange {
-		d.errorf("expand slice: cannot change")
+		err = errmsgExpandSliceCannotChange
+		return
 	}
 	scap2 = growCap(scap, stElemSize, num)
 	s2 = reflect.MakeSlice(st, l1, scap2)

+ 65 - 152
codec/encode.go

@@ -11,6 +11,7 @@ import (
 	"io"
 	"reflect"
 	"sort"
+	"strconv"
 	"sync"
 	"time"
 )
@@ -178,30 +179,6 @@ type simpleIoEncWriter struct {
 	io.Writer
 }
 
-// type bufIoEncWriter struct {
-// 	w   io.Writer
-// 	buf []byte
-// 	err error
-// }
-
-// func (x *bufIoEncWriter) Write(b []byte) (n int, err error) {
-// 	if x.err != nil {
-// 		return 0, x.err
-// 	}
-// 	if cap(x.buf)-len(x.buf) >= len(b) {
-// 		x.buf = append(x.buf, b)
-// 		return len(b), nil
-// 	}
-// 	n, err = x.w.Write(x.buf)
-// 	if err != nil {
-// 		x.err = err
-// 		return 0, x.err
-// 	}
-// 	n, err = x.w.Write(b)
-// 	x.err = err
-// 	return
-// }
-
 // ioEncWriter implements encWriter and can write to an io.Writer implementation
 type ioEncWriter struct {
 	w  io.Writer
@@ -271,84 +248,6 @@ func (z *ioEncWriter) atEndOfEncode() {
 	}
 }
 
-// // ----------------------------------------
-
-// // bytesEncWriter implements encWriter and can write to an byte slice.
-// // It is used by Marshal function.
-// type bytesEncWriter struct {
-// 	b   []byte
-// 	c   int     // cursor
-// 	out *[]byte // write out on atEndOfEncode
-// }
-
-// func (z *bytesEncWriter) writeb(s []byte) {
-// 	oc, a := z.growNoAlloc(len(s))
-// 	if a {
-// 		z.growAlloc(len(s), oc)
-// 	}
-// 	copy(z.b[oc:], s)
-// }
-
-// func (z *bytesEncWriter) writestr(s string) {
-// 	oc, a := z.growNoAlloc(len(s))
-// 	if a {
-// 		z.growAlloc(len(s), oc)
-// 	}
-// 	copy(z.b[oc:], s)
-// }
-
-// func (z *bytesEncWriter) writen1(b1 byte) {
-// 	oc, a := z.growNoAlloc(1)
-// 	if a {
-// 		z.growAlloc(1, oc)
-// 	}
-// 	z.b[oc] = b1
-// }
-
-// func (z *bytesEncWriter) writen2(b1, b2 byte) {
-// 	oc, a := z.growNoAlloc(2)
-// 	if a {
-// 		z.growAlloc(2, oc)
-// 	}
-// 	z.b[oc+1] = b2
-// 	z.b[oc] = b1
-// }
-
-// func (z *bytesEncWriter) atEndOfEncode() {
-// 	*(z.out) = z.b[:z.c]
-// }
-
-// // have a growNoalloc(n int), which can be inlined.
-// // if allocation is needed, then call growAlloc(n int)
-
-// func (z *bytesEncWriter) growNoAlloc(n int) (oldcursor int, allocNeeded bool) {
-// 	oldcursor = z.c
-// 	z.c = z.c + n
-// 	if z.c > len(z.b) {
-// 		if z.c > cap(z.b) {
-// 			allocNeeded = true
-// 		} else {
-// 			z.b = z.b[:cap(z.b)]
-// 		}
-// 	}
-// 	return
-// }
-
-// func (z *bytesEncWriter) growAlloc(n int, oldcursor int) {
-// 	// appendslice logic (if cap < 1024, *2, else *1.25): more expensive. many copy calls.
-// 	// bytes.Buffer model (2*cap + n): much better
-// 	// bs := make([]byte, 2*cap(z.b)+n)
-// 	bs := make([]byte, growCap(cap(z.b), 1, n))
-// 	copy(bs, z.b[:oldcursor])
-// 	z.b = bs
-// }
-
-// func (z *bytesEncWriter) reset(in []byte, out *[]byte) {
-// 	z.out = out
-// 	z.b = in
-// 	z.c = 0
-// }
-
 // ---------------------------------------------
 
 // bytesEncAppender implements encWriter and can write to an byte slice.
@@ -384,16 +283,6 @@ func (z *bytesEncAppender) reset(in []byte, out *[]byte) {
 // }
 
 func (e *Encoder) rawExt(f *codecFnInfo, rv reflect.Value) {
-	// rev := rv2i(rv).(RawExt)
-	// e.e.EncodeRawExt(&rev, e)
-	// var re *RawExt
-	// if rv.CanAddr() {
-	// 	re = rv2i(rv.Addr()).(*RawExt)
-	// } else {
-	// 	rev := rv2i(rv).(RawExt)
-	// 	re = &rev
-	// }
-	// e.e.EncodeRawExt(re, e)
 	e.e.EncodeRawExt(rv2i(rv).(*RawExt), e)
 }
 
@@ -580,37 +469,29 @@ func (e *Encoder) kStructNoOmitempty(f *codecFnInfo, rv reflect.Value) {
 		ee.WriteMapStart(len(tisfi))
 		// asSymbols := e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
 		asSymbols := e.h.AsSymbols == AsSymbolDefault || e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
-		if !elemsep {
+		if elemsep {
 			for _, si := range tisfi {
-				if asSymbols {
-					ee.EncodeSymbol(si.encName)
-				} else {
-					ee.EncodeString(cUTF8, si.encName)
-				}
+				ee.WriteMapElemKey()
+				encStructFieldKey(ee, fti.keyType, si.encName, asSymbols)
+				ee.WriteMapElemValue()
 				e.encodeValue(sfn.field(si), nil, true)
 			}
 		} else {
 			for _, si := range tisfi {
-				ee.WriteMapElemKey()
-				if asSymbols {
-					ee.EncodeSymbol(si.encName)
-				} else {
-					ee.EncodeString(cUTF8, si.encName)
-				}
-				ee.WriteMapElemValue()
+				encStructFieldKey(ee, fti.keyType, si.encName, asSymbols)
 				e.encodeValue(sfn.field(si), nil, true)
 			}
 		}
 		ee.WriteMapEnd()
 	} else {
 		ee.WriteArrayStart(len(tisfi))
-		if !elemsep {
+		if elemsep {
 			for _, si := range tisfi {
+				ee.WriteArrayElem()
 				e.encodeValue(sfn.field(si), nil, true)
 			}
 		} else {
 			for _, si := range tisfi {
-				ee.WriteArrayElem()
 				e.encodeValue(sfn.field(si), nil, true)
 			}
 		}
@@ -618,6 +499,30 @@ func (e *Encoder) kStructNoOmitempty(f *codecFnInfo, rv reflect.Value) {
 	}
 }
 
+func encStructFieldKey(ee encDriver, keyType valueType, s string, asSymbols bool) {
+	var m must
+	switch keyType {
+	case valueTypeString:
+		if asSymbols {
+			ee.EncodeSymbol(s)
+		} else {
+			ee.EncodeString(cUTF8, s)
+		}
+	case valueTypeInt:
+		ee.EncodeInt(m.Int(strconv.ParseInt(s, 10, 64)))
+	case valueTypeUint:
+		ee.EncodeUint(m.Uint(strconv.ParseUint(s, 10, 64)))
+	case valueTypeFloat:
+		ee.EncodeFloat64(m.Float(strconv.ParseFloat(s, 64)))
+	default: // string
+		if asSymbols {
+			ee.EncodeSymbol(s)
+		} else {
+			ee.EncodeString(cUTF8, s)
+		}
+	}
+}
+
 func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 	fti := f.ti
 	elemsep := e.esep
@@ -694,39 +599,31 @@ func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 		ee.WriteMapStart(newlen)
 		// asSymbols := e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
 		asSymbols := e.h.AsSymbols == AsSymbolDefault || e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
-		if !elemsep {
+		if elemsep {
 			for j := 0; j < newlen; j++ {
 				kv = fkvs[j]
-				if asSymbols {
-					ee.EncodeSymbol(kv.v)
-				} else {
-					ee.EncodeString(cUTF8, kv.v)
-				}
+				ee.WriteMapElemKey()
+				encStructFieldKey(ee, fti.keyType, kv.v, asSymbols)
+				ee.WriteMapElemValue()
 				e.encodeValue(kv.r, nil, true)
 			}
 		} else {
 			for j := 0; j < newlen; j++ {
 				kv = fkvs[j]
-				ee.WriteMapElemKey()
-				if asSymbols {
-					ee.EncodeSymbol(kv.v)
-				} else {
-					ee.EncodeString(cUTF8, kv.v)
-				}
-				ee.WriteMapElemValue()
+				encStructFieldKey(ee, fti.keyType, kv.v, asSymbols)
 				e.encodeValue(kv.r, nil, true)
 			}
 		}
 		ee.WriteMapEnd()
 	} else {
 		ee.WriteArrayStart(newlen)
-		if !elemsep {
+		if elemsep {
 			for j := 0; j < newlen; j++ {
+				ee.WriteArrayElem()
 				e.encodeValue(fkvs[j].r, nil, true)
 			}
 		} else {
 			for j := 0; j < newlen; j++ {
-				ee.WriteArrayElem()
 				e.encodeValue(fkvs[j].r, nil, true)
 			}
 		}
@@ -1055,6 +952,7 @@ type encWriterSwitch struct {
 
 // An Encoder writes an object to an output stream in the codec format.
 type Encoder struct {
+	panicHdl
 	// hopefully, reduce derefencing cost by laying the encWriter inside the Encoder
 	e encDriver
 	// NOTE: Encoder shouldn't call it's write methods,
@@ -1180,7 +1078,14 @@ func (e *Encoder) ResetBytes(out *[]byte) {
 // Note that the "json" key is used in the absence of the "codec" key.
 //
 // To set an option on all fields (e.g. omitempty on all fields), you
-// can create a field called _struct, and set flags on it.
+// can create a field called _struct, and set flags on it. The options
+// which can be set on _struct are:
+//    - omitempty: so all fields are omitted if empty
+//    - toarray: so struct is encoded as an array
+//    - int: so struct key names are encoded as signed integers (instead of strings)
+//    - uint: so struct key names are encoded as unsigned integers (instead of strings)
+//    - float: so struct key names are encoded as floats (instead of strings)
+// More details on these below.
 //
 // Struct values "usually" encode as maps. Each exported struct field is encoded unless:
 //    - the field's tag is "-", OR
@@ -1188,6 +1093,13 @@ func (e *Encoder) ResetBytes(out *[]byte) {
 //
 // When encoding as a map, the first string in the tag (before the comma)
 // is the map key string to use when encoding.
+// ...
+// This key is typically encoded as a string.
+// However, there are instances where the encoded stream has mapping keys encoded as numbers.
+// For example, some cbor streams have keys as integer codes in the stream, but they should map
+// to fields in a structured object. Consequently, a struct is the natural representation in code.
+// For these, you can configure the struct to encode/decode the keys as numbers (instead of string).
+// This is done with the int,uint or float option on the _struct field (see above).
 //
 // However, struct values may encode as arrays. This happens when:
 //    - StructToArray Encode option is set, OR
@@ -1220,7 +1132,13 @@ func (e *Encoder) ResetBytes(out *[]byte) {
 //      }
 //
 //      type MyStruct struct {
-//          _struct bool    `codec:",toarray"`   //encode struct as an array
+//          _struct bool    `codec:",toarray"`     //encode struct as an array
+//      }
+//
+//      type MyStruct struct {
+//          _struct bool    `codec:",uint"`        //encode struct with "unsigned integer" keys
+//          Field1 string   `codec:"1"`            //encode Field1 key using: EncodeInt(1)
+//          Field2 string   `codec:"2"`            //encode Field2 key using: EncodeInt(2)
 //      }
 //
 // The mode of encoding is based on the type of the value. When a value is seen:
@@ -1233,7 +1151,7 @@ func (e *Encoder) ResetBytes(out *[]byte) {
 // Some formats support symbols (e.g. binc) and will properly encode the string
 // only once in the stream, and use a tag to refer to it thereafter.
 func (e *Encoder) Encode(v interface{}) (err error) {
-	defer panicToErrs2(&e.err, &err)
+	defer panicToErrs2(e, &e.err, &err)
 	e.MustEncode(v)
 	return
 }
@@ -1445,11 +1363,6 @@ func (e *Encoder) rawBytes(vv Raw) {
 	e.asis(v)
 }
 
-func (e *Encoder) errorf(format string, params ...interface{}) {
-	err := fmt.Errorf(format, params...)
-	panic(err)
-}
-
-func (e *Encoder) error(err error) {
-	panic(err)
+func (e *Encoder) wrapErrstr(v interface{}, err *error) {
+	*err = fmt.Errorf("%s encode error: %v", e.hh.Name(), v)
 }

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

@@ -16473,7 +16473,7 @@ func (_ fastpathT) DecSliceFloat32V(v []float32, canChange bool, d *Decoder) (_
 		} else if dd.TryDecodeAsNil() {
 			v[j] = 0
 		} else {
-			v[j] = float32(dd.DecodeFloat(true))
+			v[j] = float32(chkFloat32(dd.DecodeFloat64()))
 		}
 	}
 	if canChange {
@@ -16569,7 +16569,7 @@ func (_ fastpathT) DecSliceFloat64V(v []float64, canChange bool, d *Decoder) (_
 		} else if dd.TryDecodeAsNil() {
 			v[j] = 0
 		} else {
-			v[j] = dd.DecodeFloat(false)
+			v[j] = dd.DecodeFloat64()
 		}
 	}
 	if canChange {
@@ -18616,7 +18616,7 @@ func (_ fastpathT) DecMapIntfFloat32V(v map[interface{}]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -18679,7 +18679,7 @@ func (_ fastpathT) DecMapIntfFloat64V(v map[interface{}]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -19574,7 +19574,7 @@ func (_ fastpathT) DecMapStringFloat32V(v map[string]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -19633,7 +19633,7 @@ func (_ fastpathT) DecMapStringFloat64V(v map[string]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -19739,7 +19739,7 @@ func (_ fastpathT) DecMapFloat32IntfV(v map[float32]interface{}, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -19803,7 +19803,7 @@ func (_ fastpathT) DecMapFloat32StringV(v map[float32]string, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -19862,7 +19862,7 @@ func (_ fastpathT) DecMapFloat32UintV(v map[float32]uint, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -19921,7 +19921,7 @@ func (_ fastpathT) DecMapFloat32Uint8V(v map[float32]uint8, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -19980,7 +19980,7 @@ func (_ fastpathT) DecMapFloat32Uint16V(v map[float32]uint16, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20039,7 +20039,7 @@ func (_ fastpathT) DecMapFloat32Uint32V(v map[float32]uint32, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20098,7 +20098,7 @@ func (_ fastpathT) DecMapFloat32Uint64V(v map[float32]uint64, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20157,7 +20157,7 @@ func (_ fastpathT) DecMapFloat32UintptrV(v map[float32]uintptr, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20216,7 +20216,7 @@ func (_ fastpathT) DecMapFloat32IntV(v map[float32]int, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20275,7 +20275,7 @@ func (_ fastpathT) DecMapFloat32Int8V(v map[float32]int8, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20334,7 +20334,7 @@ func (_ fastpathT) DecMapFloat32Int16V(v map[float32]int16, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20393,7 +20393,7 @@ func (_ fastpathT) DecMapFloat32Int32V(v map[float32]int32, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20452,7 +20452,7 @@ func (_ fastpathT) DecMapFloat32Int64V(v map[float32]int64, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20511,7 +20511,7 @@ func (_ fastpathT) DecMapFloat32Float32V(v map[float32]float32, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20524,7 +20524,7 @@ func (_ fastpathT) DecMapFloat32Float32V(v map[float32]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -20570,7 +20570,7 @@ func (_ fastpathT) DecMapFloat32Float64V(v map[float32]float64, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20583,7 +20583,7 @@ func (_ fastpathT) DecMapFloat32Float64V(v map[float32]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -20629,7 +20629,7 @@ func (_ fastpathT) DecMapFloat32BoolV(v map[float32]bool, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = float32(dd.DecodeFloat(true))
+		mk = float32(chkFloat32(dd.DecodeFloat64()))
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20689,7 +20689,7 @@ func (_ fastpathT) DecMapFloat64IntfV(v map[float64]interface{}, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20753,7 +20753,7 @@ func (_ fastpathT) DecMapFloat64StringV(v map[float64]string, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20812,7 +20812,7 @@ func (_ fastpathT) DecMapFloat64UintV(v map[float64]uint, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20871,7 +20871,7 @@ func (_ fastpathT) DecMapFloat64Uint8V(v map[float64]uint8, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20930,7 +20930,7 @@ func (_ fastpathT) DecMapFloat64Uint16V(v map[float64]uint16, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -20989,7 +20989,7 @@ func (_ fastpathT) DecMapFloat64Uint32V(v map[float64]uint32, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21048,7 +21048,7 @@ func (_ fastpathT) DecMapFloat64Uint64V(v map[float64]uint64, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21107,7 +21107,7 @@ func (_ fastpathT) DecMapFloat64UintptrV(v map[float64]uintptr, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21166,7 +21166,7 @@ func (_ fastpathT) DecMapFloat64IntV(v map[float64]int, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21225,7 +21225,7 @@ func (_ fastpathT) DecMapFloat64Int8V(v map[float64]int8, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21284,7 +21284,7 @@ func (_ fastpathT) DecMapFloat64Int16V(v map[float64]int16, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21343,7 +21343,7 @@ func (_ fastpathT) DecMapFloat64Int32V(v map[float64]int32, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21402,7 +21402,7 @@ func (_ fastpathT) DecMapFloat64Int64V(v map[float64]int64, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21461,7 +21461,7 @@ func (_ fastpathT) DecMapFloat64Float32V(v map[float64]float32, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21474,7 +21474,7 @@ func (_ fastpathT) DecMapFloat64Float32V(v map[float64]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -21520,7 +21520,7 @@ func (_ fastpathT) DecMapFloat64Float64V(v map[float64]float64, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -21533,7 +21533,7 @@ func (_ fastpathT) DecMapFloat64Float64V(v map[float64]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -21579,7 +21579,7 @@ func (_ fastpathT) DecMapFloat64BoolV(v map[float64]bool, canChange bool,
 		if esep {
 			dd.ReadMapElemKey()
 		}
-		mk = dd.DecodeFloat(false)
+		mk = dd.DecodeFloat64()
 		if esep {
 			dd.ReadMapElemValue()
 		}
@@ -22424,7 +22424,7 @@ func (_ fastpathT) DecMapUintFloat32V(v map[uint]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -22483,7 +22483,7 @@ func (_ fastpathT) DecMapUintFloat64V(v map[uint]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -23374,7 +23374,7 @@ func (_ fastpathT) DecMapUint8Float32V(v map[uint8]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -23433,7 +23433,7 @@ func (_ fastpathT) DecMapUint8Float64V(v map[uint8]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -24324,7 +24324,7 @@ func (_ fastpathT) DecMapUint16Float32V(v map[uint16]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -24383,7 +24383,7 @@ func (_ fastpathT) DecMapUint16Float64V(v map[uint16]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -25274,7 +25274,7 @@ func (_ fastpathT) DecMapUint32Float32V(v map[uint32]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -25333,7 +25333,7 @@ func (_ fastpathT) DecMapUint32Float64V(v map[uint32]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -26224,7 +26224,7 @@ func (_ fastpathT) DecMapUint64Float32V(v map[uint64]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -26283,7 +26283,7 @@ func (_ fastpathT) DecMapUint64Float64V(v map[uint64]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -27174,7 +27174,7 @@ func (_ fastpathT) DecMapUintptrFloat32V(v map[uintptr]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -27233,7 +27233,7 @@ func (_ fastpathT) DecMapUintptrFloat64V(v map[uintptr]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -28124,7 +28124,7 @@ func (_ fastpathT) DecMapIntFloat32V(v map[int]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -28183,7 +28183,7 @@ func (_ fastpathT) DecMapIntFloat64V(v map[int]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -29074,7 +29074,7 @@ func (_ fastpathT) DecMapInt8Float32V(v map[int8]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -29133,7 +29133,7 @@ func (_ fastpathT) DecMapInt8Float64V(v map[int8]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -30024,7 +30024,7 @@ func (_ fastpathT) DecMapInt16Float32V(v map[int16]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -30083,7 +30083,7 @@ func (_ fastpathT) DecMapInt16Float64V(v map[int16]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -30974,7 +30974,7 @@ func (_ fastpathT) DecMapInt32Float32V(v map[int32]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -31033,7 +31033,7 @@ func (_ fastpathT) DecMapInt32Float64V(v map[int32]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -31924,7 +31924,7 @@ func (_ fastpathT) DecMapInt64Float32V(v map[int64]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -31983,7 +31983,7 @@ func (_ fastpathT) DecMapInt64Float64V(v map[int64]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}
@@ -32874,7 +32874,7 @@ func (_ fastpathT) DecMapBoolFloat32V(v map[bool]float32, canChange bool,
 			}
 			continue
 		}
-		mv = float32(dd.DecodeFloat(true))
+		mv = float32(chkFloat32(dd.DecodeFloat64()))
 		if v != nil {
 			v[mk] = mv
 		}
@@ -32933,7 +32933,7 @@ func (_ fastpathT) DecMapBoolFloat64V(v map[bool]float64, canChange bool,
 			}
 			continue
 		}
-		mv = dd.DecodeFloat(false)
+		mv = dd.DecodeFloat64()
 		if v != nil {
 			v[mk] = mv
 		}

+ 41 - 4
codec/gen-helper.generated.go

@@ -25,21 +25,53 @@ const GenVersion = 8
 // When static codecs are created for types, they will use this value
 // to perform encoding or decoding of primitives or known slice or map types.
 
+type genHelperEncDriver struct {
+	encDriver
+}
+
+func (x genHelperEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {}
+func (x genHelperEncDriver) EncStructFieldKey(keyType valueType, s string) {
+	encStructFieldKey(x.encDriver, keyType, s, false)
+}
+
+type genHelperDecDriver struct {
+	decDriver
+}
+
+func (x genHelperDecDriver) DecodeBuiltin(rt uintptr, v interface{}) {}
+func (x genHelperDecDriver) DecStructFieldKey(keyType valueType, buf *[scratchByteArrayLen]byte) []byte {
+	return decStructFieldKey(x.decDriver, keyType, buf)
+}
+func (x genHelperDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+	f = x.DecodeFloat64()
+	if chkOverflow32 && chkOvf.Float32(f) {
+		panicv.errorf("float32 overflow: %v", f)
+	}
+	return
+}
+func (x genHelperDecDriver) DecodeFloat32As64() (f float64) {
+	f = x.DecodeFloat64()
+	if chkOvf.Float32(f) {
+		panicv.errorf("float32 overflow: %v", f)
+	}
+	return
+}
+
 // GenHelperEncoder is exported so that it can be used externally by codecgen.
 //
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
-func GenHelperEncoder(e *Encoder) (ge genHelperEncoder, ee encDriver) {
+func GenHelperEncoder(e *Encoder) (ge genHelperEncoder, ee genHelperEncDriver) {
 	ge = genHelperEncoder{e: e}
-	ee = e.e
+	ee = genHelperEncDriver{e.e}
 	return
 }
 
 // GenHelperDecoder is exported so that it can be used externally by codecgen.
 //
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
-func GenHelperDecoder(d *Decoder) (gd genHelperDecoder, dd decDriver) {
+func GenHelperDecoder(d *Decoder) (gd genHelperDecoder, dd genHelperDecDriver) {
 	gd = genHelperDecoder{d: d}
-	dd = d.d
+	dd = genHelperDecDriver{d.d}
 	return
 }
 
@@ -165,6 +197,11 @@ func (f genHelperDecoder) DecScratchBuffer() []byte {
 	return f.d.b[:]
 }
 
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecScratchArrayBuffer() *[scratchByteArrayLen]byte {
+	return &f.d.b
+}
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecFallback(iv interface{}, chkPtr bool) {
 	// println(">>>>>>>>> DecFallback")

+ 40 - 4
codec/gen-helper.go.tmpl

@@ -25,21 +25,53 @@ const GenVersion = {{ .Version }}
 // When static codecs are created for types, they will use this value
 // to perform encoding or decoding of primitives or known slice or map types.
 
+type genHelperEncDriver struct {
+	encDriver
+}
+
+func (x genHelperEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {}
+func (x genHelperEncDriver) EncStructFieldKey(keyType valueType, s string) {
+	encStructFieldKey(x.encDriver, keyType, s, false)
+}
+
+type genHelperDecDriver struct {
+	decDriver
+}
+
+func (x genHelperDecDriver) DecodeBuiltin(rt uintptr, v interface{}) {}
+func (x genHelperDecDriver) DecStructFieldKey(keyType valueType, buf *[scratchByteArrayLen]byte) []byte {
+	return decStructFieldKey(x.decDriver, keyType, buf)
+}
+func (x genHelperDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+	f = x.DecodeFloat64()
+	if chkOverflow32 && chkOvf.Float32(f) {
+		panicv.errorf("float32 overflow: %v", f)
+	}
+	return
+}
+func (x genHelperDecDriver) DecodeFloat32As64() (f float64) {
+	f = x.DecodeFloat64()
+	if chkOvf.Float32(f) {
+		panicv.errorf("float32 overflow: %v", f)
+	}
+	return
+}
+
 // GenHelperEncoder is exported so that it can be used externally by codecgen.
 //
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
-func GenHelperEncoder(e *Encoder) (ge genHelperEncoder, ee encDriver) {
+func GenHelperEncoder(e *Encoder) (ge genHelperEncoder, ee genHelperEncDriver) {
 	ge = genHelperEncoder{e:e}
-	ee = e.e
+	ee = genHelperEncDriver{e.e}
 	return 
 }
 
 // GenHelperDecoder is exported so that it can be used externally by codecgen.
 //
 // Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
-func GenHelperDecoder(d *Decoder) (gd genHelperDecoder, dd decDriver) {
+func GenHelperDecoder(d *Decoder) (gd genHelperDecoder, dd genHelperDecDriver) {
 	gd = genHelperDecoder{d:d}
-	dd = d.d
+	dd = genHelperDecDriver{d.d}
 	return
 }
 
@@ -149,6 +181,10 @@ func (f genHelperDecoder) DecScratchBuffer() []byte {
 	return f.d.b[:]
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecScratchArrayBuffer() *[scratchByteArrayLen]byte {
+	return &f.d.b
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecFallback(iv interface{}, chkPtr bool) {
 	// println(">>>>>>>>> DecFallback")
 	rv := reflect.ValueOf(iv)

+ 24 - 15
codec/gen.go

@@ -252,9 +252,15 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, noExtensions bool,
 	x.linef("codecSelferCcUTF8%s = %v", x.xs, int64(cUTF8))
 	x.linef("codecSelferCcRAW%s = %v", x.xs, int64(cRAW))
 	x.linef("// ----- value types used ----")
-	x.linef("codecSelferValueTypeArray%s = %v", x.xs, int64(valueTypeArray))
-	x.linef("codecSelferValueTypeMap%s = %v", x.xs, int64(valueTypeMap))
-	// These are no longer needed, as there are functions created for them
+	for _, vt := range [...]valueType{
+		valueTypeArray, valueTypeMap, valueTypeString,
+		valueTypeInt, valueTypeUint, valueTypeFloat} {
+		x.linef("codecSelferValueType%s%s = %v", vt.String(), x.xs, int64(vt))
+	}
+
+	// x.linef("codecSelferValueTypeArray%s = %v", x.xs, int64(valueTypeArray))
+	// x.linef("codecSelferValueTypeMap%s = %v", x.xs, int64(valueTypeMap))
+	// // These are no longer needed, as there are functions created for them
 	// x.linef("// ----- containerStateValues ----")
 	// x.linef("codecSelferKcontainerMapKey%s = %v", x.xs, int64(containerMapKey))
 	// x.linef("codecSelferKcontainerMapValue%s = %v", x.xs, int64(containerMapValue))
@@ -987,7 +993,8 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 			x.linef("if %s[%v] {", numfieldsvar, j)
 		}
 		x.line("r.WriteMapElemKey()") // x.linef("z.EncSendContainerState(codecSelferKcontainerMapKey%s)", x.xs)
-		x.line("r.EncodeString(codecSelferCcUTF8" + x.xs + ", `" + si.encName + "`)")
+		// x.line("r.EncodeString(codecSelferCcUTF8" + x.xs + ", `" + si.encName + "`)")
+		x.linef("r.EncStructFieldKey(codecSelferValueType%s%s, `%s`)", ti.keyType.String(), x.xs, si.encName)
 		x.line("r.WriteMapElemValue()") // x.linef("z.EncSendContainerState(codecSelferKcontainerMapValue%s)", x.xs)
 		if labelUsed {
 			x.line("if " + isNilVarName + " { r.EncodeNil() } else { ")
@@ -1238,10 +1245,10 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 		x.line("*((*uintptr)(" + varname + ")) = uintptr(r.DecodeUint(codecSelferBitsize" + x.xs + "))")
 
 	case reflect.Float32:
-		x.line("*((*float32)(" + varname + ")) = float32(r.DecodeFloat(true))")
+		x.line("*((*float32)(" + varname + ")) = float32(r.DecodeFloat32As64())")
 		//x.line("z.DecFloat32((*float32)(" + varname + "))")
 	case reflect.Float64:
-		x.line("*((*float64)(" + varname + ")) = float64(r.DecodeFloat(false))")
+		x.line("*((*float64)(" + varname + ")) = r.DecodeFloat64()")
 		// x.line("z.DecFloat64((*float64)(" + varname + "))")
 
 	case reflect.Bool:
@@ -1331,9 +1338,9 @@ func (x *genRunner) decTryAssignPrimitive(varname string, t reflect.Type) (tryAs
 		x.linef("%s = r.DecodeUint(codecSelferBitsize%s)", varname, x.xs)
 
 	case reflect.Float32:
-		x.linef("%s = r.DecodeFloat(true)", varname)
+		x.linef("%s = r.DecodeFloat32As64()", varname)
 	case reflect.Float64:
-		x.linef("%s = r.DecodeFloat(false)", varname)
+		x.linef("%s = r.DecodeFloat64()", varname)
 
 	case reflect.Bool:
 		x.linef("%s = r.DecodeBool()", varname)
@@ -1496,6 +1503,7 @@ func (x *genRunner) decStructMapSwitch(kName string, varname string, rtid uintpt
 
 func (x *genRunner) decStructMap(varname, lenvarname string, rtid uintptr, t reflect.Type, style genStructMapStyle) {
 	tpfx := genTempVarPfx
+	ti := x.ti.get(rtid, t)
 	i := x.varsfx()
 	kName := tpfx + "s" + i
 
@@ -1503,9 +1511,8 @@ func (x *genRunner) decStructMap(varname, lenvarname string, rtid uintptr, t ref
 	// x.line("var " + kName + "Slc = " + kName + "Arr[:] // default slice to decode into")
 	// use the scratch buffer to avoid allocation (most field names are < 32).
 
-	x.line("var " + kName + "Slc = z.DecScratchBuffer() // default slice to decode into")
-
-	x.line("_ = " + kName + "Slc")
+	// x.line("var " + kName + "Slc = z.DecScratchBuffer() // default slice to decode into")
+	// x.line("_ = " + kName + "Slc")
 	switch style {
 	case genStructMapStyleLenPrefix:
 		x.linef("for %sj%s := 0; %sj%s < %s; %sj%s++ {", tpfx, i, tpfx, i, lenvarname, tpfx, i)
@@ -1518,9 +1525,11 @@ func (x *genRunner) decStructMap(varname, lenvarname string, rtid uintptr, t ref
 		x.line("} else { if r.CheckBreak() { break }; }")
 	}
 	x.line("r.ReadMapElemKey()") // f("z.DecSendContainerState(codecSelferKcontainerMapKey%s)", x.xs)
-	x.line(kName + "Slc = r.DecodeStringAsBytes()")
+	// x.line(kName + "Slc = r.DecodeStringAsBytes()")
 	// let string be scoped to this loop alone, so it doesn't escape.
-	x.line(kName + " := string(" + kName + "Slc)")
+	// x.line(kName + " := string(" + kName + "Slc)")
+	x.linef("%s := z.StringView(r.DecStructFieldKey(codecSelferValueType%s%s, z.DecScratchArrayBuffer()))",
+		kName, ti.keyType.String(), x.xs)
 	x.line("r.ReadMapElemValue()") // f("z.DecSendContainerState(codecSelferKcontainerMapValue%s)", x.xs)
 	x.decStructMapSwitch(kName, varname, rtid, t)
 
@@ -1891,9 +1900,9 @@ func genInternalDecCommandAsString(s string) string {
 	case "string":
 		return "dd.DecodeString()"
 	case "float32":
-		return "float32(dd.DecodeFloat(true))"
+		return "float32(chkFloat32(dd.DecodeFloat64()))"
 	case "float64":
-		return "dd.DecodeFloat(false)"
+		return "dd.DecodeFloat64()"
 	case "bool":
 		return "dd.DecodeBool()"
 	default:

+ 151 - 18
codec/helper.go

@@ -102,6 +102,7 @@ import (
 	"encoding/binary"
 	"errors"
 	"fmt"
+	"io"
 	"math"
 	"os"
 	"reflect"
@@ -138,8 +139,8 @@ var (
 )
 
 var refBitset bitset32
-
 var pool pooler
+var panicv panicHdl
 
 func init() {
 	pool.init()
@@ -442,6 +443,7 @@ func (x *BasicHandle) getTypeInfo(rtid uintptr, rt reflect.Type) (pti *typeInfo)
 // and not modified while in use. Such a pre-configured Handle
 // is safe for concurrent access.
 type Handle interface {
+	Name() string
 	getBasicHandle() *BasicHandle
 	newEncDriver(w *Encoder) encDriver
 	newDecDriver(r *Decoder) decDriver
@@ -733,7 +735,7 @@ type structFieldInfo struct {
 	is        [maxLevelsEmbedding]uint16 // (recursive/embedded) field index in struct
 	nis       uint8                      // num levels of embedding. if 1, then it's not embedded.
 	omitEmpty bool
-	toArray   bool // if field is _struct, is the toArray set?
+	// toArray   bool // if field is _struct, is the toArray set?
 }
 
 func (si *structFieldInfo) setToZeroValue(v reflect.Value) {
@@ -764,6 +766,35 @@ func (si *structFieldInfo) field(v reflect.Value, update bool) (rv2 reflect.Valu
 // 	return v
 // }
 
+func parseStructInfo(stag string) (toArray, omitEmpty bool, keytype valueType) {
+	keytype = valueTypeString // default
+	if stag == "" {
+		return
+	}
+	for i, s := range strings.Split(stag, ",") {
+		if i == 0 {
+		} else {
+			switch s {
+			case "omitempty":
+				omitEmpty = true
+			case "toarray":
+				toArray = true
+			case "int":
+				keytype = valueTypeInt
+			case "uint":
+				keytype = valueTypeUint
+			case "float":
+				keytype = valueTypeFloat
+				// case "bool":
+				// 	keytype = valueTypeBool
+			case "string":
+				keytype = valueTypeString
+			}
+		}
+	}
+	return
+}
+
 func parseStructFieldInfo(fname string, stag string) (si *structFieldInfo) {
 	// if fname == "" {
 	// 	panic(errNoFieldNameToStructFieldInfo)
@@ -779,10 +810,11 @@ func parseStructFieldInfo(fname string, stag string) (si *structFieldInfo) {
 				si.encName = s
 			}
 		} else {
-			if s == "omitempty" {
+			switch s {
+			case "omitempty":
 				si.omitEmpty = true
-			} else if s == "toarray" {
-				si.toArray = true
+				// case "toarray":
+				// 	si.toArray = true
 			}
 		}
 	}
@@ -934,7 +966,8 @@ type typeInfo struct {
 	cs  bool // T is a Selfer
 	csp bool // *T is a Selfer
 
-	toArray bool // whether this (struct) type should be encoded as an array
+	toArray bool      // whether this (struct) type should be encoded as an array
+	keyType valueType // what type of key: default is string
 }
 
 // define length beyond which we do a binary search instead of a linear search.
@@ -1063,9 +1096,9 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
 	if rk == reflect.Struct {
 		var omitEmpty bool
 		if f, ok := rt.FieldByName(structInfoFieldName); ok {
-			siInfo := parseStructFieldInfo(structInfoFieldName, x.structTag(f.Tag))
-			ti.toArray = siInfo.toArray
-			omitEmpty = siInfo.omitEmpty
+			ti.toArray, omitEmpty, ti.keyType = parseStructInfo(x.structTag(f.Tag))
+		} else {
+			ti.keyType = valueTypeString
 		}
 		pp, pi := pool.tiLoad()
 		pv := pi.(*typeInfoLoadArray)
@@ -1293,24 +1326,44 @@ func xprintf(format string, a ...interface{}) {
 	}
 }
 
-func panicToErr(err *error) {
+func panicToErr(h errstrDecorator, err *error) {
 	if recoverPanicToErr {
 		if x := recover(); x != nil {
 			// if false && xDebug {
 			// 	fmt.Printf("panic'ing with: %v\n", x)
 			// 	debug.PrintStack()
 			// }
-			panicValToErr(x, err)
+			panicValToErr(h, x, err)
 		}
 	}
 }
 
-func panicToErrs2(err1, err2 *error) {
+func panicToErrs2(h errstrDecorator, err1, err2 *error) {
 	if recoverPanicToErr {
 		if x := recover(); x != nil {
-			panicValToErr(x, err1)
-			panicValToErr(x, err2)
+			panicValToErr(h, x, err1)
+			panicValToErr(h, x, err2)
+		}
+	}
+}
+
+func panicValToErr(h errstrDecorator, v interface{}, err *error) {
+	switch xerr := v.(type) {
+	case nil:
+	case error:
+		switch xerr {
+		case nil:
+		case io.EOF, io.ErrUnexpectedEOF, errEncoderNotInitialized: // treat as special (bubble up)
+			*err = xerr
+		default:
+			h.wrapErrstr(xerr.Error(), err)
 		}
+	case string:
+		if xerr != "" {
+			h.wrapErrstr(xerr, err)
+		}
+	default:
+		h.wrapErrstr(v, err)
 	}
 }
 
@@ -1620,9 +1673,29 @@ func (c *codecFner) get(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (
 
 // ----
 
-// these functions must be inlinable, and not call anybody
+func chkFloat32(f float64) (f32 float32) {
+	// f32 = float32(f)
+	if chkOvf.Float32(f) {
+		panicv.errorf("float32 overflow: %v", f)
+	}
+	return float32(f)
+}
+
+// these "checkOverflow" functions must be inlinable, and not call anybody.
+// Overflow means that the value cannot be represented without wrapping/overflow.
+// Overflow=false does not mean that the value can be represented without losing precision
+// (especially for floating point).
+
 type checkOverflow struct{}
 
+// func (checkOverflow) Float16(f float64) (overflow bool) {
+// 	panicv.errorf("unimplemented")
+// 	if f < 0 {
+// 		f = -f
+// 	}
+// 	return math.MaxFloat32 < f && f <= math.MaxFloat64
+// }
+
 func (checkOverflow) Float32(f float64) (overflow bool) {
 	if f < 0 {
 		f = -f
@@ -1657,15 +1730,16 @@ func (checkOverflow) SignedInt(v uint64) (i int64, overflow bool) {
 	if pos {
 		if ui2 > math.MaxInt64 {
 			overflow = true
-			return
+		} else {
+			i = int64(v)
 		}
 	} else {
 		if ui2 > math.MaxInt64-1 {
 			overflow = true
-			return
+		} else {
+			i = int64(v)
 		}
 	}
-	i = int64(v)
 	return
 }
 
@@ -1964,3 +2038,62 @@ func (p *pooler) decNaked() (sp *sync.Pool, v interface{}) {
 func (p *pooler) tiLoad() (sp *sync.Pool, v interface{}) {
 	return &p.tiload, p.tiload.Get()
 }
+
+type panicHdl struct{}
+
+func (panicHdl) errorv(err error) {
+	if err != nil {
+		panic(err)
+	}
+}
+
+func (panicHdl) errorstr(message string) {
+	if message != "" {
+		panic(message)
+	}
+}
+
+func (panicHdl) errorf(format string, params ...interface{}) {
+	if format != "" {
+		if len(params) == 0 {
+			panic(format)
+		} else {
+			panic(fmt.Sprintf(format, params...))
+		}
+	}
+}
+
+type errstrDecorator interface {
+	wrapErrstr(interface{}, *error)
+}
+
+type errstrDecoratorDef struct{}
+
+func (errstrDecoratorDef) wrapErrstr(v interface{}, e *error) { *e = fmt.Errorf("%v", v) }
+
+type must struct{}
+
+func (must) String(s string, err error) string {
+	if err != nil {
+		panicv.errorv(err)
+	}
+	return s
+}
+func (must) Int(s int64, err error) int64 {
+	if err != nil {
+		panicv.errorv(err)
+	}
+	return s
+}
+func (must) Uint(s uint64, err error) uint64 {
+	if err != nil {
+		panicv.errorv(err)
+	}
+	return s
+}
+func (must) Float(s float64, err error) float64 {
+	if err != nil {
+		panicv.errorv(err)
+	}
+	return s
+}

+ 0 - 15
codec/helper_internal.go

@@ -7,25 +7,10 @@ package codec
 // so porting to different environment is easy (just update functions).
 
 import (
-	"errors"
-	"fmt"
 	"reflect"
 	"time"
 )
 
-func panicValToErr(panicVal interface{}, err *error) {
-	switch xerr := panicVal.(type) {
-	case nil:
-	case error:
-		*err = xerr
-	case string:
-		*err = errors.New(xerr)
-	default:
-		*err = fmt.Errorf("%v", panicVal)
-	}
-	return
-}
-
 func hIsEmptyValue(v reflect.Value, deref, checkStruct bool) bool {
 	switch v.Kind() {
 	case reflect.Invalid:

+ 6 - 2
codec/helper_not_unsafe.go

@@ -117,11 +117,15 @@ func (d *Decoder) kTime(f *codecFnInfo, rv reflect.Value) {
 }
 
 func (d *Decoder) kFloat32(f *codecFnInfo, rv reflect.Value) {
-	rv.SetFloat(d.d.DecodeFloat(true))
+	fv := d.d.DecodeFloat64()
+	if chkOvf.Float32(fv) {
+		d.errorf("float32 overflow: %v", fv)
+	}
+	rv.SetFloat(fv)
 }
 
 func (d *Decoder) kFloat64(f *codecFnInfo, rv reflect.Value) {
-	rv.SetFloat(d.d.DecodeFloat(false))
+	rv.SetFloat(d.d.DecodeFloat64())
 }
 
 func (d *Decoder) kInt(f *codecFnInfo, rv reflect.Value) {

+ 6 - 2
codec/helper_unsafe.go

@@ -174,13 +174,17 @@ func (d *Decoder) kTime(f *codecFnInfo, rv reflect.Value) {
 }
 
 func (d *Decoder) kFloat32(f *codecFnInfo, rv reflect.Value) {
+	fv := d.d.DecodeFloat64()
+	if chkOvf.Float32(fv) {
+		d.errorf("float32 overflow: %v", fv)
+	}
 	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
-	*(*float32)(urv.ptr) = float32(d.d.DecodeFloat(true))
+	*(*float32)(urv.ptr) = float32(fv)
 }
 
 func (d *Decoder) kFloat64(f *codecFnInfo, rv reflect.Value) {
 	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
-	*(*float64)(urv.ptr) = d.d.DecodeFloat(false)
+	*(*float64)(urv.ptr) = d.d.DecodeFloat64()
 }
 
 func (d *Decoder) kInt(f *codecFnInfo, rv reflect.Value) {

+ 49 - 19
codec/json.go

@@ -311,20 +311,17 @@ func (e *jsonEncDriver) EncodeBool(b bool) {
 }
 
 func (e *jsonEncDriver) EncodeFloat32(f float32) {
-	e.encodeFloat(float64(f), 32)
+	// e.encodeFloat(float64(f), 32)
+	e.EncodeFloat64(float64(f))
 }
 
 func (e *jsonEncDriver) EncodeFloat64(f float64) {
-	e.encodeFloat(f, 64)
-}
-
-func (e *jsonEncDriver) encodeFloat(f float64, bits int) {
 	var blen int
 	// instead of using 'g', specify whether to use 'e' or 'f'
 	var abs = math.Abs(f)
 	var fmt byte
 	var prec int = -1
-	if abs != 0 && (bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21)) {
+	if abs != 0 && (abs < 1e-6 || abs >= 1e21) {
 		fmt = 'e'
 	} else {
 		fmt = 'f'
@@ -341,15 +338,51 @@ func (e *jsonEncDriver) encodeFloat(f float64, bits int) {
 	}
 
 	if e.h.MapKeyAsString && e.c == containerMapKey {
-		blen = 2 + len(strconv.AppendFloat(e.b[1:1], f, fmt, prec, bits))
+		blen = 2 + len(strconv.AppendFloat(e.b[1:1], f, fmt, prec, 64))
 		e.b[0] = '"'
 		e.b[blen-1] = '"'
 	} else {
-		blen = len(strconv.AppendFloat(e.b[:0], f, fmt, prec, bits))
+		blen = len(strconv.AppendFloat(e.b[:0], f, fmt, prec, 64))
 	}
 	e.w.writeb(e.b[:blen])
 }
 
+// func (e *jsonEncDriver) EncodeFloat64(f float64) {
+// 	e.encodeFloat(f, 64)
+// }
+
+// func (e *jsonEncDriver) encodeFloat(f float64, bits int) {
+// 	var blen int
+// 	// instead of using 'g', specify whether to use 'e' or 'f'
+// 	var abs = math.Abs(f)
+// 	var fmt byte
+// 	var prec int = -1
+// 	if abs != 0 && (bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21)) {
+// 		fmt = 'e'
+// 	} else {
+// 		fmt = 'f'
+// 		// set prec to 1 iff mod is 0.
+// 		//     better than using jsonIsFloatBytesB2 to check if a . or E in the float bytes.
+// 		// this ensures that every float has an e or .0 in it.
+// 		if abs <= 1 {
+// 			if abs == 0 || abs == 1 {
+// 				prec = 1
+// 			}
+// 		} else if _, mod := math.Modf(abs); mod == 0 {
+// 			prec = 1
+// 		}
+// 	}
+
+// 	if e.h.MapKeyAsString && e.c == containerMapKey {
+// 		blen = 2 + len(strconv.AppendFloat(e.b[1:1], f, fmt, prec, bits))
+// 		e.b[0] = '"'
+// 		e.b[blen-1] = '"'
+// 	} else {
+// 		blen = len(strconv.AppendFloat(e.b[:0], f, fmt, prec, bits))
+// 	}
+// 	e.w.writeb(e.b[:blen])
+// }
+
 func (e *jsonEncDriver) EncodeInt(v int64) {
 	x := e.h.IntegerAsString
 	if x == 'A' || x == 'L' && (v > 1<<53 || v < -(1<<53)) || (e.h.MapKeyAsString && e.c == containerMapKey) {
@@ -708,7 +741,7 @@ func (d *jsonDecDriver) DecodeTime() (t time.Time) {
 	}
 	t, err := time.Parse(time.RFC3339, stringView(d.bs))
 	if err != nil {
-		d.d.error(err)
+		d.d.errorv(err)
 	}
 	return
 }
@@ -762,22 +795,16 @@ func (d *jsonDecDriver) DecodeInt(bitsize uint8) (i int64) {
 	bs := d.decNumBytes()
 	i, err := strconv.ParseInt(stringView(bs), 10, int(bitsize))
 	if err != nil {
-		d.d.errorf("json: decode int from %s: %v", bs, err)
-		return
+		d.d.errorv(err)
 	}
 	return
 }
 
-func (d *jsonDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+func (d *jsonDecDriver) DecodeFloat64() (f float64) {
 	bs := d.decNumBytes()
-	bitsize := 64
-	if chkOverflow32 {
-		bitsize = 32
-	}
-	f, err := strconv.ParseFloat(stringView(bs), bitsize)
+	f, err := strconv.ParseFloat(stringView(bs), 64)
 	if err != nil {
-		d.d.errorf("json: decode float from %s: %v", bs, err)
-		return
+		d.d.errorv(err)
 	}
 	return
 }
@@ -1149,6 +1176,9 @@ type JsonHandle struct {
 	MapKeyAsString bool
 }
 
+// Name returns the name of the handle: json
+func (h *JsonHandle) Name() string { return "json" }
+
 func (h *JsonHandle) hasElemSeparators() bool { return true }
 
 // SetInterfaceExt sets an extension

文件差异内容过多而无法显示
+ 116 - 112
codec/mammoth2_codecgen_generated_test.go


+ 4 - 5
codec/msgpack.go

@@ -559,7 +559,7 @@ func (d *msgpackDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
 }
 
 // float can either be decoded from msgpack type: float, double or intX
-func (d *msgpackDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+func (d *msgpackDecDriver) DecodeFloat64() (f float64) {
 	if !d.bdRead {
 		d.readNextBd()
 	}
@@ -570,10 +570,6 @@ func (d *msgpackDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
 	} else {
 		f = float64(d.DecodeInt(0))
 	}
-	if chkOverflow32 && chkOvf.Float32(f) {
-		d.d.errorf("msgpack: float32 overflow: %v", f)
-		return
-	}
 	d.bdRead = false
 	return
 }
@@ -890,6 +886,9 @@ type MsgpackHandle struct {
 	noElemSeparators
 }
 
+// Name returns the name of the handle: msgpack
+func (h *MsgpackHandle) Name() string { return "msgpack" }
+
 // SetBytesExt sets an extension
 func (h *MsgpackHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
 	return h.SetExt(rt, tag, &setExtWrapper{b: ext})

+ 1 - 1
codec/rpc.go

@@ -103,7 +103,7 @@ func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2 bool) (err error) {
 }
 
 func (c *rpcCodec) swallow(err *error) {
-	defer panicToErr(err)
+	defer panicToErr(c.dec, err)
 	c.dec.swallow()
 }
 

+ 8 - 9
codec/simple.go

@@ -206,7 +206,7 @@ func (e *simpleEncDriver) EncodeTime(t time.Time) {
 	}
 	v, err := t.MarshalBinary()
 	if err != nil {
-		e.e.error(err)
+		e.e.errorv(err)
 		return
 	}
 	// time.Time marshalbinary takes about 14 bytes.
@@ -346,7 +346,7 @@ func (d *simpleDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
 	return
 }
 
-func (d *simpleDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+func (d *simpleDecDriver) DecodeFloat64() (f float64) {
 	if !d.bdRead {
 		d.readNextBd()
 	}
@@ -362,10 +362,6 @@ func (d *simpleDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
 			return
 		}
 	}
-	if chkOverflow32 && chkOvf.Float32(f) {
-		d.d.errorf("msgpack: float32 overflow: %v", f)
-		return
-	}
 	d.bdRead = false
 	return
 }
@@ -501,7 +497,7 @@ func (d *simpleDecDriver) DecodeTime() (t time.Time) {
 	clen := int(d.r.readn1())
 	b := d.r.readx(clen)
 	if err := (&t).UnmarshalBinary(b); err != nil {
-		d.d.error(err)
+		d.d.errorv(err)
 	}
 	return
 }
@@ -576,10 +572,10 @@ func (d *simpleDecDriver) DecodeNaked() {
 		n.i = d.DecodeInt(64)
 	case simpleVdFloat32:
 		n.v = valueTypeFloat
-		n.f = d.DecodeFloat(true)
+		n.f = d.DecodeFloat64()
 	case simpleVdFloat64:
 		n.v = valueTypeFloat
-		n.f = d.DecodeFloat(false)
+		n.f = d.DecodeFloat64()
 	case simpleVdTime:
 		n.v = valueTypeTime
 		n.t = d.DecodeTime()
@@ -639,6 +635,9 @@ type SimpleHandle struct {
 	EncZeroValuesAsNil bool
 }
 
+// Name returns the name of the handle: simple
+func (h *SimpleHandle) Name() string { return "simple" }
+
 // SetBytesExt sets an extension
 func (h *SimpleHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
 	return h.SetExt(rt, tag, &setExtWrapper{b: ext})

部分文件因为文件数量过多而无法显示