Kaynağa Gözat

codec: Canonical natural ordering, and MaxValueReset and InterfaceReset options

Add MaxValueReset and InterfaceReset options, to allow decoding map values or interfaces into new values.

    Many decoders (e.g. json, gob, etc) will write a new value into a map or interface,
    instead of retrieving what is in the map/interface and decoding into that.

    To support that feature-parity, we introduce some flags to enable this.

    - Added MaxValueReset flag:
      control whether we get the value mapped to a key and decode into that,
      or just decode into a "blank" value and set into the map.
    - Added InterfaceReset flag:
      Controls whether we decode into the value contained in the interface,
      or just decode into a "blank" value and set into the interface.

    Support both flags in reflection, fast-path and codecgen.

    One advantage of this, especially for large maps, is that it gives better
    performance parity when contrasting against the standard library.

    Fixed json to use a single slice for all number and string decodings.
    Previously, we always used an initial byte off an array. However, if the
    string was longer than array length, an allocation was done, and thereafter
    the slice was thrown away. We need keep that slice and use throughout
    within the decoding.

    Json code was also streamlined to support this, by removing jsonNum.bytes
    and just using the bytes field in the Decoder.

Support canonical natural ordering mode, and canonical mode in fast-path

    canonical mode previously would always encode the keys out of band, and then
    sort them, and then write them out.

    This has been changed to a stricter canonical mode, where numbers, bool and strings
    are sorted in natural mode (numeric order or lexicographic order).

    If the type does not support natural ordering, then we encode out-of-band first
    like was done before.

       - If there is a natural sort order (ie for number, bool, string or []byte keys),
         then the map keys are first sorted in natural order and then written
         with corresponding map values to the strema.
       - If there is no natural sort order, then the map keys will first be
         encoded into []byte, and then sorted,
         before writing the sorted keys and the corresponding map values to the stream.

    We also now support canonical mode in fast-path. This follows as we support
    natural order, and all fast-path have maps with builtin keys which can be sorted.

    To enable this, we defined re-usable slices of builtin types, and defined their
    sorting.

    Also, we made GenInternalGoFile private. Instead, at fastpath generation time,
    we create a wrapper file, and remove it when done.

Misc:

    - run codegenerators commands (for msgp, ffjson, codecgen) in parallel

Fixes #107
Ugorji Nwoke 10 yıl önce
ebeveyn
işleme
5d8e71f204

+ 0 - 12
codec/codecgen/gen.go

@@ -41,10 +41,6 @@ package {{ $.PackageName }}
 
 import (
 	{{ if not .CodecPkgFiles }}{{ .CodecPkgName }} "{{ .CodecImportPath }}"{{ end }}
-{{/*
-	{{ if .Types }}"{{ .ImportPath }}"{{ end }}
-	"io"
-*/}}
 	"os"
 	"reflect"
 	"bytes"
@@ -52,14 +48,6 @@ import (
 	"go/format"
 )
 
-{{/* This is not used anymore. Remove it.
-func write(w io.Writer, s string) {
-	if _, err := io.WriteString(w, s); err != nil {
-		panic(err)
-	}
-}
-*/}}
-
 func CodecGenTempWrite{{ .RandString }}() {
 	fout, err := os.Create("{{ .OutFile }}")
 	if err != nil {

+ 84 - 21
codec/decode.go

@@ -95,6 +95,14 @@ type DecodeOptions struct {
 	// If nil, we use []interface{}
 	SliceType reflect.Type
 
+	// MaxInitLen defines the initial length that we "make" a collection (slice, chan or map) with.
+	// If 0 or negative, we default to a sensible value based on the size of an element in the collection.
+	//
+	// For example, when decoding, a stream may say that it has MAX_UINT elements.
+	// We should not auto-matically provision a slice of that length, to prevent Out-Of-Memory crash.
+	// Instead, we provision up to MaxInitLen, fill that up, and start appending after that.
+	MaxInitLen int
+
 	// If ErrorIfNoField, return an error when decoding a map
 	// from a codec stream into a struct, and no matching struct field is found.
 	ErrorIfNoField bool
@@ -107,13 +115,32 @@ type DecodeOptions struct {
 	// If SignedInteger, use the int64 during schema-less decoding of unsigned values (not uint64).
 	SignedInteger bool
 
-	// MaxInitLen defines the initial length that we "make" a collection (slice, chan or map) with.
-	// If 0 or negative, we default to a sensible value based on the size of an element in the collection.
+	// MapValueReset controls how we decode into a map value.
 	//
-	// For example, when decoding, a stream may say that it has MAX_UINT elements.
-	// We should not auto-matically provision a slice of that length, to prevent Out-Of-Memory crash.
-	// Instead, we provision up to MaxInitLen, fill that up, and start appending after that.
-	MaxInitLen int
+	// By default, we MAY retrieve the mapping for a key, and then decode into that.
+	// However, especially with big maps, that retrieval may be expensive and unnecessary
+	// if the stream already contains all that is necessary to recreate the value.
+	//
+	// If true, we will never retrieve the previous mapping,
+	// but rather decode into a new value and set that in the map.
+	//
+	// If false, we will retrieve the previous mapping if necessary e.g.
+	// the previous mapping is a pointer, or is a struct or array with pre-set state,
+	// or is an interface.
+	MapValueReset bool
+
+	// InterfaceReset controls how we decode into an interface.
+	//
+	// By default, when we see a field that is an interface{...},
+	// or a map with interface{...} value, we will attempt decoding into the
+	// "contained" value.
+	//
+	// However, this prevents us from reading a string into an interface{}
+	// that formerly contained a number.
+	//
+	// If true, we will decode into a new "blank" value, and set that in the interface.
+	// If true, we will decode into whatever is contained in the interface.
+	InterfaceReset bool
 }
 
 // ------------------------------------
@@ -582,25 +609,34 @@ func (f *decFnInfo) kInterface(rv reflect.Value) {
 	// to decode into what was there before.
 	// We do not replace with a generic value (as got from decodeNaked).
 
+	var rvn reflect.Value
 	if rv.IsNil() {
-		rvn := f.kInterfaceNaked()
+		rvn = f.kInterfaceNaked()
 		if rvn.IsValid() {
 			rv.Set(rvn)
 		}
+	} else if f.d.h.InterfaceReset {
+		rvn = f.kInterfaceNaked()
+		if rvn.IsValid() {
+			rv.Set(rvn)
+		} else {
+			// reset to zero value based on current type in there.
+			rv.Set(reflect.Zero(rv.Elem().Type()))
+		}
 	} else {
-		rve := rv.Elem()
+		rvn = rv.Elem()
 		// Note: interface{} is settable, but underlying type may not be.
 		// Consequently, we have to set the reflect.Value directly.
 		// if underlying type is settable (e.g. ptr or interface),
 		// we just decode into it.
 		// Else we create a settable value, decode into it, and set on the interface.
-		if rve.CanSet() {
-			f.d.decodeValue(rve, nil)
+		if rvn.CanSet() {
+			f.d.decodeValue(rvn, nil)
 		} else {
-			rve2 := reflect.New(rve.Type()).Elem()
-			rve2.Set(rve)
-			f.d.decodeValue(rve2, nil)
-			rv.Set(rve2)
+			rvn2 := reflect.New(rvn.Type()).Elem()
+			rvn2.Set(rvn)
+			f.d.decodeValue(rvn2, nil)
+			rv.Set(rvn2)
 		}
 	}
 }
@@ -887,10 +923,30 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
 	for xtyp = vtype; xtyp.Kind() == reflect.Ptr; xtyp = xtyp.Elem() {
 	}
 	valFn = d.getDecFn(xtyp, true, true)
+	var mapGet bool
+	if !f.d.h.MapValueReset {
+		// if pointer, mapGet = true
+		// if interface, mapGet = true if !DecodeNakedAlways (else false)
+		// if builtin, mapGet = false
+		// else mapGet = true
+		vtypeKind := vtype.Kind()
+		if vtypeKind == reflect.Ptr {
+			mapGet = true
+		} else if vtypeKind == reflect.Interface {
+			if !f.d.h.InterfaceReset {
+				mapGet = true
+			}
+		} else if !isImmutableKind(vtypeKind) {
+			mapGet = true
+		}
+	}
+
+	var rvk, rvv reflect.Value
+
 	// for j := 0; j < containerLen; j++ {
 	if containerLen > 0 {
 		for j := 0; j < containerLen; j++ {
-			rvk := reflect.New(ktype).Elem()
+			rvk = reflect.New(ktype).Elem()
 			d.decodeValue(rvk, keyFn)
 
 			// special case if a byte array.
@@ -900,9 +956,12 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
 					rvk = reflect.ValueOf(string(rvk.Bytes()))
 				}
 			}
-			rvv := rv.MapIndex(rvk)
-			// TODO: is !IsValid check required?
-			if !rvv.IsValid() {
+			if mapGet {
+				rvv = rv.MapIndex(rvk)
+				if !rvv.IsValid() {
+					rvv = reflect.New(vtype).Elem()
+				}
+			} else {
 				rvv = reflect.New(vtype).Elem()
 			}
 			d.decodeValue(rvv, valFn)
@@ -910,7 +969,7 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
 		}
 	} else {
 		for j := 0; !dd.CheckBreak(); j++ {
-			rvk := reflect.New(ktype).Elem()
+			rvk = reflect.New(ktype).Elem()
 			d.decodeValue(rvk, keyFn)
 
 			// special case if a byte array.
@@ -920,8 +979,12 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
 					rvk = reflect.ValueOf(string(rvk.Bytes()))
 				}
 			}
-			rvv := rv.MapIndex(rvk)
-			if !rvv.IsValid() {
+			if mapGet {
+				rvv = rv.MapIndex(rvk)
+				if !rvv.IsValid() {
+					rvv = reflect.New(vtype).Elem()
+				}
+			} else {
 				rvv = reflect.New(vtype).Elem()
 			}
 			d.decodeValue(rvv, valFn)

+ 163 - 76
codec/encode.go

@@ -4,7 +4,6 @@
 package codec
 
 import (
-	"bytes"
 	"encoding"
 	"fmt"
 	"io"
@@ -80,17 +79,6 @@ type encNoSeparator struct{}
 
 func (_ encNoSeparator) EncodeEnd() {}
 
-type encStructFieldBytesV struct {
-	b []byte
-	v reflect.Value
-}
-
-type encStructFieldBytesVslice []encStructFieldBytesV
-
-func (p encStructFieldBytesVslice) Len() int           { return len(p) }
-func (p encStructFieldBytesVslice) Less(i, j int) bool { return bytes.Compare(p[i].b, p[j].b) == -1 }
-func (p encStructFieldBytesVslice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
-
 type ioEncWriterWriter interface {
 	WriteByte(c byte) error
 	WriteString(s string) (n int, err error)
@@ -109,8 +97,16 @@ type EncodeOptions struct {
 	// sequence of bytes.
 	//
 	// This only affects maps, as the iteration order for maps is random.
-	// In this case, the map keys will first be encoded into []byte, and then sorted,
-	// before writing the sorted keys and the corresponding map values to the stream.
+	//
+	// The implementation MAY use the natural sort order for the map keys if possible:
+	//
+	//     - If there is a natural sort order (ie for number, bool, string or []byte keys),
+	//       then the map keys are first sorted in natural order and then written
+	//       with corresponding map values to the strema.
+	//     - If there is no natural sort order, then the map keys will first be
+	//       encoded into []byte, and then sorted,
+	//       before writing the sorted keys and the corresponding map values to the stream.
+	//
 	Canonical bool
 
 	// AsSymbols defines what should be encoded as symbols.
@@ -493,27 +489,27 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
 		tisfi = fti.sfi
 	}
 	newlen = 0
-	var kv encStructFieldKV
+	var kv stringRv
 	for _, si := range tisfi {
-		kv.v = si.field(rv, false)
+		kv.r = si.field(rv, false)
 		// if si.i != -1 {
 		// 	rvals[newlen] = rv.Field(int(si.i))
 		// } else {
 		// 	rvals[newlen] = rv.FieldByIndex(si.is)
 		// }
 		if toMap {
-			if si.omitEmpty && isEmptyValue(kv.v) {
+			if si.omitEmpty && isEmptyValue(kv.r) {
 				continue
 			}
-			kv.k = si.encName
+			kv.v = si.encName
 		} else {
 			// use the zero value.
 			// if a reference or struct, set to nil (so you do not output too much)
-			if si.omitEmpty && isEmptyValue(kv.v) {
-				switch kv.v.Kind() {
+			if si.omitEmpty && isEmptyValue(kv.r) {
+				switch kv.r.Kind() {
 				case reflect.Struct, reflect.Interface, reflect.Ptr, reflect.Array,
 					reflect.Map, reflect.Slice:
-					kv.v = reflect.Value{} //encode as nil
+					kv.r = reflect.Value{} //encode as nil
 				}
 			}
 		}
@@ -532,17 +528,17 @@ func (f *encFnInfo) kStruct(rv reflect.Value) {
 		for j := 0; j < newlen; j++ {
 			kv = fkvs[j]
 			if asSymbols {
-				ee.EncodeSymbol(kv.k)
+				ee.EncodeSymbol(kv.v)
 			} else {
-				ee.EncodeString(c_UTF8, kv.k)
+				ee.EncodeString(c_UTF8, kv.v)
 			}
-			e.encodeValue(kv.v, nil)
+			e.encodeValue(kv.r, nil)
 		}
 	} else {
 		ee.EncodeArrayStart(newlen)
 		for j := 0; j < newlen; j++ {
 			kv = fkvs[j]
-			e.encodeValue(kv.v, nil)
+			e.encodeValue(kv.r, nil)
 		}
 	}
 	ee.EncodeEnd()
@@ -621,24 +617,9 @@ func (f *encFnInfo) kMap(rv reflect.Value) {
 	}
 	mks := rv.MapKeys()
 	// for j, lmks := 0, len(mks); j < lmks; j++ {
+
 	if e.h.Canonical {
-		// first encode each key to a []byte first, then sort them, then record
-		// println(">>>>>>>> CANONICAL <<<<<<<<")
-		var mksv []byte = make([]byte, 0, len(mks)*16) // temporary byte slice for the encoding
-		e2 := NewEncoderBytes(&mksv, e.hh)
-		mksbv := make([]encStructFieldBytesV, len(mks))
-		for i, k := range mks {
-			l := len(mksv)
-			e2.MustEncode(k)
-			mksbv[i].v = k
-			mksbv[i].b = mksv[l:]
-			// fmt.Printf(">>>>> %s\n", mksv[l:])
-		}
-		sort.Sort(encStructFieldBytesVslice(mksbv))
-		for j := range mksbv {
-			e.asis(mksbv[j].b)
-			e.encodeValue(rv.MapIndex(mksbv[j].v), valFn)
-		}
+		e.kMapCanonical(rtkeyid, rtkey, rv, mks, valFn, asSymbols)
 	} else {
 		for j := range mks {
 			if keyTypeIsString {
@@ -653,9 +634,128 @@ func (f *encFnInfo) kMap(rv reflect.Value) {
 			e.encodeValue(rv.MapIndex(mks[j]), valFn)
 		}
 	}
+
 	ee.EncodeEnd()
 }
 
+func (e *Encoder) kMapCanonical(rtkeyid uintptr, rtkey reflect.Type, rv reflect.Value, mks []reflect.Value, valFn *encFn, asSymbols bool) {
+	ee := e.e
+	// we previously did out-of-band if an extension was registered.
+	// This is not necessary, as the natural kind is sufficient for ordering.
+
+	if rtkeyid == uint8SliceTypId {
+		mksv := make([]bytesRv, len(mks))
+		for i, k := range mks {
+			v := &mksv[i]
+			v.r = k
+			v.v = k.Bytes()
+		}
+		sort.Sort(bytesRvSlice(mksv))
+		for i := range mksv {
+			ee.EncodeStringBytes(c_RAW, mksv[i].v)
+			e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
+		}
+	} else {
+		switch rtkey.Kind() {
+		case reflect.Bool:
+			mksv := make([]boolRv, len(mks))
+			for i, k := range mks {
+				v := &mksv[i]
+				v.r = k
+				v.v = k.Bool()
+			}
+			sort.Sort(boolRvSlice(mksv))
+			for i := range mksv {
+				ee.EncodeBool(mksv[i].v)
+				e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
+			}
+		case reflect.String:
+			mksv := make([]stringRv, len(mks))
+			for i, k := range mks {
+				v := &mksv[i]
+				v.r = k
+				v.v = k.String()
+			}
+			sort.Sort(stringRvSlice(mksv))
+			for i := range mksv {
+				if asSymbols {
+					ee.EncodeSymbol(mksv[i].v)
+				} else {
+					ee.EncodeString(c_UTF8, mksv[i].v)
+				}
+				e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
+			}
+		case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr:
+			mksv := make([]uintRv, len(mks))
+			for i, k := range mks {
+				v := &mksv[i]
+				v.r = k
+				v.v = k.Uint()
+			}
+			sort.Sort(uintRvSlice(mksv))
+			for i := range mksv {
+				ee.EncodeUint(mksv[i].v)
+				e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
+			}
+		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+			mksv := make([]intRv, len(mks))
+			for i, k := range mks {
+				v := &mksv[i]
+				v.r = k
+				v.v = k.Int()
+			}
+			sort.Sort(intRvSlice(mksv))
+			for i := range mksv {
+				ee.EncodeInt(mksv[i].v)
+				e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
+			}
+		case reflect.Float32:
+			mksv := make([]floatRv, len(mks))
+			for i, k := range mks {
+				v := &mksv[i]
+				v.r = k
+				v.v = k.Float()
+			}
+			sort.Sort(floatRvSlice(mksv))
+			for i := range mksv {
+				ee.EncodeFloat32(float32(mksv[i].v))
+				e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
+			}
+		case reflect.Float64:
+			mksv := make([]floatRv, len(mks))
+			for i, k := range mks {
+				v := &mksv[i]
+				v.r = k
+				v.v = k.Float()
+			}
+			sort.Sort(floatRvSlice(mksv))
+			for i := range mksv {
+				ee.EncodeFloat64(mksv[i].v)
+				e.encodeValue(rv.MapIndex(mksv[i].r), valFn)
+			}
+		default:
+			// out-of-band
+			// first encode each key to a []byte first, then sort them, then record
+			var mksv []byte = make([]byte, 0, len(mks)*16) // temporary byte slice for the encoding
+			e2 := NewEncoderBytes(&mksv, e.hh)
+			mksbv := make([]bytesRv, len(mks))
+			for i, k := range mks {
+				v := &mksbv[i]
+				l := len(mksv)
+				e2.MustEncode(k)
+				v.r = k
+				v.v = mksv[l:]
+				// fmt.Printf(">>>>> %s\n", mksv[l:])
+			}
+			sort.Sort(bytesRvSlice(mksbv))
+			for j := range mksbv {
+				e.asis(mksbv[j].v)
+				e.encodeValue(rv.MapIndex(mksbv[j].r), valFn)
+			}
+		}
+	}
+}
+
 // --------------------------------------------------
 
 // encFn encapsulates the captured variables and the encode function.
@@ -903,16 +1003,9 @@ func (e *Encoder) encode(iv interface{}) {
 		e.e.EncodeStringBytes(c_RAW, *v)
 
 	default:
-		// canonical mode is not supported for fastpath of maps (but is fine for slices)
 		const checkCodecSelfer1 = true // in case T is passed, where *T is a Selfer, still checkCodecSelfer
-		if e.h.Canonical {
-			if !fastpathEncodeTypeSwitchSlice(iv, e) {
-				e.encodeI(iv, false, checkCodecSelfer1)
-			}
-		} else {
-			if !fastpathEncodeTypeSwitch(iv, e) {
-				e.encodeI(iv, false, checkCodecSelfer1)
-			}
+		if !fastpathEncodeTypeSwitch(iv, e) {
+			e.encodeI(iv, false, checkCodecSelfer1)
 		}
 	}
 }
@@ -1019,8 +1112,7 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo
 		fn.f = (*encFnInfo).textMarshal
 	} else {
 		rk := rt.Kind()
-		// if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {
-		if fastpathEnabled && checkFastpath && (rk == reflect.Slice || (rk == reflect.Map && !e.h.Canonical)) {
+		if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {
 			if rt.PkgPath() == "" {
 				if idx := fastpathAV.index(rtid); idx != -1 {
 					fn.f = fastpathAV[idx].encfn
@@ -1114,11 +1206,6 @@ func (e *Encoder) errorf(format string, params ...interface{}) {
 
 // ----------------------------------------
 
-type encStructFieldKV struct {
-	k string
-	v reflect.Value
-}
-
 const encStructPoolLen = 5
 
 // encStructPool is an array of sync.Pool.
@@ -1133,33 +1220,33 @@ const encStructPoolLen = 5
 var encStructPool [encStructPoolLen]sync.Pool
 
 func init() {
-	encStructPool[0].New = func() interface{} { return new([8]encStructFieldKV) }
-	encStructPool[1].New = func() interface{} { return new([16]encStructFieldKV) }
-	encStructPool[2].New = func() interface{} { return new([32]encStructFieldKV) }
-	encStructPool[3].New = func() interface{} { return new([64]encStructFieldKV) }
-	encStructPool[4].New = func() interface{} { return new([128]encStructFieldKV) }
+	encStructPool[0].New = func() interface{} { return new([8]stringRv) }
+	encStructPool[1].New = func() interface{} { return new([16]stringRv) }
+	encStructPool[2].New = func() interface{} { return new([32]stringRv) }
+	encStructPool[3].New = func() interface{} { return new([64]stringRv) }
+	encStructPool[4].New = func() interface{} { return new([128]stringRv) }
 }
 
-func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []encStructFieldKV) {
+func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []stringRv) {
 	// if encStructPoolLen != 5 { // constant chec, so removed at build time.
 	// 	panic(errors.New("encStructPoolLen must be equal to 4")) // defensive, in case it is changed
 	// }
 	// idxpool := newlen / 8
 
 	// if pool == nil {
-	// 	fkvs = make([]encStructFieldKV, newlen)
+	// 	fkvs = make([]stringRv, newlen)
 	// } else {
 	// 	poolv = pool.Get()
 	// 	switch vv := poolv.(type) {
-	// 	case *[8]encStructFieldKV:
+	// 	case *[8]stringRv:
 	// 		fkvs = vv[:newlen]
-	// 	case *[16]encStructFieldKV:
+	// 	case *[16]stringRv:
 	// 		fkvs = vv[:newlen]
-	// 	case *[32]encStructFieldKV:
+	// 	case *[32]stringRv:
 	// 		fkvs = vv[:newlen]
-	// 	case *[64]encStructFieldKV:
+	// 	case *[64]stringRv:
 	// 		fkvs = vv[:newlen]
-	// 	case *[128]encStructFieldKV:
+	// 	case *[128]stringRv:
 	// 		fkvs = vv[:newlen]
 	// 	}
 	// }
@@ -1167,25 +1254,25 @@ func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []encStructFie
 	if newlen <= 8 {
 		p = &encStructPool[0]
 		v = p.Get()
-		s = v.(*[8]encStructFieldKV)[:newlen]
+		s = v.(*[8]stringRv)[:newlen]
 	} else if newlen <= 16 {
 		p = &encStructPool[1]
 		v = p.Get()
-		s = v.(*[16]encStructFieldKV)[:newlen]
+		s = v.(*[16]stringRv)[:newlen]
 	} else if newlen <= 32 {
 		p = &encStructPool[2]
 		v = p.Get()
-		s = v.(*[32]encStructFieldKV)[:newlen]
+		s = v.(*[32]stringRv)[:newlen]
 	} else if newlen <= 64 {
 		p = &encStructPool[3]
 		v = p.Get()
-		s = v.(*[64]encStructFieldKV)[:newlen]
+		s = v.(*[64]stringRv)[:newlen]
 	} else if newlen <= 128 {
 		p = &encStructPool[4]
 		v = p.Get()
-		s = v.(*[128]encStructFieldKV)[:newlen]
+		s = v.(*[128]stringRv)[:newlen]
 	} else {
-		s = make([]encStructFieldKV, newlen)
+		s = make([]stringRv, newlen)
 	}
 	return
 }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 576 - 30
codec/fast-path.generated.go


+ 55 - 18
codec/fast-path.go.tmpl

@@ -182,14 +182,50 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Ele
 		return
 	}
 	ee.EncodeMapStart(len(v))
-	{{if eq .MapKey "string"}}asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0{{end}}
-	for k2, v2 := range v {
-		{{if eq .MapKey "string"}}if asSymbols {
-			ee.EncodeSymbol(k2)
-		} else {
-			ee.EncodeString(c_UTF8, k2)
-		}{{else}}{{ encmd .MapKey "k2"}}{{end}}
-		{{ encmd .Elem "v2"}}
+	{{if eq .MapKey "string"}}asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+	{{end}}if e.h.Canonical {
+		{{if eq .MapKey "interface{}"}}{{/* out of band 
+		*/}}var mksv []byte = make([]byte, 0, len(v)*16) // temporary byte slice for the encoding
+		e2 := NewEncoderBytes(&mksv, e.hh)
+		v2 := make([]bytesI, len(v))
+		var i, l int
+		var vp *bytesI {{/* put loop variables outside. seems currently needed for better perf */}}
+		for k2, _ := range v {
+			l = len(mksv)
+			e2.MustEncode(k2)
+			vp = &v2[i]
+			vp.v = mksv[l:]
+			vp.i = k2 
+			i++
+		}
+		sort.Sort(bytesISlice(v2))
+		for j := range v2 {
+			e.asis(v2[j].v)
+			e.encode(v[v2[j].i])
+		} {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
+		var i int 
+		for k, _ := range v {
+			v2[i] = {{ $x }}(k)
+			i++
+		}
+		sort.Sort({{ sorttype .MapKey false}}(v2))
+		for _, k2 := range v2 {
+			{{if eq .MapKey "string"}}if asSymbols {
+				ee.EncodeSymbol(k2)
+			} else {
+				ee.EncodeString(c_UTF8, k2)
+			}{{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
+			{{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
+		} {{end}}
+	} else {
+		for k2, v2 := range v {
+			{{if eq .MapKey "string"}}if asSymbols {
+				ee.EncodeSymbol(k2)
+			} else {
+				ee.EncodeString(c_UTF8, k2)
+			}{{else}}{{ encmd .MapKey "k2"}}{{end}}
+			{{ encmd .Elem "v2"}}
+		}
 	}
 	ee.EncodeEnd()
 }
@@ -382,30 +418,31 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
 		v = make(map[{{ .MapKey }}]{{ .Elem }}, xlen)
 		changed = true
 	}
+	{{ if eq .Elem "interface{}" }}mapGet := !d.h.MapValueReset && !d.h.InterfaceReset{{end}}
+	var mk {{ .MapKey }}
+	var mv {{ .Elem }}
 	if containerLen > 0 {
 		for j := 0; j < containerLen; j++ {
-			{{ if eq .MapKey "interface{}" }}var mk interface{}
+			{{ if eq .MapKey "interface{}" }}mk = nil 
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 				mk = string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
-			}{{ else }}mk := {{ decmd .MapKey }}{{ end }}
-			mv := v[mk]
-			{{ if eq .Elem "interface{}" }}d.decode(&mv)
-			{{ else }}mv = {{ decmd .Elem }}{{ end }}
+			}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
+			{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
+			d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
 			if v != nil {
 				v[mk] = mv
 			}
 		}
 	} else if containerLen < 0 {
 		for j := 0; !dd.CheckBreak(); j++ {
-			{{ if eq .MapKey "interface{}" }}var mk interface{}
+			{{ if eq .MapKey "interface{}" }}mk = nil 
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 				mk = string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
-			}{{ else }}mk := {{ decmd .MapKey }}{{ end }}
-			mv := v[mk]
-			{{ if eq .Elem "interface{}" }}d.decode(&mv)
-			{{ else }}mv = {{ decmd .Elem }}{{ end }}
+			}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
+			{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
+			d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
 			if v != nil {
 				v[mk] = mv
 			}

+ 20 - 13
codec/gen-dec-map.go.tmpl

@@ -1,20 +1,28 @@
 {{var "v"}} := *{{ .Varname }}
 {{var "l"}} := r.ReadMapStart()
+{{var "bh"}} := z.DecBasicHandle()
 if {{var "v"}} == nil {
-	{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
+	{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, {{var "bh"}}.MaxInitLen, {{ .Size }})
 	{{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
 	*{{ .Varname }} = {{var "v"}}
 }
+var {{var "mk"}} {{ .KTyp }}
+var {{var "mv"}} {{ .Typ }}
+var {{var "mg"}} bool
+if {{var "bh"}}.MapValueReset {
+	{{if decElemKindPtr}}{{var "mg"}} = true
+	{{else if decElemKindIntf}}if !{{var "bh"}}.InterfaceReset { {{var "mg"}} = true }
+	{{else if not decElemKindImmutable}}{{var "mg"}} = true
+	{{end}} }
 if {{var "l"}} > 0  {
 for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
-	var {{var "mk"}} {{ .KTyp }} 
 	{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
-{{ if eq .KTyp "interface{}" }}// special case if a byte array.
-	if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
+{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
 		{{var "mk"}} = string({{var "bv"}})
-	}
-{{ end }}
-	{{var "mv"}} := {{var "v"}}[{{var "mk"}}]
+	}{{ end }}
+	if {{var "mg"}} {
+		{{var "mv"}} = {{var "v"}}[{{var "mk"}}]
+	} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
 	{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
 	if {{var "v"}} != nil {
 		{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
@@ -22,14 +30,13 @@ for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
 }
 } else if {{var "l"}} < 0  {
 for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
-	var {{var "mk"}} {{ .KTyp }} 
 	{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
-{{ if eq .KTyp "interface{}" }}// special case if a byte array.
-	if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
+{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
 		{{var "mk"}} = string({{var "bv"}})
-	}
-{{ end }}
-	{{var "mv"}} := {{var "v"}}[{{var "mk"}}]
+	}{{ end }}
+	if {{var "mg"}} {
+		{{var "mv"}} = {{var "v"}}[{{var "mk"}}]
+	} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
 	{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
 	if {{var "v"}} != nil {
 		{{var "v"}}[{{var "mk"}}] = {{var "mv"}}

+ 20 - 13
codec/gen.generated.go

@@ -8,21 +8,29 @@ package codec
 const genDecMapTmpl = `
 {{var "v"}} := *{{ .Varname }}
 {{var "l"}} := r.ReadMapStart()
+{{var "bh"}} := z.DecBasicHandle()
 if {{var "v"}} == nil {
-	{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
+	{{var "rl"}}, _ := z.DecInferLen({{var "l"}}, {{var "bh"}}.MaxInitLen, {{ .Size }})
 	{{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
 	*{{ .Varname }} = {{var "v"}}
 }
+var {{var "mk"}} {{ .KTyp }}
+var {{var "mv"}} {{ .Typ }}
+var {{var "mg"}} bool
+if {{var "bh"}}.MapValueReset {
+	{{if decElemKindPtr}}{{var "mg"}} = true
+	{{else if decElemKindIntf}}if !{{var "bh"}}.InterfaceReset { {{var "mg"}} = true }
+	{{else if not decElemKindImmutable}}{{var "mg"}} = true
+	{{end}} }
 if {{var "l"}} > 0  {
 for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
-	var {{var "mk"}} {{ .KTyp }} 
 	{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
-{{ if eq .KTyp "interface{}" }}// special case if a byte array.
-	if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
+{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
 		{{var "mk"}} = string({{var "bv"}})
-	}
-{{ end }}
-	{{var "mv"}} := {{var "v"}}[{{var "mk"}}]
+	}{{ end }}
+	if {{var "mg"}} {
+		{{var "mv"}} = {{var "v"}}[{{var "mk"}}]
+	} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
 	{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
 	if {{var "v"}} != nil {
 		{{var "v"}}[{{var "mk"}}] = {{var "mv"}}
@@ -30,14 +38,13 @@ for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
 }
 } else if {{var "l"}} < 0  {
 for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
-	var {{var "mk"}} {{ .KTyp }} 
 	{{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
-{{ if eq .KTyp "interface{}" }}// special case if a byte array.
-	if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
+{{ if eq .KTyp "interface{}" }}{{/* // special case if a byte array. */}}if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
 		{{var "mk"}} = string({{var "bv"}})
-	}
-{{ end }}
-	{{var "mv"}} := {{var "v"}}[{{var "mk"}}]
+	}{{ end }}
+	if {{var "mg"}} {
+		{{var "mv"}} = {{var "v"}}[{{var "mk"}}]
+	} {{if not decElemKindImmutable}}else { {{var "mv"}} = {{decElemZero}} }{{end}}
 	{{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
 	if {{var "v"}} != nil {
 		{{var "v"}}[{{var "mk"}}] = {{var "mv"}}

+ 44 - 5
codec/gen.go

@@ -169,7 +169,7 @@ func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, ti *TypeIn
 		ts:     []reflect.Type{},
 		bp:     genImportPath(typ[0]),
 		xs:     uid,
-		ti:     ti, //TODO: make it configurable
+		ti:     ti,
 	}
 	if x.ti == nil {
 		x.ti = defTypeInfos
@@ -928,6 +928,7 @@ func (x *genRunner) encListFallback(varname string, t reflect.Type) {
 }
 
 func (x *genRunner) encMapFallback(varname string, t reflect.Type) {
+	// TODO: expand this to handle canonical.
 	i := x.varsfx()
 	x.line("r.EncodeMapStart(len(" + varname + "))")
 	x.linef("for %sk%s, %sv%s := range %s {", genTempVarPfx, i, genTempVarPfx, i, varname)
@@ -1301,8 +1302,24 @@ func (x *genRunner) decMapFallback(varname string, rtid uintptr, t reflect.Type)
 	}
 	telem := t.Elem()
 	tkey := t.Key()
-	ts := tstruc{genTempVarPfx, x.varsfx(), varname, x.genTypeName(tkey), x.genTypeName(telem), int(telem.Size() + tkey.Size())}
+	ts := tstruc{
+		genTempVarPfx, x.varsfx(), varname, x.genTypeName(tkey),
+		x.genTypeName(telem), int(telem.Size() + tkey.Size()),
+	}
+
 	funcs := make(template.FuncMap)
+	funcs["decElemZero"] = func() string {
+		return x.genZeroValueR(telem)
+	}
+	funcs["decElemKindImmutable"] = func() bool {
+		return genIsImmutable(telem)
+	}
+	funcs["decElemKindPtr"] = func() bool {
+		return telem.Kind() == reflect.Ptr
+	}
+	funcs["decElemKindIntf"] = func() bool {
+		return telem.Kind() == reflect.Interface
+	}
 	funcs["decLineVarK"] = func(varname string) string {
 		x.decVar(varname, tkey, false)
 		return ""
@@ -1726,6 +1743,8 @@ func genInternalDecCommandAsString(s string) string {
 		return "uint32(dd.DecodeUint(32))"
 	case "uint64":
 		return "dd.DecodeUint(64)"
+	case "uintptr":
+		return "uintptr(dd.DecodeUint(uintBitsize))"
 	case "int":
 		return "int(dd.DecodeInt(intBitsize))"
 	case "int8":
@@ -1746,9 +1765,24 @@ func genInternalDecCommandAsString(s string) string {
 	case "bool":
 		return "dd.DecodeBool()"
 	default:
-		panic(errors.New("unknown type for decode: " + s))
+		panic(errors.New("gen internal: unknown type for decode: " + s))
 	}
+}
 
+func genInternalSortType(s string, elem bool) string {
+	for _, v := range [...]string{"int", "uint", "float", "bool", "string"} {
+		if strings.HasPrefix(s, v) {
+			if elem {
+				if v == "int" || v == "uint" || v == "float" {
+					return v + "64"
+				} else {
+					return v
+				}
+			}
+			return v + "Slice"
+		}
+	}
+	panic("sorttype: unexpected type: " + s)
 }
 
 // var genInternalMu sync.Mutex
@@ -1767,6 +1801,7 @@ func genInternalInit() {
 		"uint16",
 		"uint32",
 		"uint64",
+		"uintptr",
 		"int",
 		"int8",
 		"int16",
@@ -1784,6 +1819,7 @@ func genInternalInit() {
 		"uint16",
 		"uint32",
 		"uint64",
+		"uintptr",
 		"int",
 		"int8",
 		"int16",
@@ -1803,6 +1839,7 @@ func genInternalInit() {
 		"uint16":      2,
 		"uint32":      4,
 		"uint64":      8,
+		"uintptr":     1 * wordSizeBytes,
 		"int":         1 * wordSizeBytes,
 		"int8":        1,
 		"int16":       2,
@@ -1837,16 +1874,18 @@ func genInternalInit() {
 	funcs["encmd"] = genInternalEncCommandAsString
 	funcs["decmd"] = genInternalDecCommandAsString
 	funcs["zerocmd"] = genInternalZeroValue
+	funcs["hasprefix"] = strings.HasPrefix
+	funcs["sorttype"] = genInternalSortType
 
 	genInternalV = gt
 	genInternalTmplFuncs = funcs
 }
 
-// GenInternalGoFile is used to generate source files from templates.
+// genInternalGoFile is used to generate source files from templates.
 // It is run by the program author alone.
 // Unfortunately, it has to be exported so that it can be called from a command line tool.
 // *** DO NOT USE ***
-func GenInternalGoFile(r io.Reader, w io.Writer, safe bool) (err error) {
+func genInternalGoFile(r io.Reader, w io.Writer, safe bool) (err error) {
 	genInternalOnce.Do(genInternalInit)
 
 	gt := genInternalV

+ 112 - 0
codec/helper.go

@@ -101,6 +101,7 @@ package codec
 // check for these error conditions.
 
 import (
+	"bytes"
 	"encoding"
 	"encoding/binary"
 	"errors"
@@ -975,3 +976,114 @@ func (_ checkOverflow) SignedInt(v uint64) (i int64, overflow bool) {
 	i = int64(v)
 	return
 }
+
+// ------------------ SORT -----------------
+
+func isNaN(f float64) bool { return f != f }
+
+// -----------------------
+
+type intSlice []int64
+type uintSlice []uint64
+type floatSlice []float64
+type boolSlice []bool
+type stringSlice []string
+type bytesSlice [][]byte
+
+func (p intSlice) Len() int           { return len(p) }
+func (p intSlice) Less(i, j int) bool { return p[i] < p[j] }
+func (p intSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func (p uintSlice) Len() int           { return len(p) }
+func (p uintSlice) Less(i, j int) bool { return p[i] < p[j] }
+func (p uintSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func (p floatSlice) Len() int { return len(p) }
+func (p floatSlice) Less(i, j int) bool {
+	return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j])
+}
+func (p floatSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+func (p stringSlice) Len() int           { return len(p) }
+func (p stringSlice) Less(i, j int) bool { return p[i] < p[j] }
+func (p stringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func (p bytesSlice) Len() int           { return len(p) }
+func (p bytesSlice) Less(i, j int) bool { return bytes.Compare(p[i], p[j]) == -1 }
+func (p bytesSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func (p boolSlice) Len() int           { return len(p) }
+func (p boolSlice) Less(i, j int) bool { return !p[i] && p[j] }
+func (p boolSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+// ---------------------
+
+type intRv struct {
+	v int64
+	r reflect.Value
+}
+type intRvSlice []intRv
+type uintRv struct {
+	v uint64
+	r reflect.Value
+}
+type uintRvSlice []uintRv
+type floatRv struct {
+	v float64
+	r reflect.Value
+}
+type floatRvSlice []floatRv
+type boolRv struct {
+	v bool
+	r reflect.Value
+}
+type boolRvSlice []boolRv
+type stringRv struct {
+	v string
+	r reflect.Value
+}
+type stringRvSlice []stringRv
+type bytesRv struct {
+	v []byte
+	r reflect.Value
+}
+type bytesRvSlice []bytesRv
+
+func (p intRvSlice) Len() int           { return len(p) }
+func (p intRvSlice) Less(i, j int) bool { return p[i].v < p[j].v }
+func (p intRvSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func (p uintRvSlice) Len() int           { return len(p) }
+func (p uintRvSlice) Less(i, j int) bool { return p[i].v < p[j].v }
+func (p uintRvSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func (p floatRvSlice) Len() int { return len(p) }
+func (p floatRvSlice) Less(i, j int) bool {
+	return p[i].v < p[j].v || isNaN(p[i].v) && !isNaN(p[j].v)
+}
+func (p floatRvSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+func (p stringRvSlice) Len() int           { return len(p) }
+func (p stringRvSlice) Less(i, j int) bool { return p[i].v < p[j].v }
+func (p stringRvSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func (p bytesRvSlice) Len() int           { return len(p) }
+func (p bytesRvSlice) Less(i, j int) bool { return bytes.Compare(p[i].v, p[j].v) == -1 }
+func (p bytesRvSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+func (p boolRvSlice) Len() int           { return len(p) }
+func (p boolRvSlice) Less(i, j int) bool { return !p[i].v && p[j].v }
+func (p boolRvSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+// -----------------
+
+type bytesI struct {
+	v []byte
+	i interface{}
+}
+
+type bytesISlice []bytesI
+
+func (p bytesISlice) Len() int           { return len(p) }
+func (p bytesISlice) Less(i, j int) bool { return bytes.Compare(p[i].v, p[j].v) == -1 }
+func (p bytesISlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

+ 52 - 40
codec/json.go

@@ -411,7 +411,7 @@ func (e *jsonEncDriver) quoteStr(s string) {
 //--------------------------------
 
 type jsonNum struct {
-	bytes            []byte // may have [+-.eE0-9]
+	// bytes            []byte // may have [+-.eE0-9]
 	mantissa         uint64 // where mantissa ends, and maybe dot begins.
 	exponent         int16  // exponent value.
 	manOverflow      bool
@@ -421,7 +421,6 @@ type jsonNum struct {
 }
 
 func (x *jsonNum) reset() {
-	x.bytes = x.bytes[:0]
 	x.manOverflow = false
 	x.neg = false
 	x.dot = false
@@ -454,29 +453,26 @@ func (x *jsonNum) uintExp() (n uint64, overflow bool) {
 	// return
 }
 
-func (x *jsonNum) floatVal() (f float64) {
+// these constants are only used withn floatVal.
+// They are brought out, so that floatVal can be inlined.
+const (
+	jsonUint64MantissaBits = 52
+	jsonMaxExponent        = int16(len(jsonFloat64Pow10)) - 1
+)
+
+func (x *jsonNum) floatVal() (f float64, parseUsingStrConv bool) {
 	// We do not want to lose precision.
 	// Consequently, we will delegate to strconv.ParseFloat if any of the following happen:
 	//    - There are more digits than in math.MaxUint64: 18446744073709551615 (20 digits)
 	//      We expect up to 99.... (19 digits)
 	//    - The mantissa cannot fit into a 52 bits of uint64
 	//    - The exponent is beyond our scope ie beyong 22.
-	const uint64MantissaBits = 52
-	const maxExponent = int16(len(jsonFloat64Pow10)) - 1
+	parseUsingStrConv = x.manOverflow ||
+		x.exponent > jsonMaxExponent ||
+		(x.exponent < 0 && -(x.exponent) > jsonMaxExponent) ||
+		x.mantissa>>jsonUint64MantissaBits != 0
 
-	parseUsingStrConv := x.manOverflow ||
-		x.exponent > maxExponent ||
-		(x.exponent < 0 && -(x.exponent) > maxExponent) ||
-		x.mantissa>>uint64MantissaBits != 0
 	if parseUsingStrConv {
-		var err error
-		if f, err = strconv.ParseFloat(stringView(x.bytes), 64); err != nil {
-			panic(fmt.Errorf("parse float: %s, %v", x.bytes, err))
-			return
-		}
-		if x.neg {
-			f = -f
-		}
 		return
 	}
 
@@ -500,8 +496,9 @@ type jsonDecDriver struct {
 	r    decReader // *bytesDecReader decReader
 	ct   valueType // container type. one of unset, array or map.
 	bstr [8]byte   // scratch used for string \UXXX parsing
-	b    [64]byte  // scratch
-	b2   [64]byte
+	b    [64]byte  // scratch, used for parsing strings or numbers
+	b2   [64]byte  // scratch, used only for decodeBytes (after base64)
+	bs   []byte    // scratch. Initialized from b. Used for parsing strings or numbers.
 
 	wsSkipped bool // whitespace skipped
 
@@ -662,8 +659,6 @@ func (d *jsonDecDriver) IsContainerType(vt valueType) bool {
 }
 
 func (d *jsonDecDriver) decNum(storeBytes bool) {
-	// storeBytes = true // TODO: remove.
-
 	// If it is has a . or an e|E, decode as a float; else decode as an int.
 	b := d.skipWhitespace(false)
 	if !(b == '+' || b == '-' || b == '.' || (b >= '0' && b <= '9')) {
@@ -674,10 +669,9 @@ func (d *jsonDecDriver) decNum(storeBytes bool) {
 	const cutoff = (1<<64-1)/uint64(10) + 1 // cutoff64(base)
 	const jsonNumUintMaxVal = 1<<uint64(64) - 1
 
-	// var n jsonNum // create stack-copy jsonNum, and set to pointer at end.
-	// n.bytes = d.n.bytes[:0]
 	n := &d.n
 	n.reset()
+	d.bs = d.bs[:0]
 
 	// The format of a number is as below:
 	// parsing:     sign? digit* dot? digit* e?  sign? digit*
@@ -773,7 +767,7 @@ LOOP:
 			break LOOP
 		}
 		if storeBytes {
-			n.bytes = append(n.bytes, b)
+			d.bs = append(d.bs, b)
 		}
 		b, eof = d.r.readn1eof()
 	}
@@ -834,13 +828,28 @@ func (d *jsonDecDriver) DecodeInt(bitsize uint8) (i int64) {
 		i = -i
 	}
 	if chkOvf.Int(i, bitsize) {
-		d.d.errorf("json: overflow %v bits: %s", bitsize, n.bytes)
+		d.d.errorf("json: overflow %v bits: %s", bitsize, d.bs)
 		return
 	}
 	// fmt.Printf("DecodeInt: %v\n", i)
 	return
 }
 
+// floatVal MUST only be called after a decNum, as d.bs now contains the bytes of the number
+func (d *jsonDecDriver) floatVal() (f float64) {
+	f, useStrConv := d.n.floatVal()
+	if useStrConv {
+		var err error
+		if f, err = strconv.ParseFloat(stringView(d.bs), 64); err != nil {
+			panic(fmt.Errorf("parse float: %s, %v", d.bs, err))
+		}
+		if d.n.neg {
+			f = -f
+		}
+	}
+	return
+}
+
 func (d *jsonDecDriver) DecodeUint(bitsize uint8) (u uint64) {
 	if c := d.s.sc.sep(); c != 0 {
 		d.expectChar(c)
@@ -868,7 +877,7 @@ func (d *jsonDecDriver) DecodeUint(bitsize uint8) (u uint64) {
 		}
 	}
 	if chkOvf.Uint(u, bitsize) {
-		d.d.errorf("json: overflow %v bits: %s", bitsize, n.bytes)
+		d.d.errorf("json: overflow %v bits: %s", bitsize, d.bs)
 		return
 	}
 	// fmt.Printf("DecodeUint: %v\n", u)
@@ -880,10 +889,9 @@ func (d *jsonDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
 		d.expectChar(c)
 	}
 	d.decNum(true)
-	n := &d.n
-	f = n.floatVal()
+	f = d.floatVal()
 	if chkOverflow32 && chkOvf.Float32(f) {
-		d.d.errorf("json: overflow float32: %v, %s", f, n.bytes)
+		d.d.errorf("json: overflow float32: %v, %s", f, d.bs)
 		return
 	}
 	return
@@ -916,12 +924,13 @@ func (d *jsonDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut [
 	if c := d.s.sc.sep(); c != 0 {
 		d.expectChar(c)
 	}
-	bs0 := d.appendStringAsBytes(d.b[:0])
+	d.appendStringAsBytes()
 	// if isstring, then just return the bytes, even if it is using the scratch buffer.
 	// the bytes will be converted to a string as needed.
 	if isstring {
-		return bs0
+		return d.bs
 	}
+	bs0 := d.bs
 	slen := base64.StdEncoding.DecodedLen(len(bs0))
 	if slen <= cap(bs) {
 		bsOut = bs[:slen]
@@ -945,13 +954,16 @@ func (d *jsonDecDriver) DecodeString() (s string) {
 	if c := d.s.sc.sep(); c != 0 {
 		d.expectChar(c)
 	}
-	return string(d.appendStringAsBytes(d.b[:0]))
+	d.appendStringAsBytes()
+	return string(d.bs)
 }
 
-func (d *jsonDecDriver) appendStringAsBytes(v []byte) []byte {
+func (d *jsonDecDriver) appendStringAsBytes() {
 	d.expectChar('"')
+	v := d.bs[:0]
+	var c uint8
 	for {
-		c := d.r.readn1()
+		c = d.r.readn1()
 		if c == '"' {
 			break
 		} else if c == '\\' {
@@ -979,7 +991,6 @@ func (d *jsonDecDriver) appendStringAsBytes(v []byte) []byte {
 				v = append(v, d.bstr[:w2]...)
 			default:
 				d.d.errorf("json: unsupported escaped value: %c", c)
-				return nil
 			}
 		} else {
 			v = append(v, c)
@@ -988,7 +999,7 @@ func (d *jsonDecDriver) appendStringAsBytes(v []byte) []byte {
 	if jsonTrackSkipWhitespace {
 		d.wsSkipped = false
 	}
-	return v
+	d.bs = v
 }
 
 func (d *jsonDecDriver) jsonU4(checkSlashU bool) rune {
@@ -1040,7 +1051,8 @@ func (d *jsonDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurthe
 		decodeFurther = true
 	case '"':
 		vt = valueTypeString
-		v = string(d.appendStringAsBytes(d.b[:0])) // same as d.DecodeString(), but skipping sep() call.
+		d.appendStringAsBytes()
+		v = string(d.bs) // same as d.DecodeString(), but skipping sep() call.
 	default: // number
 		d.decNum(true)
 		n := &d.n
@@ -1048,7 +1060,7 @@ func (d *jsonDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurthe
 		switch {
 		case n.explicitExponent, n.dot, n.exponent < 0, n.manOverflow:
 			vt = valueTypeFloat
-			v = n.floatVal()
+			v = d.floatVal()
 		case n.exponent == 0:
 			u := n.mantissa
 			switch {
@@ -1067,7 +1079,7 @@ func (d *jsonDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurthe
 			switch {
 			case overflow:
 				vt = valueTypeFloat
-				v = n.floatVal()
+				v = d.floatVal()
 			case n.neg:
 				vt = valueTypeInt
 				v = -int64(u)
@@ -1122,8 +1134,8 @@ func (h *JsonHandle) newEncDriver(e *Encoder) encDriver {
 func (h *JsonHandle) newDecDriver(d *Decoder) decDriver {
 	// d := jsonDecDriver{r: r.(*bytesDecReader), h: h}
 	hd := jsonDecDriver{d: d, r: d.r, h: h}
+	hd.bs = hd.b[:0]
 	hd.se.i = h.RawBytesExt
-	hd.n.bytes = d.b[:]
 	return &hd
 }
 

+ 29 - 10
codec/prebuild.sh

@@ -99,6 +99,14 @@ var fastpathAV fastpathA
 
 EOF
 
+    cat > gen-from-tmpl.codec.generated.go <<EOF
+package codec 
+import "io"
+func GenInternalGoFile(r io.Reader, w io.Writer, safe bool) error {
+return genInternalGoFile(r, w, safe)
+}
+EOF
+    
     cat > gen-from-tmpl.generated.go <<EOF
 //+build ignore
 
@@ -130,7 +138,7 @@ run("gen-helper.go.tmpl", "gen-helper.generated.go", false)
 
 EOF
     go run gen-from-tmpl.generated.go && \
-        rm -f gen-from-tmpl.generated.go 
+        rm -f gen-from-tmpl.*generated.go 
 }
 
 _codegenerators() {
@@ -140,15 +148,26 @@ _codegenerators() {
                 "1" == $( _needgen "values_ffjson${zsfx}" ) ||
                 1 == 0 ]] 
     then
-        true && \
-            echo "codecgen - !unsafe ... " && \
-            codecgen -rt codecgen -t 'x,codecgen,!unsafe' -o values_codecgen${zsfx} -d 1978 $zfin && \
-            echo "codecgen - unsafe ... " && \
-            codecgen  -u -rt codecgen -t 'x,codecgen,unsafe' -o values_codecgen_unsafe${zsfx} -d 1978 $zfin && \
-            echo "msgp ... " && \
-            msgp -tests=false -pkg=codec -o=values_msgp${zsfx} -file=$zfin && \
-            echo "ffjson ... " && \
-            ffjson -w values_ffjson${zsfx} $zfin && \
+        # codecgen creates some temporary files in the directory (main, pkg).
+        # Consequently, we should start msgp and ffjson first, and also put a small time latency before
+        # starting codecgen.
+        # Without this, ffjson chokes on one of the temporary files from codecgen.
+        echo "ffjson ... " && \
+            ffjson -w values_ffjson${zsfx} $zfin &
+        zzzIdFF=$!
+        echo "msgp ... " && \
+            msgp -tests=false -pkg=codec -o=values_msgp${zsfx} -file=$zfin &
+        zzzIdMsgp=$!
+
+        sleep 1 # give ffjson and msgp some buffer time. see note above.
+
+        echo "codecgen - !unsafe ... " && \
+            codecgen -rt codecgen -t 'x,codecgen,!unsafe' -o values_codecgen${zsfx} -d 19780 $zfin &
+        zzzIdC=$!
+        echo "codecgen - unsafe ... " && \
+            codecgen  -u -rt codecgen -t 'x,codecgen,unsafe' -o values_codecgen_unsafe${zsfx} -d 19781 $zfin &
+        zzzIdCU=$!
+        wait $zzzIdC $zzzIdCU $zzzIdMsgp $zzzIdFF && \
             # remove (M|Unm)arshalJSON implementations, so they don't conflict with encoding/json bench \
             sed -i 's+ MarshalJSON(+ _MarshalJSON(+g' values_ffjson${zsfx} && \
             sed -i 's+ UnmarshalJSON(+ _UnmarshalJSON(+g' values_ffjson${zsfx} && \

+ 1 - 1
codec/tests.sh

@@ -45,7 +45,7 @@ _run() {
 }
 
 # echo ">>>>>>> RUNNING VARIATIONS OF TESTS"    
-if [[ "x$@" = x ]]; then
+if [[ "x$@" = "x" ]]; then
     # r, x, g, gu
     _run "-rtcins"
     _run "-xtcins"

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor