Browse Source

codec: support optimized decode of true/false/null in json

Do this by supporting the ability to read up to 5 bytes
at a time efficiently.
Ugorji Nwoke 6 years ago
parent
commit
9d3a64a20a
3 changed files with 87 additions and 13 deletions
  1. 6 6
      codec/json.go
  2. 77 6
      codec/reader.go
  3. 4 1
      codec/writer.go

+ 6 - 6
codec/json.go

@@ -740,25 +740,25 @@ func (d *jsonDecDriver) ReadMapEnd() {
 // }
 
 func (d *jsonDecDriver) readLit4True() {
-	bs := d.d.decRd.readx(3)
+	bs := d.d.decRd.readn3()
 	d.tok = 0
-	if jsonValidateSymbols && !bytes.Equal(bs, jsonLiteral4True) {
+	if jsonValidateSymbols && bs != [3]byte{'r', 'u', 'e'} { // !bytes.Equal(bs, jsonLiteral4True)
 		d.d.errorf("expecting %s: got %s", jsonLiteral4True, bs)
 	}
 }
 
 func (d *jsonDecDriver) readLit4False() {
-	bs := d.d.decRd.readx(4)
+	bs := d.d.decRd.readn4()
 	d.tok = 0
-	if jsonValidateSymbols && !bytes.Equal(bs, jsonLiteral4False) {
+	if jsonValidateSymbols && bs != [4]byte{'a', 'l', 's', 'e'} { // !bytes.Equal(bs, jsonLiteral4False)
 		d.d.errorf("expecting %s: got %s", jsonLiteral4False, bs)
 	}
 }
 
 func (d *jsonDecDriver) readLit4Null() {
-	bs := d.d.decRd.readx(3)
+	bs := d.d.decRd.readn3() // readx(3)
 	d.tok = 0
-	if jsonValidateSymbols && !bytes.Equal(bs, jsonLiteral4Null) {
+	if jsonValidateSymbols && bs != [3]byte{'u', 'l', 'l'} { // !bytes.Equal(bs, jsonLiteral4Null)
 		d.d.errorf("expecting %s: got %s", jsonLiteral4Null, bs)
 	}
 	d.fnil = true

+ 77 - 6
codec/reader.go

@@ -196,6 +196,18 @@ func (z *ioDecReader) UnreadByte() (err error) {
 	return
 }
 
+func (z *ioDecReader) readn3() (bs [3]byte) {
+	z.readb(bs[:])
+	// copy(bs[:], z.readx(3))
+	return
+}
+
+func (z *ioDecReader) readn4() (bs [4]byte) {
+	z.readb(bs[:])
+	// copy(bs[:], z.readx(4))
+	return
+}
+
 func (z *ioDecReader) readx(n uint) (bs []byte) {
 	if n == 0 {
 		return
@@ -389,7 +401,6 @@ func (z *bufioDecReader) readb(p []byte) {
 	}
 }
 
-//go:noinline - fallback when z.buf is consumed
 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:]
@@ -467,6 +478,17 @@ func (z *bufioDecReader) unreadn1() {
 	}
 }
 
+func (z *bufioDecReader) readn3() (bs [3]byte) {
+	z.readb(bs[:])
+	return
+}
+
+func (z *bufioDecReader) readn4() (bs [4]byte) {
+	z.readb(bs[:])
+	// copy(bs[:], z.readx(4))
+	return
+}
+
 func (z *bufioDecReader) readx(n uint) (bs []byte) {
 	if n == 0 {
 		// return
@@ -816,13 +838,39 @@ func (z *bytesDecReader) readb(bs []byte) {
 }
 
 func (z *bytesDecReader) readn1() (v uint8) {
-	if z.c == uint(len(z.b)) {
+	if z.c >= uint(len(z.b)) {
 		panic(io.EOF)
 	}
 	v = z.b[z.c]
 	z.c++
 	// z.a--
 	return
+	// return
+}
+
+func (z *bytesDecReader) readn3() (bs [3]byte) {
+	if z.c+2 >= uint(len(z.b)) {
+		panic(io.EOF)
+	}
+	// copy(bs[:], z.b[z.c:z.c+3])
+	bs[2] = z.b[z.c+2]
+	bs[1] = z.b[z.c+1]
+	bs[0] = z.b[z.c]
+	z.c += 3
+	return
+}
+
+func (z *bytesDecReader) readn4() (bs [4]byte) {
+	if z.c+3 >= uint(len(z.b)) {
+		panic(io.EOF)
+	}
+	// copy(bs[:], z.b[z.c:z.c+4])
+	bs[3] = z.b[z.c+3]
+	bs[2] = z.b[z.c+2]
+	bs[1] = z.b[z.c+1]
+	bs[0] = z.b[z.c]
+	z.c += 4
+	return
 }
 
 // func (z *bytesDecReader) readn1eof() (v uint8, eof bool) {
@@ -1164,6 +1212,32 @@ func (z *decRd) unreadn1() {
 	}
 }
 
+func (z *decRd) readn3() [3]byte {
+	if z.bytes {
+		return z.rb.readn3()
+	}
+	return z.readn3IO()
+}
+func (z *decRd) readn3IO() [3]byte {
+	if z.bufio {
+		return z.bi.readn3()
+	}
+	return z.ri.readn3()
+}
+
+func (z *decRd) readn4() [4]byte {
+	if z.bytes {
+		return z.rb.readn4()
+	}
+	return z.readn4IO()
+}
+func (z *decRd) readn4IO() [4]byte {
+	if z.bufio {
+		return z.bi.readn4()
+	}
+	return z.ri.readn4()
+}
+
 func (z *decRd) readx(n uint) []byte {
 	if z.bytes {
 		return z.rb.readx(n)
@@ -1185,7 +1259,6 @@ func (z *decRd) readb(s []byte) {
 	}
 }
 
-//go:noinline - fallback for io, ensures z.bytes path is inlined
 func (z *decRd) readbIO(s []byte) {
 	if z.bufio {
 		z.bi.readb(s)
@@ -1226,21 +1299,19 @@ func (z *decRd) readTo(accept *bitset256) (out []byte) {
 	}
 	return z.readToIO(accept)
 }
-
-//go:noinline - fallback for io, ensures z.bytes path is inlined
 func (z *decRd) readToIO(accept *bitset256) (out []byte) {
 	if z.bufio {
 		return z.bi.readTo(accept)
 	}
 	return z.ri.readTo(accept)
 }
+
 func (z *decRd) readUntil(stop byte) (out []byte) {
 	if z.bytes {
 		return z.rb.readUntil(stop)
 	}
 	return z.readUntilIO(stop)
 }
-
 func (z *decRd) readUntilIO(stop byte) (out []byte) {
 	if z.bufio {
 		return z.bi.readUntil(stop)

+ 4 - 1
codec/writer.go

@@ -312,7 +312,10 @@ func (z *bytesEncAppender) writen1(b1 byte) {
 	z.b = append(z.b, b1)
 }
 func (z *bytesEncAppender) writen2(b1, b2 byte) {
-	z.b = append(z.b, b1, b2)
+	z.b = append(z.b, b1, b2) // cost: 81
+	// z.b = append(z.b, b1, b2, b1, b2, b1, b2) // cost: 85
+	// z.b = append(z.b, []byte{b1, b2}...) // cost: 83
+	// z.b = append(append(z.b, b1), b2) // cost 82
 }
 func (z *bytesEncAppender) endErr() error {
 	*(z.out) = z.b