Explorar o código

codec: use uint indices for array element and slicing to eliminate the x >= 0 bounds check, and more inlining

During array access and slicing, uint indices eliminate the need for a "index >= 0" bounds check

Also, goto's allow a function to be inlined, while "for loops" still prevent inlining (as at go 1.12)

Changes:
- cursor, numread fields in dec readers are uint (not int)
  so that slicing/indexing bounds checks are cheaper
- readx(uint) now takes uint length - formerly took int parameter
- use "for ... range" instead of "for i = 0; ... i++" pattern to eliminate some bounds checks
- binary search functions: fastpathA.index(...) and TypeInfos.find(...)
  - use uint indices to help eliminate some bounds checks
  - use goto's in place of "for loops" making them inline'able.

This improves the performance of decoding.
Ugorji Nwoke %!s(int64=7) %!d(string=hai) anos
pai
achega
6c173b2436
Modificáronse 9 ficheiros con 300 adicións e 257 borrados
  1. 6 6
      codec/binc.go
  2. 1 1
      codec/cbor.go
  3. 1 1
      codec/codec_test.go
  4. 101 75
      codec/decode.go
  5. 127 122
      codec/fast-path.generated.go
  6. 43 38
      codec/fast-path.go.tmpl
  7. 14 7
      codec/helper.go
  8. 3 3
      codec/msgpack.go
  9. 4 4
      codec/simple.go

+ 6 - 6
codec/binc.go

@@ -478,7 +478,7 @@ func (d *bincDecDriver) DecodeTime() (t time.Time) {
 		d.d.errorf("cannot decode time - %s %x-%x/%s", msgBadDesc, d.vd, d.vs, bincdesc(d.vd, d.vs))
 		return
 	}
-	t, err := bincDecodeTime(d.r.readx(int(d.vs)))
+	t, err := bincDecodeTime(d.r.readx(uint(d.vs)))
 	if err != nil {
 		panic(err)
 	}
