Browse Source

codec: add writeqstr implementation to efficiently write a quoted string in json

Ugorji Nwoke 6 years ago
parent
commit
92a3d5189e
1 changed files with 81 additions and 24 deletions
  1. 81 24
      codec/encode.go

+ 81 - 24
codec/encode.go

@@ -30,6 +30,7 @@ var errEncoderNotInitialized = errors.New("Encoder not initialized")
 type encWriter interface {
 	writeb([]byte)
 	writestr(string)
+	writeqstr(string) // write string wrapped in quotes ie "..."
 	writen1(byte)
 	writen2(byte, byte)
 	end()
@@ -250,6 +251,10 @@ func (z *ioEncWriter) writestr(s string) {
 	}
 }
 
+func (z *ioEncWriter) writeqstr(s string) {
+	writestr("\"" + s + "\"")
+}
+
 func (z *ioEncWriter) writen1(b byte) {
 	if err := z.bw.WriteByte(b); err != nil {
 		panic(err)
@@ -288,21 +293,27 @@ func (z *ioEncWriter) end() {
 
 // bufioEncWriter
 type bufioEncWriter struct {
+	w io.Writer
+
 	buf []byte
-	w   io.Writer
-	n   int
-	sz  int // buf size
+
+	n int
 
 	// Extensions can call Encode() within a current Encode() call.
 	// We need to know when the top level Encode() call returns,
 	// so we can decide whether to Release() or not.
 	calls uint16 // what depth in mustDecode are we in now.
 
-	_ [6]uint8 // padding
+	sz int // buf size
+	// _ uint64 // padding (cache-aligned)
+
+	// ---- cache line
 
+	// write-most fields below
+
+	// less used fields
 	bytesBufPooler
 
-	_ [1]uint64 // padding
 	// a int
 	// b   [4]byte
 	// err
@@ -373,6 +384,29 @@ LOOP:
 	z.n += copy(z.buf[z.n:], s)
 }
 
+func (z *bufioEncWriter) writeqstr(s string) {
+	// z.writen1('"')
+	// z.writestr(s)
+	// z.writen1('"')
+
+	if z.n+len(s)+2 > len(z.buf) {
+		z.flush()
+	}
+	z.buf[z.n] = '"'
+	z.n++
+LOOP:
+	a := len(z.buf) - z.n
+	if len(s)+1 > a {
+		z.n += copy(z.buf[z.n:], s[:a])
+		s = s[a:]
+		z.flush()
+		goto LOOP
+	}
+	z.n += copy(z.buf[z.n:], s)
+	z.buf[z.n] = '"'
+	z.n++
+}
+
 func (z *bufioEncWriter) writen1(b1 byte) {
 	if 1 > len(z.buf)-z.n {
 		z.flush()
@@ -411,6 +445,15 @@ func (z *bytesEncAppender) writeb(s []byte) {
 func (z *bytesEncAppender) writestr(s string) {
 	z.b = append(z.b, s...)
 }
+func (z *bytesEncAppender) writeqstr(s string) {
+	// z.writen1('"')
+	// z.writestr(s)
+	// z.writen1('"')
+	z.b = append(append(append(z.b, '"'), s...), '"')
+	// z.b = append(z.b, '"')
+	// z.b = append(z.b, s...)
+	// z.b = append(z.b, '"')
+}
 func (z *bytesEncAppender) writen1(b1 byte) {
 	z.b = append(z.b, b1)
 }
@@ -1071,12 +1114,12 @@ type encWriterSwitch struct {
 	wb bytesEncAppender
 	wf *bufioEncWriter
 	// typ  entryType
-	bytes bool    // encoding to []byte
-	esep  bool    // whether it has elem separators
-	isas  bool    // whether e.as != nil
-	js    bool    // is json encoder?
-	be    bool    // is binary encoder?
-	_     [2]byte // padding
+	bytes bool // encoding to []byte
+	esep  bool // whether it has elem separators
+	isas  bool // whether e.as != nil
+	js    bool // is json encoder?
+	be    bool // is binary encoder?
+	// _     [2]byte // padding
 	// _    [2]uint64 // padding
 	// _    uint64    // padding
 }
@@ -1088,6 +1131,13 @@ func (z *encWriterSwitch) writeb(s []byte) {
 		z.wf.writeb(s)
 	}
 }
+func (z *encWriterSwitch) writeqstr(s string) {
+	if z.bytes {
+		z.wb.writeqstr(s)
+	} else {
+		z.wf.writeqstr(s)
+	}
+}
 func (z *encWriterSwitch) writestr(s string) {
 	if z.bytes {
 		z.wb.writestr(s)
@@ -1235,18 +1285,19 @@ type Encoder struct {
 	// bw *bufio.Writer
 	as encDriverAsis
 
-	err error
-
 	h  *BasicHandle
 	hh Handle
+
 	// ---- cpu cache line boundary? + 3
 	encWriterSwitch
 
-	ci set
-
-	b [(5 * 8)]byte // for encoding chan or (non-addressable) [N]byte
+	err error
 
+	// ---- cpu cache line boundary
 	// ---- writable fields during execution --- *try* to keep in sep cache line
+	ci set
+
+	b [(5 * 8)]byte // for encoding chan byte, (non-addressable) [N]byte, etc
 
 	// ---- cpu cache line boundary?
 	// b [scratchByteArrayLen]byte
@@ -1456,10 +1507,14 @@ func (e *Encoder) Encode(v interface{}) (err error) {
 			err = e.w.endErr()
 			x := recover()
 			if x == nil {
-				e.err = err
+				if e.err != err {
+					e.err = err
+				}
 			} else {
 				panicValToErr(e, x, &e.err)
-				err = e.err
+				if e.err != err {
+					err = e.err
+				}
 			}
 		}()
 	}
@@ -1777,18 +1832,20 @@ func encStructFieldKey(encName string, ee encDriver, w *encWriterSwitch,
 	// since keyType is typically valueTypeString, branch prediction is pretty good.
 	if keyType == valueTypeString {
 		if js && encNameAsciiAlphaNum { // keyType == valueTypeString
+			w.writeqstr(encName)
+			// ----
 			// w.writen1('"')
 			// w.writestr(encName)
 			// w.writen1('"')
 			// ----
 			// w.writestr(`"` + encName + `"`)
 			// ----
-			// do concat myself, so it is faster than the generic string concat
-			b := make([]byte, len(encName)+2)
-			copy(b[1:], encName)
-			b[0] = '"'
-			b[len(b)-1] = '"'
-			w.writeb(b)
+			// // do concat myself, so it is faster than the generic string concat
+			// b := make([]byte, len(encName)+2)
+			// copy(b[1:], encName)
+			// b[0] = '"'
+			// b[len(b)-1] = '"'
+			// w.writeb(b)
 		} else { // keyType == valueTypeString
 			ee.EncodeStringEnc(cUTF8, encName)
 		}