240 lines
5.0 KiB
Go
240 lines
5.0 KiB
Go
// Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved.
|
|
// Use of this source code is governed by a MIT license found in the LICENSE file.
|
|
|
|
package codec
|
|
|
|
import (
|
|
"io"
|
|
)
|
|
|
|
const maxConsecutiveEmptyWrites = 16 // 2 is sufficient, 16 is enough, 64 is optimal
|
|
|
|
// encWriter abstracts writing to a byte array or to an io.Writer.
|
|
type encWriterI interface {
|
|
writeb([]byte)
|
|
writestr(string)
|
|
writeqstr(string) // write string wrapped in quotes ie "..."
|
|
writen1(byte)
|
|
|
|
// add convenience functions for writing 2,4
|
|
writen2(byte, byte)
|
|
writen4([4]byte)
|
|
writen8([8]byte)
|
|
|
|
// isBytes() bool
|
|
end()
|
|
|
|
resetIO(w io.Writer, bufsize int, blist *bytesFreeList)
|
|
resetBytes(in []byte, out *[]byte)
|
|
}
|
|
|
|
// ---------------------------------------------
|
|
|
|
type bufioEncWriter struct {
|
|
w io.Writer
|
|
|
|
buf []byte
|
|
|
|
n int
|
|
|
|
b [16]byte // scratch buffer and padding (cache-aligned)
|
|
}
|
|
|
|
// MARKER: use setByteAt/byteAt to elide the bounds-checks
|
|
// when we are sure that we don't go beyond the bounds.
|
|
|
|
func (z *bufioEncWriter) resetBytes(in []byte, out *[]byte) {
|
|
halt.errorStr("resetBytes is unsupported by bufioEncWriter")
|
|
}
|
|
|
|
func (z *bufioEncWriter) resetIO(w io.Writer, bufsize int, blist *bytesFreeList) {
|
|
z.w = w
|
|
z.n = 0
|
|
// use minimum bufsize of 16, matching the array z.b and accomodating writen methods (where n <= 8)
|
|
bufsize = max(16, bufsize) // max(byteBufSize, bufsize)
|
|
if cap(z.buf) < bufsize {
|
|
if len(z.buf) > 0 && &z.buf[0] != &z.b[0] {
|
|
blist.put(z.buf)
|
|
}
|
|
if len(z.b) > bufsize {
|
|
z.buf = z.b[:]
|
|
} else {
|
|
z.buf = blist.get(bufsize)
|
|
}
|
|
}
|
|
z.buf = z.buf[:cap(z.buf)]
|
|
}
|
|
|
|
func (z *bufioEncWriter) flushErr() (err error) {
|
|
var n int
|
|
for i := maxConsecutiveEmptyReads; i > 0; i-- {
|
|
n, err = z.w.Write(z.buf[:z.n])
|
|
z.n -= n
|
|
if z.n == 0 || err != nil {
|
|
return
|
|
}
|
|
// at this point: z.n > 0 && err == nil
|
|
if n > 0 {
|
|
copy(z.buf, z.buf[n:z.n+n])
|
|
}
|
|
}
|
|
return io.ErrShortWrite // OR io.ErrNoProgress: not enough (or no) data written
|
|
}
|
|
|
|
func (z *bufioEncWriter) flush() {
|
|
halt.onerror(z.flushErr())
|
|
}
|
|
|
|
func (z *bufioEncWriter) writeb(s []byte) {
|
|
LOOP:
|
|
a := len(z.buf) - z.n
|
|
if len(s) > 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)
|
|
}
|
|
|
|
func (z *bufioEncWriter) writestr(s string) {
|
|
// z.writeb(bytesView(s)) // inlined below
|
|
LOOP:
|
|
a := len(z.buf) - z.n
|
|
if len(s) > 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)
|
|
}
|
|
|
|
func (z *bufioEncWriter) writeqstr(s string) {
|
|
// z.writen1('"')
|
|
// z.writestr(s)
|
|
// z.writen1('"')
|
|
|
|
if z.n+len(s)+2 > len(z.buf) {
|
|
z.flush()
|
|
}
|
|
setByteAt(z.buf, uint(z.n), '"')
|
|
// 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)
|
|
setByteAt(z.buf, uint(z.n), '"')
|
|
// z.buf[z.n] = '"'
|
|
z.n++
|
|
}
|
|
|
|
func (z *bufioEncWriter) writen1(b1 byte) {
|
|
if 1 > len(z.buf)-z.n {
|
|
z.flush()
|
|
}
|
|
setByteAt(z.buf, uint(z.n), b1)
|
|
// z.buf[z.n] = b1
|
|
z.n++
|
|
}
|
|
|
|
func (z *bufioEncWriter) writen2(b1, b2 byte) {
|
|
if 2 > len(z.buf)-z.n {
|
|
z.flush()
|
|
}
|
|
setByteAt(z.buf, uint(z.n+1), b2)
|
|
setByteAt(z.buf, uint(z.n), b1)
|
|
// z.buf[z.n+1] = b2
|
|
// z.buf[z.n] = b1
|
|
z.n += 2
|
|
}
|
|
|
|
func (z *bufioEncWriter) writen4(b [4]byte) {
|
|
if 4 > len(z.buf)-z.n {
|
|
z.flush()
|
|
}
|
|
// setByteAt(z.buf, uint(z.n+3), b4)
|
|
// setByteAt(z.buf, uint(z.n+2), b3)
|
|
// setByteAt(z.buf, uint(z.n+1), b2)
|
|
// setByteAt(z.buf, uint(z.n), b1)
|
|
copy(z.buf[z.n:], b[:])
|
|
z.n += 4
|
|
}
|
|
|
|
func (z *bufioEncWriter) writen8(b [8]byte) {
|
|
if 8 > len(z.buf)-z.n {
|
|
z.flush()
|
|
}
|
|
copy(z.buf[z.n:], b[:])
|
|
z.n += 8
|
|
}
|
|
|
|
func (z *bufioEncWriter) endErr() (err error) {
|
|
if z.n > 0 {
|
|
err = z.flushErr()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (z *bufioEncWriter) end() {
|
|
halt.onerror(z.endErr())
|
|
}
|
|
|
|
// ---------------------------------------------
|
|
|
|
var bytesEncAppenderDefOut = []byte{}
|
|
|
|
// bytesEncAppender implements encWriter and can write to an byte slice.
|
|
type bytesEncAppender struct {
|
|
b []byte
|
|
out *[]byte
|
|
}
|
|
|
|
func (z *bytesEncAppender) writeb(s []byte) {
|
|
z.b = append(z.b, s...)
|
|
}
|
|
func (z *bytesEncAppender) writestr(s string) {
|
|
z.b = append(z.b, s...)
|
|
}
|
|
func (z *bytesEncAppender) writeqstr(s string) {
|
|
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)
|
|
}
|
|
func (z *bytesEncAppender) writen2(b1, b2 byte) {
|
|
z.b = append(z.b, b1, b2)
|
|
}
|
|
|
|
func (z *bytesEncAppender) writen4(b [4]byte) {
|
|
z.b = append(z.b, b[:]...)
|
|
// z.b = append(z.b, b1, b2, b3, b4) // prevents inlining encWr.writen4
|
|
}
|
|
|
|
func (z *bytesEncAppender) writen8(b [8]byte) {
|
|
z.b = append(z.b, b[:]...)
|
|
// z.b = append(z.b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7])
|
|
}
|
|
|
|
func (z *bytesEncAppender) end() {
|
|
*(z.out) = z.b
|
|
}
|
|
|
|
func (z *bytesEncAppender) resetBytes(in []byte, out *[]byte) {
|
|
z.b = in[:0]
|
|
z.out = out
|
|
}
|
|
|
|
func (z *bytesEncAppender) resetIO(w io.Writer, bufsize int, blist *bytesFreeList) {
|
|
halt.errorStr("resetIO is unsupported by bytesEncAppender")
|
|
}
|