@@ -711,7 +711,7 @@ func (d *bincDecDriver) decStringAndBytes(bs []byte, withString, zerocopy bool)
 		slen = d.decLen()
 		if zerocopy {
 			if d.br {
-				bs2 = d.r.readx(slen)
+				bs2 = d.r.readx(uint(slen))
 			} else if len(bs) == 0 {
 				bs2 = decByteSlice(d.r, slen, d.d.h.MaxInitLen, d.b[:])
 			} else {
@@ -820,7 +820,7 @@ func (d *bincDecDriver) DecodeBytes(bs []byte, zerocopy bool) (bsOut []byte) {
 	d.bdRead = false
 	if zerocopy {
 		if d.br {
-			return d.r.readx(clen)
+			return d.r.readx(uint(clen))
 		} else if len(bs) == 0 {
 			bs = d.b[:]
 		}
@@ -857,7 +857,7 @@ func (d *bincDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs []b
 			return
 		}
 		if d.br {
-			xbs = d.r.readx(l)
+			xbs = d.r.readx(uint(l))
 		} else {
 			xbs = decByteSlice(d.r, l, d.d.h.MaxInitLen, d.d.b[:])
 		}
@@ -936,7 +936,7 @@ func (d *bincDecDriver) DecodeNaked() {
 		n.l = d.DecodeBytes(nil, false)
 	case bincVdTimestamp:
 		n.v = valueTypeTime
-		tt, err := bincDecodeTime(d.r.readx(int(d.vs)))
+		tt, err := bincDecodeTime(d.r.readx(uint(d.vs)))
 		if err != nil {
 			panic(err)
 		}
@@ -946,7 +946,7 @@ func (d *bincDecDriver) DecodeNaked() {
 		l := d.decLen()
 		n.u = uint64(d.r.readn1())
 		if d.br {
-			n.l = d.r.readx(l)
+			n.l = d.r.readx(uint(l))
 		} else {
 			n.l = decByteSlice(d.r, l, d.d.h.MaxInitLen, d.d.b[:])
 		}

+ 1 - 1
codec/cbor.go

@@ -543,7 +543,7 @@ func (d *cborDecDriver) DecodeBytes(bs []byte, zerocopy bool) (bsOut []byte) {
 	d.bdRead = false
 	if zerocopy {
 		if d.br {
-			return d.r.readx(clen)
+			return d.r.readx(uint(clen))
 		} else if len(bs) == 0 {
 			bs = d.d.b[:]
 		}

+ 1 - 1
codec/codec_test.go

@@ -1686,7 +1686,7 @@ func doTestSwallowAndZero(t *testing.T, h Handle) {
 	e1.MustEncode(v1)
 	d1 := NewDecoderBytes(b1, h)
 	d1.swallow()
-	if d1.r.numread() != len(b1) {
+	if d1.r.numread() != uint(len(b1)) {
 		logT(t, "swallow didn't consume all encoded bytes: %v out of %v", d1.r.numread(), len(b1))
 		failT(t)
 	}

+ 101 - 75
codec/decode.go

@@ -56,7 +56,7 @@ type decReader interface {
 	readx(n int) []byte
 	readb([]byte)
 	readn1() uint8
-	numread() int // number of bytes read
+	numread() uint // number of bytes read
 	track()
 	stopTrack() []byte
 
@@ -274,7 +274,7 @@ const (
 type ioDecReaderCommon struct {
 	r io.Reader // the reader passed in
 
-	n int // num read
+	n uint // num read
 
 	l   byte             // last byte
 	ls  unreadByteStatus // last byte status
@@ -295,7 +295,7 @@ func (z *ioDecReaderCommon) reset(r io.Reader) {
 	}
 }
 
-func (z *ioDecReaderCommon) numread() int {
+func (z *ioDecReaderCommon) numread() uint {
 	return z.n
 }
 
@@ -392,11 +392,11 @@ func (z *ioDecReader) UnreadByte() (err error) {
 	return
 }
 
-func (z *ioDecReader) readx(n int) (bs []byte) {
-	if n <= 0 {
+func (z *ioDecReader) readx(n uint) (bs []byte) {
+	if n == 0 {
 		return
 	}
-	if n < len(z.x) {
+	if n < uint(len(z.x)) {
 		bs = z.x[:n]
 	} else {
 		bs = make([]byte, n)
@@ -404,7 +404,7 @@ func (z *ioDecReader) readx(n int) (bs []byte) {
 	if _, err := decReadFull(z.rr, bs); err != nil {
 		panic(err)
 	}
-	z.n += len(bs)
+	z.n += uint(len(bs))
 	if z.trb {
 		z.tr = append(z.tr, bs...)
 	}
@@ -418,7 +418,7 @@ func (z *ioDecReader) readb(bs []byte) {
 	if _, err := decReadFull(z.rr, bs); err != nil {
 		panic(err)
 	}
-	z.n += len(bs)
+	z.n += uint(len(bs))
 	if z.trb {
 		z.tr = append(z.tr, bs...)
 	}
@@ -545,7 +545,7 @@ func (z *ioDecReader) unreadn1() {
 type bufioDecReader struct {
 	ioDecReaderCommon
 
-	c   int // cursor
+	c   uint // cursor
 	buf []byte
 	// err error
 	_ [1]uint64 // padding
@@ -558,10 +558,10 @@ func (z *bufioDecReader) reset(r io.Reader) {
 }
 
 func (z *bufioDecReader) readb(p []byte) {
-	var n = copy(p, z.buf[z.c:])
+	var n = uint(copy(p, z.buf[z.c:]))
 	z.n += n
 	z.c += n
-	if len(p) == n {
+	if len(p) == int(n) {
 		if z.trb {
 			z.tr = append(z.tr, p...) // cost=9
 		}
@@ -571,10 +571,10 @@ func (z *bufioDecReader) readb(p []byte) {
 }
 
 //go:noinline - fallback when z.buf is consumed
-func (z *bufioDecReader) readbFill(p0 []byte, n int) {
+func (z *bufioDecReader) readbFill(p0 []byte, n uint) {
 	// at this point, there's nothing in z.buf to read (z.buf is fully consumed)
 	p := p0[n:]
-	var n2 int
+	var n2 uint
 	var err error
 	if len(p) > cap(z.buf) {
 		n2, err = decReadFull(z.r, p)
@@ -597,12 +597,14 @@ LOOP:
 	// for len(p) > 0 && z.err == nil {
 	if len(p) > 0 {
 		z.buf = z.buf[0:cap(z.buf)]
-		n2, err = z.r.Read(z.buf)
+		var n1 int
+		n1, err = z.r.Read(z.buf)
+		n2 = uint(n1)
 		if n2 == 0 && err != nil {
 			panic(err)
 		}
 		z.buf = z.buf[:n2]
-		n2 = copy(p, z.buf)
+		n2 = uint(copy(p, z.buf))
 		z.c = n2
 		n += n2
 		z.n += n2
@@ -621,7 +623,7 @@ LOOP:
 
 func (z *bufioDecReader) readn1() (b byte) {
 	// fast-path, so we elide calling into Read() most of the time
-	if z.c < len(z.buf) {
+	if z.c < uint(len(z.buf)) {
 		b = z.buf[z.c]
 		z.c++
 		z.n++
@@ -646,11 +648,10 @@ func (z *bufioDecReader) unreadn1() {
 	}
 }
 
-func (z *bufioDecReader) readx(n int) (bs []byte) {
-	if n <= 0 { // cost=5 // assume n > 0 always.
-		return
-	}
-	if z.c+n <= len(z.buf) {
+func (z *bufioDecReader) readx(n uint) (bs []byte) {
+	if n == 0 {
+		// return
+	} else if z.c+n <= uint(len(z.buf)) {
 		bs = z.buf[z.c : z.c+n]
 		z.n += n
 		z.c += n
@@ -660,7 +661,7 @@ func (z *bufioDecReader) readx(n int) (bs []byte) {
 	} else {
 		bs = make([]byte, n)
 		// n no longer used - can reuse
-		n = copy(bs, z.buf[z.c:])
+		n = uint(copy(bs, z.buf[z.c:]))
 		z.n += n
 		z.c += n
 		z.readbFill(bs, n)
@@ -669,11 +670,11 @@ func (z *bufioDecReader) readx(n int) (bs []byte) {
 }
 
 //go:noinline - track called by Decoder.nextValueBytes() (called by jsonUnmarshal,rawBytes)
-func (z *bufioDecReader) doTrack(y int) {
+func (z *bufioDecReader) doTrack(y uint) {
 	z.tr = append(z.tr, z.buf[z.c:y]...) // cost=14???
 }
 
-func (z *bufioDecReader) skipLoopFn(i int) {
+func (z *bufioDecReader) skipLoopFn(i uint) {
 	z.n += (i - z.c) - 1
 	i++
 	if z.trb {
@@ -695,9 +696,15 @@ func (z *bufioDecReader) skip(accept *bitset256) (token byte) {
 
 	i := z.c
 LOOP:
-	if i < len(z.buf) {
+	if i < uint(len(z.buf)) {
 		if token = z.buf[i]; !accept.isset(token) {
-			z.skipLoopFn(i)
+			// z.skipLoopFn(i) // inline below so cost is within inline budget
+			z.n += (i - z.c) - 1
+			i++
+			if z.trb {
+				z.doTrack(i)
+			}
+			z.c = i
 			return
 		}
 		i++
@@ -707,7 +714,7 @@ LOOP:
 }
 
 func (z *bufioDecReader) skipFill(accept *bitset256) (token byte) {
-	z.n += len(z.buf) - z.c
+	z.n += uint(len(z.buf)) - z.c
 	if z.trb {
 		z.tr = append(z.tr, z.buf[z.c:]...)
 	}
@@ -721,20 +728,27 @@ func (z *bufioDecReader) skipFill(accept *bitset256) (token byte) {
 			panic(err)
 		}
 		z.buf = z.buf[:n2]
-		for i := 0; i < n2; i++ {
-			if token = z.buf[i]; !accept.isset(token) {
-				z.skipLoopFn(i)
+		var i int
+		for i, token = range z.buf {
+			if !accept.isset(token) {
+				z.skipLoopFn(uint(i))
 				return
 			}
 		}
-		z.n += n2
+		// for i := 0; i < n2; i++ {
+		// 	if token = z.buf[i]; !accept.isset(token) {
+		// 		z.skipLoopFn(i)
+		// 		return
+		// 	}
+		// }
+		z.n += uint(n2)
 		if z.trb {
-			z.tr = append(z.tr, z.buf[:n2]...)
+			z.tr = append(z.tr, z.buf...)
 		}
 	}
 }
 
-func (z *bufioDecReader) readToLoopFn(i int, out0 []byte) (out []byte) {
+func (z *bufioDecReader) readToLoopFn(i uint, out0 []byte) (out []byte) {
 	// out0 is never nil
 	z.n += (i - z.c) - 1
 	out = append(out0, z.buf[z.c:i]...)
@@ -756,7 +770,7 @@ func (z *bufioDecReader) readTo(in []byte, accept *bitset256) (out []byte) {
 
 	i := z.c
 LOOP:
-	if i < len(z.buf) {
+	if i < uint(len(z.buf)) {
 		if !accept.isset(z.buf[i]) {
 			// return z.readToLoopFn(i, nil)
 			// inline readToLoopFn here (for performance)
@@ -775,7 +789,7 @@ LOOP:
 }
 
 func (z *bufioDecReader) readToFill(in []byte, accept *bitset256) (out []byte) {
-	z.n += len(z.buf) - z.c
+	z.n += uint(len(z.buf)) - z.c
 	out = append(in, z.buf[z.c:]...)
 	if z.trb {
 		z.tr = append(z.tr, z.buf[z.c:]...)
@@ -793,20 +807,25 @@ func (z *bufioDecReader) readToFill(in []byte, accept *bitset256) (out []byte) {
 			panic(err)
 		}
 		z.buf = z.buf[:n2]
-		for i := 0; i < n2; i++ {
-			if !accept.isset(z.buf[i]) {
-				return z.readToLoopFn(i, out)
+		for i, token := range z.buf {
+			if !accept.isset(token) {
+				return z.readToLoopFn(uint(i), out)
 			}
 		}
-		out = append(out, z.buf[:n2]...)
-		z.n += n2
+		// for i := 0; i < n2; i++ {
+		// 	if !accept.isset(z.buf[i]) {
+		// 		return z.readToLoopFn(i, out)
+		// 	}
+		// }
+		out = append(out, z.buf...)
+		z.n += uint(n2)
 		if z.trb {
-			z.tr = append(z.tr, z.buf[:n2]...)
+			z.tr = append(z.tr, z.buf...)
 		}
 	}
 }
 
-func (z *bufioDecReader) readUntilLoopFn(i int, out0 []byte) (out []byte) {
+func (z *bufioDecReader) readUntilLoopFn(i uint, out0 []byte) (out []byte) {
 	z.n += (i - z.c) - 1
 	i++
 	out = append(out0, z.buf[z.c:i]...)
@@ -829,7 +848,7 @@ func (z *bufioDecReader) readUntil(in []byte, stop byte) (out []byte) {
 
 	i := z.c
 LOOP:
-	if i < len(z.buf) {
+	if i < uint(len(z.buf)) {
 		if z.buf[i] == stop {
 			// inline readUntilLoopFn
 			// return z.readUntilLoopFn(i, nil)
@@ -849,30 +868,37 @@ LOOP:
 }
 
 func (z *bufioDecReader) readUntilFill(in []byte, stop byte) (out []byte) {
-	z.n += len(z.buf) - z.c
+	z.n += uint(len(z.buf)) - z.c
 	out = append(in, z.buf[z.c:]...)
 	if z.trb {
 		z.tr = append(z.tr, z.buf[z.c:]...)
 	}
-	var n2 int
+	var n1 int
+	var n2 uint
 	var err error
 	for {
 		z.c = 0
 		z.buf = z.buf[0:cap(z.buf)]
-		n2, err = z.r.Read(z.buf)
+		n1, err = z.r.Read(z.buf)
+		n2 = uint(n1)
 		if n2 == 0 && err != nil {
 			panic(err)
 		}
 		z.buf = z.buf[:n2]
-		for i := 0; i < n2; i++ {
-			if z.buf[i] == stop {
-				return z.readUntilLoopFn(i, out)
+		for i, token := range z.buf {
+			if token == stop {
+				return z.readUntilLoopFn(uint(i), out)
 			}
 		}
-		out = append(out, z.buf[:n2]...)
+		// for i := 0; i < n2; i++ {
+		// 	if z.buf[i] == stop {
+		// 		return z.readUntilLoopFn(i, out)
+		// 	}
+		// }
+		out = append(out, z.buf...)
 		z.n += n2
 		if z.trb {
-			z.tr = append(z.tr, z.buf[:n2]...)
+			z.tr = append(z.tr, z.buf...)
 		}
 	}
 }
@@ -884,8 +910,8 @@ var errBytesDecReaderCannotUnread = errors.New("cannot unread last byte read")
 // bytesDecReader is a decReader that reads off a byte slice with zero copying
 type bytesDecReader struct {
 	b []byte // data
-	c int    // cursor
-	t int    // track start
+	c uint   // cursor
+	t uint   // track start
 	// a int    // available
 }
 
@@ -896,7 +922,7 @@ func (z *bytesDecReader) reset(in []byte) {
 	z.t = 0
 }
 
-func (z *bytesDecReader) numread() int {
+func (z *bytesDecReader) numread() uint {
 	return z.c
 }
 
@@ -908,7 +934,7 @@ func (z *bytesDecReader) unreadn1() {
 	// z.a++
 }
 
-func (z *bytesDecReader) readx(n int) (bs []byte) {
+func (z *bytesDecReader) readx(n uint) (bs []byte) {
 	// slicing from a non-constant start position is more expensive,
 	// as more computation is required to decipher the pointer start position.
 	// However, we do it only once, and it's better than reslicing both z.b and return value.
@@ -926,13 +952,13 @@ func (z *bytesDecReader) readx(n int) (bs []byte) {
 	// }
 	// return
 
-	if n <= 0 {
+	if n == 0 {
 		return
 	}
-	if z.c == len(z.b) {
+	if z.c == uint(len(z.b)) {
 		panic(io.EOF)
 	}
-	if n > len(z.b)-z.c {
+	if z.c+n > uint(len(z.b)) {
 		panic(io.ErrUnexpectedEOF)
 	}
 
@@ -942,11 +968,11 @@ func (z *bytesDecReader) readx(n int) (bs []byte) {
 }
 
 func (z *bytesDecReader) readb(bs []byte) {
-	copy(bs, z.readx(len(bs)))
+	copy(bs, z.readx(uint(len(bs))))
 }
 
 func (z *bytesDecReader) readn1() (v uint8) {
-	if z.c == len(z.b) {
+	if z.c == uint(len(z.b)) {
 		panic(io.EOF)
 	}
 	v = z.b[z.c]
@@ -967,7 +993,7 @@ func (z *bytesDecReader) readn1() (v uint8) {
 // }
 
 func (z *bytesDecReader) skip(accept *bitset256) (token byte) {
-	i := uint(z.c)
+	i := z.c
 	// if i == len(z.b) {
 	// 	goto END
 	// 	// panic(io.EOF)
@@ -993,7 +1019,7 @@ LOOP:
 			goto LOOP
 		}
 		// z.a -= (i - z.c)
-		z.c = int(i)
+		z.c = i
 		return
 	}
 	// END:
@@ -1008,7 +1034,8 @@ func (z *bytesDecReader) readTo(_ []byte, accept *bitset256) (out []byte) {
 }
 
 func (z *bytesDecReader) readToNoInput(accept *bitset256) (out []byte) {
-	if z.c == len(z.b) {
+	i := z.c
+	if i == uint(len(z.b)) {
 		panic(io.EOF)
 	}
 
@@ -1044,7 +1071,6 @@ func (z *bytesDecReader) readToNoInput(accept *bitset256) (out []byte) {
 	// 	return
 
 	// c := i
-	i := uint(z.c)
 LOOP:
 	if i < uint(len(z.b)) {
 		if accept.isset(z.b[i]) {
@@ -1055,7 +1081,7 @@ LOOP:
 
 	out = z.b[z.c:i]
 	// z.a -= (i - z.c)
-	z.c = int(i)
+	z.c = i
 	return // z.b[c:i]
 	// z.c, i = i, z.c
 	// return z.b[i:z.c]
@@ -1066,7 +1092,7 @@ func (z *bytesDecReader) readUntil(_ []byte, stop byte) (out []byte) {
 }
 
 func (z *bytesDecReader) readUntilNoInput(stop byte) (out []byte) {
-	i := uint(z.c)
+	i := z.c
 	// if i == len(z.b) {
 	// 	panic(io.EOF)
 	// }
@@ -1087,7 +1113,7 @@ LOOP:
 			i++
 			out = z.b[z.c:i]
 			// z.a -= (i - z.c)
-			z.c = int(i)
+			z.c = i
 			return
 		}
 		i++
@@ -2112,7 +2138,7 @@ func (z *decReaderSwitch) readUntil(in []byte, stop byte) (out []byte) {
 // This allows for the inlining of the common path when z.bytes=true.
 // Go 1.12+ supports inlining methods with up to 1 inlined function (or 2 if no other constructs).
 
-func (z *decReaderSwitch) numread() int {
+func (z *decReaderSwitch) numread() uint {
 	if z.bytes {
 		return z.rb.numread()
 	} else if z.bufio {
@@ -2155,14 +2181,14 @@ func (z *decReaderSwitch) unreadn1IO() {
 		z.ri.unreadn1()
 	}
 }
-func (z *decReaderSwitch) readx(n int) []byte {
+func (z *decReaderSwitch) readx(n uint) []byte {
 	if z.bytes {
 		return z.rb.readx(n)
 	}
 	return z.readxIO(n)
 }
 
-func (z *decReaderSwitch) readxIO(n int) []byte {
+func (z *decReaderSwitch) readxIO(n uint) []byte {
 	if z.bufio {
 		return z.bi.readx(n)
 	}
@@ -2872,12 +2898,12 @@ func (d *Decoder) rawBytes() []byte {
 }
 
 func (d *Decoder) wrapErr(v interface{}, err *error) {
-	*err = decodeError{codecError: codecError{name: d.hh.Name(), err: v}, pos: d.r.numread()}
+	*err = decodeError{codecError: codecError{name: d.hh.Name(), err: v}, pos: int(d.r.numread())}
 }
 
 // NumBytesRead returns the number of bytes read
 func (d *Decoder) NumBytesRead() int {
-	return d.r.numread()
+	return int(d.r.numread())
 }
 
 // --------------------------------------------------
@@ -3038,16 +3064,16 @@ func expandSliceRV(s reflect.Value, st reflect.Type, canChange bool, stElemSize,
 	return
 }
 
-func decReadFull(r io.Reader, bs []byte) (n int, err error) {
+func decReadFull(r io.Reader, bs []byte) (n uint, err error) {
 	var nn int
-	for n < len(bs) && err == nil {
+	for n < uint(len(bs)) && err == nil {
 		nn, err = r.Read(bs[n:])
 		if nn > 0 {
 			if err == io.EOF {
 				// leave EOF for next time
 				err = nil
 			}
-			n += nn
+			n += uint(nn)
 		}
 	}
 	// xdebugf("decReadFull: len(bs): %v, n: %v, err: %v", len(bs), n, err)

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 127 - 122
codec/fast-path.generated.go


+ 43 - 38
codec/fast-path.go.tmpl

@@ -52,17 +52,22 @@ type fastpathA [{{ .FastpathLen }}]fastpathE
 
 func (x *fastpathA) index(rtid uintptr) int {
 	// use binary search to grab the index (adapted from sort/search.go)
-	h, i, j := 0, 0, {{ .FastpathLen }} // len(x)
-	for i < j {
+	// Note: we use goto (instead of for loop) so this can be inlined.
+ 	// h, i, j := 0, 0, len(x)
+	var h, i uint
+	var j = uint(len(x))
+LOOP:
+	if i < j {
 		h = i + (j-i)/2
 		if x[h].rtid < rtid {
 			i = h + 1
 		} else {
 			j = h
 		}
+		goto LOOP
 	}
-	if i < {{ .FastpathLen }} && x[i].rtid == rtid {
-		return i
+	if i < uint(len(x)) && x[i].rtid == rtid {
+		return int(i)
 	}
 	return -1
 }
@@ -118,7 +123,7 @@ func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
 */}}{{end}}{{end}}{{end}}
 
 	default:
-        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
+		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
 		return false
 	}
 	return true
@@ -142,7 +147,7 @@ func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
 		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e)
 {{end}}{{end}}{{end}}
 	default:
-        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
+		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
 		return false
 	}
 	return true
@@ -157,7 +162,7 @@ func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
 		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e)
 {{end}}{{end}}{{end}}
 	default:
-        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
+		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
 		return false
 	}
 	return true
@@ -249,10 +254,10 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Ele
 		*/}}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 i, l uint
 		var vp *bytesI {{/* put loop variables outside. seems currently needed for better perf */}}
 		for k2 := range v {
-			l = len(mksv)
+			l = uint(len(mksv))
 			e2.MustEncode(k2)
 			vp = &v2[i]
 			vp.v = mksv[l:]
@@ -279,7 +284,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Ele
 			if esep { ee.WriteMapElemValue() }
 			e.encode(v[v2[j].i])
 		} */}} {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
-		var i int 
+		var i uint
 		for k := range v {
 			v2[i] = {{ $x }}(k)
 			i++
@@ -333,19 +338,19 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Ele
 
 // -- -- fast path type switch
 func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
-     var changed bool
+	 var changed bool
 	switch v := iv.(type) {
 {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}{{if ne .Elem "uint8"}}
 	case []{{ .Elem }}:
-        var v2 []{{ .Elem }}
+		var v2 []{{ .Elem }}
 		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, false, d)
-        if changed && len(v) > 0 && len(v2) > 0 && !(len(v2) == len(v) && &v2[0] == &v[0]) {
+		if changed && len(v) > 0 && len(v2) > 0 && !(len(v2) == len(v) && &v2[0] == &v[0]) {
 			copy(v, v2)
 		}
 	case *[]{{ .Elem }}:
-        var v2 []{{ .Elem }}
+		var v2 []{{ .Elem }}
 		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d)
-        if changed {
+		if changed {
 			*v = v2 
 		}{{/*
 */}}{{end}}{{end}}{{end}}{{end}}
@@ -355,14 +360,14 @@ func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
 	case map[{{ .MapKey }}]{{ .Elem }}:
 		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, false, d)
 	case *map[{{ .MapKey }}]{{ .Elem }}:
-         var v2 map[{{ .MapKey }}]{{ .Elem }}
+		 var v2 map[{{ .MapKey }}]{{ .Elem }}
 		v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, true, d)
-        if changed {
+		if changed {
 			*v = v2 
 		}{{/*
 */}}{{end}}{{end}}{{end}}
 	default:
-        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
+		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
 		return false
 	}
 	return true
@@ -379,7 +384,7 @@ func fastpathDecodeSetZeroTypeSwitch(iv interface{}) bool {
 		*v = nil {{/*
 */}}{{end}}{{end}}{{end}}
 	default:
-        _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
+		_ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4
 		return false
 	}
 	return true
@@ -397,23 +402,23 @@ func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv r
 	if array := f.seq == seqTypeArray; !array && rv.Kind() == reflect.Ptr {
 		vp := rv2i(rv).(*[]{{ .Elem }})
 		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, !array, d)
-        if changed { *vp = v }
+		if changed { *vp = v }
 	} else {
 		v := rv2i(rv).([]{{ .Elem }})
-        v2, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, !array, d)
-        if changed && len(v) > 0 && len(v2) > 0 && !(len(v2) == len(v) && &v2[0] == &v[0]) {
-           copy(v, v2)
-        }
+		v2, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, !array, d)
+		if changed && len(v) > 0 && len(v2) > 0 && !(len(v2) == len(v) && &v2[0] == &v[0]) {
+		   copy(v, v2)
+		}
 	}
 }
 func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, d *Decoder) {
 	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
-    if changed { *vp = v }
+	if changed { *vp = v }
 }
 func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
 	dd := d.d{{/*
-	    // if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil()
-    */}}
+		// if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil()
+	*/}}
 	slh, containerLenS := d.decSliceHelperStart()
 	if containerLenS == 0 {
 		if canChange {
@@ -430,9 +435,9 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange
 		if containerLenS > cap(v) {
 			xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
 			if xlen <= cap(v) {
-				v = v[:xlen]
+				v = v[:uint(xlen)]
 			} else {
-				v = make([]{{ .Elem }}, xlen)
+				v = make([]{{ .Elem }}, uint(xlen))
 			}
 			changed = true 
 		} else if containerLenS != len(v) {
@@ -448,7 +453,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange
 			} else {
 				xlen = 8
 			}
-			v = make([]{{ .Elem }}, xlen)
+			v = make([]{{ .Elem }}, uint(xlen))
 			changed = true 
 		}
 		// if indefinite, etc, then expand the slice if necessary
@@ -466,14 +471,14 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange
 		if decodeIntoBlank {
 			d.swallow()
 		} else if dd.TryDecodeAsNil() {
-			v[j] = {{ zerocmd .Elem }}
+			v[uint(j)] = {{ zerocmd .Elem }}
 		} else {
-			{{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
+			{{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem }}{{ end }}
 		}
 	}
 	if canChange {
 		if j < len(v) {
-			v = v[:j]
+			v = v[:uint(j)]
 			changed = true
 		} else if j == 0 && v == nil {
 			v = make([]{{ .Elem }}, 0)
@@ -498,8 +503,8 @@ func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv r
 		v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d);
 		if changed { *vp = v }
 	} else {
-	    fastpathTV.{{ .MethodNamePfx "Dec" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), false, d)
-    }
+		fastpathTV.{{ .MethodNamePfx "Dec" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), false, d)
+	}
 }
 func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) {
 	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
@@ -508,8 +513,8 @@ func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .E
 func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, canChange bool, 
 	d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
 	dd, esep := d.d, d.hh.hasElemSeparators(){{/*
-	    // if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil()
-    */}}
+		// if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil()
+	*/}}
 	containerLen := dd.ReadMapStart()
 	if canChange && v == nil {
 		xlen := decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})
@@ -522,7 +527,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
 	}
 	d.depthIncr()
 	{{ if eq .Elem "interface{}" }}mapGet := v != nil && !d.h.MapValueReset && !d.h.InterfaceReset
-    {{end}}var mk {{ .MapKey }}
+	{{end}}var mk {{ .MapKey }}
 	var mv {{ .Elem }}
 	hasLen := containerLen > 0
 	for j := 0; (hasLen && j < containerLen) || !(hasLen || dd.CheckBreak()); j++ {

+ 14 - 7
codec/helper.go

@@ -1224,30 +1224,36 @@ func (x *TypeInfos) structTag(t reflect.StructTag) (s string) {
 	return
 }
 
-func (x *TypeInfos) find(s []rtid2ti, rtid uintptr) (idx int, ti *typeInfo) {
+func (x *TypeInfos) find(s []rtid2ti, rtid uintptr) (i uint, ti *typeInfo) {
 	// binary search. adapted from sort/search.go.
+	// Note: we use goto (instead of for loop) so this can be inlined.
+
 	// if sp == nil {
 	// 	return -1, nil
 	// }
 	// s := *sp
-	h, i, j := 0, 0, len(s)
-	for i < j {
+
+	// h, i, j := 0, 0, len(s)
+	var h uint // var h, i uint
+	var j = uint(len(s))
+LOOP:
+	if i < j {
 		h = i + (j-i)/2
 		if s[h].rtid < rtid {
 			i = h + 1
 		} else {
 			j = h
 		}
+		goto LOOP
 	}
-	if i < len(s) && s[i].rtid == rtid {
-		return i, s[i].ti
+	if i < uint(len(s)) && s[i].rtid == rtid {
+		ti = s[i].ti
 	}
-	return i, nil
+	return
 }
 
 func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
 	sp := x.infos.load()
-	var idx int
 	if sp != nil {
 		_, pti = x.find(sp, rtid)
 		if pti != nil {
@@ -1334,6 +1340,7 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
 		vs := []rtid2ti{{rtid, pti}}
 		x.infos.store(vs)
 	} else {
+		var idx uint
 		idx, pti = x.find(sp, rtid)
 		if pti == nil {
 			pti = &ti

+ 3 - 3
codec/msgpack.go

@@ -542,7 +542,7 @@ func (d *msgpackDecDriver) DecodeNaked() {
 				n.v = valueTypeTime
 				n.t = d.decodeTime(clen)
 			} else if d.br {
-				n.l = d.r.readx(clen)
+				n.l = d.r.readx(uint(clen))
 			} else {
 				n.l = decByteSlice(d.r, clen, d.d.h.MaxInitLen, d.d.b[:])
 			}
@@ -726,7 +726,7 @@ func (d *msgpackDecDriver) DecodeBytes(bs []byte, zerocopy bool) (bsOut []byte)
 	}
 	if zerocopy {
 		if d.br {
-			return d.r.readx(clen)
+			return d.r.readx(uint(clen))
 		} else if len(bs) == 0 {
 			bs = d.d.b[:]
 		}
@@ -937,7 +937,7 @@ func (d *msgpackDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs
 			return
 		}
 		if d.br {
-			xbs = d.r.readx(clen)
+			xbs = d.r.readx(uint(clen))
 		} else {
 			xbs = decByteSlice(d.r, clen, d.d.h.MaxInitLen, d.d.b[:])
 		}

+ 4 - 4
codec/simple.go

@@ -459,7 +459,7 @@ func (d *simpleDecDriver) DecodeBytes(bs []byte, zerocopy bool) (bsOut []byte) {
 	d.bdRead = false
 	if zerocopy {
 		if d.br {
-			return d.r.readx(clen)
+			return d.r.readx(uint(clen))
 		} else if len(bs) == 0 {
 			bs = d.d.b[:]
 		}
@@ -481,7 +481,7 @@ func (d *simpleDecDriver) DecodeTime() (t time.Time) {
 	}
 	d.bdRead = false
 	clen := int(d.r.readn1())
-	b := d.r.readx(clen)
+	b := d.r.readx(uint(clen))
 	if err := (&t).UnmarshalBinary(b); err != nil {
 		d.d.errorv(err)
 	}
@@ -518,7 +518,7 @@ func (d *simpleDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs [
 			return
 		}
 		if d.br {
-			xbs = d.r.readx(l)
+			xbs = d.r.readx(uint(l))
 		} else {
 			xbs = decByteSlice(d.r, l, d.d.h.MaxInitLen, d.d.b[:])
 		}
@@ -583,7 +583,7 @@ func (d *simpleDecDriver) DecodeNaked() {
 		l := d.decLen()
 		n.u = uint64(d.r.readn1())
 		if d.br {
-			n.l = d.r.readx(l)
+			n.l = d.r.readx(uint(l))
 		} else {
 			n.l = decByteSlice(d.r, l, d.d.h.MaxInitLen, d.d.b[:])
 		}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio