summaryrefslogtreecommitdiff
path: root/vendor/github.com/ugorji/go/codec/reader.go
diff options
context:
space:
mode:
authorLibravatar kim <grufwub@gmail.com>2025-06-30 15:19:09 +0200
committerLibravatar kim <gruf@noreply.codeberg.org>2025-06-30 15:19:09 +0200
commit8b0ea560279a5bf4479555d3924c763ddeecfcad (patch)
tree005e26d4a658e565594fb259cc17948659195822 /vendor/github.com/ugorji/go/codec/reader.go
parent[chore] bumps ncruces/go-sqlite3 v0.26.1 => v0.26.3 (#4302) (diff)
downloadgotosocial-8b0ea560279a5bf4479555d3924c763ddeecfcad.tar.xz
[chore] update go dependencies (#4304)
- github.com/KimMachineGun/automemlimit v0.7.2 => v0.7.3 - github.com/gin-contrib/cors v1.7.5 => v1.7.6 - github.com/minio/minio-go/v7 v7.0.92 => v7.0.94 - github.com/spf13/cast v1.8.0 => v1.9.2 - github.com/uptrace/bun{,/*} v1.2.11 => v1.2.14 - golang.org/x/image v0.27.0 => v0.28.0 - golang.org/x/net v0.40.0 => v0.41.0 - code.superseriousbusiness.org/go-swagger v0.31.0-gts-go1.23-fix => v0.32.3-gts-go1.23-fix Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4304 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'vendor/github.com/ugorji/go/codec/reader.go')
-rw-r--r--vendor/github.com/ugorji/go/codec/reader.go985
1 files changed, 604 insertions, 381 deletions
diff --git a/vendor/github.com/ugorji/go/codec/reader.go b/vendor/github.com/ugorji/go/codec/reader.go
index ec5dac0e9..86c403ab5 100644
--- a/vendor/github.com/ugorji/go/codec/reader.go
+++ b/vendor/github.com/ugorji/go/codec/reader.go
@@ -4,22 +4,30 @@
package codec
import (
- "bufio"
- "bytes"
+ "errors"
"io"
- "strings"
+ "os"
)
// decReader abstracts the reading source, allowing implementations that can
// read from an io.Reader or directly off a byte slice with zero-copying.
-type decReader interface {
- // readx will return a view of the []byte if decoding from a []byte, OR
- // read into the implementation scratch buffer if possible i.e. n < len(scratchbuf), OR
- // create a new []byte and read into that
+type decReaderI interface {
+ // readx will return a view of the []byte in one of 2 ways:
+ // - direct view into []byte which decoding is happening from (if bytes)
+ // - view into a mutable []byte which the ioReader is using (if IO)
+ //
+ // Users should directly consume the contents read, and not store for future use.
readx(n uint) []byte
+ // skip n bytes
+ skip(n uint)
+
readb([]byte)
+ // readxb will read n bytes, returning as out, and a flag stating whether
+ // an internal buffer (not the view) was used.
+ readxb(n uint) (out []byte, usingBuf bool)
+
readn1() byte
readn2() [2]byte
readn3() [3]byte
@@ -35,14 +43,22 @@ type decReader interface {
// skip any whitespace characters, and return the first non-matching byte
skipWhitespace() (token byte)
- // jsonReadNum will include last read byte in first element of slice,
- // and continue numeric characters until it sees a non-numeric char
- // or EOF. If it sees a non-numeric character, it will unread that.
- jsonReadNum() []byte
+ // jsonReadNum will read a sequence of numeric characters, checking from the last
+ // read byte. It will return a sequence of numeric characters (v),
+ // and the next token character (tok - returned separately),
+ //
+ // if an EOF is found before the next token is seen, it returns a token value of 0.
+ jsonReadNum() (v []byte, token byte)
- // jsonReadAsisChars will read json plain characters (anything but " or \)
- // and return a slice terminated by a non-json asis character.
- jsonReadAsisChars() []byte
+ // jsonReadAsisChars recognizes 2 terminal characters (" or \).
+ // jsonReadAsisChars will read json plain characters until it reaches a terminal char,
+ // and returns a slice up to the terminal char (excluded),
+ // and also returns the terminal char separately (" or \).
+ jsonReadAsisChars() (v []byte, terminal byte)
+
+ // readUntil will read characters until it reaches a ",
+ // return a slice up to " (excluded)
+ jsonReadUntilDblQuote() (v []byte)
// skip will skip any byte that matches, and return the first non-matching byte
// skip(accept *bitset256) (token byte)
@@ -50,21 +66,28 @@ type decReader interface {
// readTo will read any byte that matches, stopping once no-longer matching.
// readTo(accept *bitset256) (out []byte)
- // readUntil will read, only stopping once it matches the 'stop' byte (which it excludes).
- readUntil(stop byte) (out []byte)
-}
+ // // readUntil will read characters until it reaches a stop char,
+ // // return a slice up to the terminal byte (excluded)
+ // readUntil(stop byte) (out []byte)
-// ------------------------------------------------
+ // only supported when reading from bytes
+ // bytesReadFrom(startpos uint) []byte
-type unreadByteStatus uint8
+ // isBytes() bool
+ resetIO(r io.Reader, bufsize int, maxInitLen int, blist *bytesFreeList)
-// unreadByteStatus goes from
-// undefined (when initialized) -- (read) --> canUnread -- (unread) --> canRead ...
-const (
- unreadByteUndefined unreadByteStatus = iota
- unreadByteCanRead
- unreadByteCanUnread
-)
+ resetBytes(in []byte)
+
+ // nextValueBytes() captures bytes read between a call to startRecording and stopRecording.
+ // startRecording will always includes the last byte read.
+ startRecording()
+ // stopRecording will include all bytes read between the point of startRecording and now.
+ stopRecording() []byte
+}
+
+// // ------------------------------------------------
+
+const maxConsecutiveEmptyReads = 16 // 2 is sufficient, 16 is enough, 64 is optimal
// const defBufReaderSize = 4096
@@ -74,276 +97,568 @@ const (
type ioReaderByteScanner interface {
io.Reader
io.ByteScanner
- // ReadByte() (byte, error)
- // UnreadByte() error
- // Read(p []byte) (n int, err error)
}
-// ioReaderByteScannerT does a simple wrapper of a io.ByteScanner
-// over a io.Reader
-type ioReaderByteScannerT struct {
+// MARKER: why not separate bufioDecReader from ioDecReader?
+//
+// We tried, but only readn1 of bufioDecReader came close to being
+// inlined (at inline cost 82). All other methods were at inline cost >= 90.
+//
+// Consequently, there's no performance impact from having both together
+// (except a single if z.bufio branch, which is likely well predicted and happens
+// only once per call (right at the top).
+
+// ioDecReader is a decReader that reads off an io.Reader.
+type ioDecReader struct {
r io.Reader
- l byte // last byte
- ls unreadByteStatus // last byte status
+ blist *bytesFreeList
- _ [2]byte // padding
- b [4]byte // tiny buffer for reading single bytes
+ maxInitLen uint
+
+ n uint // num read
+
+ bufsize uint
+
+ bufio bool // are we buffering (rc and wc are valid)
+ rbr bool // r is a byte reader
+ recording bool // are we recording (src and erc are valid)
+ done bool // did we reach EOF and are we done?
+
+ // valid when: bufio=false
+ b [1]byte // tiny buffer for reading single byte (if z.br == nil)
+ l byte // last byte read
+ br io.ByteReader // main reader used for ReadByte
+
+ // valid when: bufio=true
+ wc uint // read cursor
+ rc uint // write cursor
+ err error
+
+ // valid when: recording=true
+ recc uint // start-recording cursor (valid: recording=true)
+
+ buf []byte // buffer for bufio OR recording (if !bufio)
+}
+
+func (z *ioDecReader) resetBytes(in []byte) {
+ halt.errorStr("resetBytes unsupported by ioDecReader")
}
-func (z *ioReaderByteScannerT) ReadByte() (c byte, err error) {
- if z.ls == unreadByteCanRead {
- z.ls = unreadByteCanUnread
- c = z.l
+func (z *ioDecReader) resetIO(r io.Reader, bufsize int, maxInitLen int, blist *bytesFreeList) {
+ buf := z.buf
+ *z = ioDecReader{}
+ z.maxInitLen = max(1024, uint(maxInitLen))
+ z.blist = blist
+ z.buf = blist.check(buf, max(256, bufsize))
+ z.bufsize = uint(max(0, bufsize))
+ z.bufio = z.bufsize > 0
+ if z.bufio {
+ z.buf = z.buf[:cap(z.buf)]
} else {
- _, err = z.Read(z.b[:1])
- c = z.b[0]
+ z.buf = z.buf[:0]
}
- return
+ if r == nil {
+ z.r = &eofReader
+ } else {
+ z.r = r
+ }
+ z.br, z.rbr = z.r.(io.ByteReader)
}
-func (z *ioReaderByteScannerT) UnreadByte() (err error) {
- switch z.ls {
- case unreadByteCanUnread:
- z.ls = unreadByteCanRead
- case unreadByteCanRead:
- err = errDecUnreadByteLastByteNotRead
- case unreadByteUndefined:
- err = errDecUnreadByteNothingToRead
- default:
- err = errDecUnreadByteUnknown
- }
- return
+func (z *ioDecReader) numread() uint {
+ return z.n
}
-func (z *ioReaderByteScannerT) Read(p []byte) (n int, err error) {
- if len(p) == 0 {
- return
- }
- var firstByte bool
- if z.ls == unreadByteCanRead {
- z.ls = unreadByteCanUnread
- p[0] = z.l
- if len(p) == 1 {
- n = 1
- return
- }
- firstByte = true
- p = p[1:]
- }
- n, err = z.r.Read(p)
- if n > 0 {
- if err == io.EOF && n == len(p) {
- err = nil // read was successful, so postpone EOF (till next time)
- }
- z.l = p[n-1]
- z.ls = unreadByteCanUnread
- }
- if firstByte {
- n++
- }
- return
+func (z *ioDecReader) readn2() [2]byte {
+ return ([2]byte)(z.readx(2))
+ // using readb forced return bs onto heap, unnecessarily
+ // z.readb(bs[:])
+ // return
}
-func (z *ioReaderByteScannerT) reset(r io.Reader) {
- z.r = r
- z.ls = unreadByteUndefined
- z.l = 0
+func (z *ioDecReader) readn3() [3]byte {
+ return ([3]byte)(z.readx(3))
}
-// ioDecReader is a decReader that reads off an io.Reader.
-type ioDecReader struct {
- rr ioReaderByteScannerT // the reader passed in, wrapped into a reader+bytescanner
+func (z *ioDecReader) readn4() [4]byte {
+ return ([4]byte)(z.readx(4))
+}
- n uint // num read
+func (z *ioDecReader) readn8() [8]byte {
+ return ([8]byte)(z.readx(8))
+}
- blist *bytesFreelist
+func (z *ioDecReader) readx(n uint) (bs []byte) {
+ return bytesOK(z.readxb(n))
+}
- bufr []byte // buffer for readTo/readUntil
- br ioReaderByteScanner // main reader used for Read|ReadByte|UnreadByte
- bb *bufio.Reader // created internally, and reused on reset if needed
+func (z *ioDecReader) readErr() (err error) {
+ err, z.err = z.err, nil
+ return
+}
- x [64 + 40]byte // for: get struct field name, swallow valueTypeBytes, etc
+func (z *ioDecReader) checkErr() {
+ halt.onerror(z.readErr())
}
-func (z *ioDecReader) reset(r io.Reader, bufsize int, blist *bytesFreelist) {
- z.blist = blist
- z.n = 0
- z.bufr = z.blist.check(z.bufr, 256)
- z.br = nil
+func (z *ioDecReader) readOne() (b byte, err error) {
+ n, err := z.r.Read(z.b[:])
+ if n == 1 {
+ err = nil
+ b = z.b[0]
+ }
+ return
+}
- var ok bool
+// fillbuf reads a new chunk into the buffer.
+func (z *ioDecReader) fillbuf(bufsize uint) (numShift, numRead uint) {
+ z.checkErr()
+ bufsize = max(bufsize, z.bufsize)
- if bufsize <= 0 {
- z.br, ok = r.(ioReaderByteScanner)
- if !ok {
- z.rr.reset(r)
- z.br = &z.rr
+ // Slide existing data to beginning.
+ if z.recording {
+ numShift = z.recc // recc is always <= rc
+ } else {
+ numShift = z.rc
+ }
+ if numShift > 0 {
+ numShift-- // never shift last byte read out
+ }
+ copy(z.buf, z.buf[numShift:z.wc])
+ z.wc -= numShift
+ z.rc -= numShift
+ if z.recording {
+ z.recc -= numShift
+ }
+ // add enough to allow u to read up to bufsize again iff
+ // - buf is fully written
+ // - NOTE: don't pre-allocate more until needed
+ if uint(len(z.buf)) == z.wc {
+ if bufsize+z.wc < uint(cap(z.buf)) {
+ z.buf = z.buf[:uint(cap(z.buf))]
+ } else {
+ bufsize = max(uint(cap(z.buf)*3/2), bufsize+z.wc)
+ buf := z.blist.get(int(bufsize))
+ buf = buf[:cap(buf)]
+ copy(buf, z.buf[:z.wc])
+ z.blist.put(z.buf)
+ z.buf = buf
}
- return
}
+ // Read new data: try a limited number of times.
+ // if n == 0: try up to maxConsecutiveEmptyReads
+ // if n > 0 and err == nil: try one more time (to see if we get n == 0 and EOF)
+ for i := maxConsecutiveEmptyReads; i > 0; i-- {
+ n, err := z.r.Read(z.buf[z.wc:])
+ numRead += uint(n)
+ z.wc += uint(n)
+ if err != nil {
+ // if os read dealine, and we have read something, return
+ z.err = err
+ if err == io.EOF {
+ z.done = true
+ } else if errors.Is(err, os.ErrDeadlineExceeded) {
+ // os read deadline, but some bytes read: return (don't store err)
+ z.err = nil
+ }
+ return
+ }
- // bufsize > 0 ...
-
- // if bytes.[Buffer|Reader], no value in adding extra buffer
- // if bufio.Reader, no value in extra buffer unless size changes
- switch bb := r.(type) {
- case *strings.Reader:
- z.br = bb
- case *bytes.Buffer:
- z.br = bb
- case *bytes.Reader:
- z.br = bb
- case *bufio.Reader:
- if bb.Size() == bufsize {
- z.br = bb
+ // if z.wc == uint(len(z.buf)) {
+ // return
+ // }
+ // only read one time if results returned
+ // if n > 0 && i > 2 {
+ // i = 2 // try max one more time (to see about getting EOF)
+ // }
+
+ // Once you have some data from this read call, move on.
+ // Consequently, a blocked Read has less chance of happening.
+ if n > 0 {
+ return
}
}
+ z.err = io.ErrNoProgress // either no data read OR not enough data read, without an EOF
+ return
+}
- if z.br == nil {
- if z.bb != nil && z.bb.Size() == bufsize {
- z.bb.Reset(r)
- } else {
- z.bb = bufio.NewReaderSize(r, bufsize)
+func (z *ioDecReader) readb(bs []byte) {
+ if len(bs) == 0 {
+ return
+ }
+ var err error
+ var n int
+ if z.bufio {
+ BUFIO:
+ for z.rc == z.wc {
+ z.fillbuf(0)
}
- z.br = z.bb
+ n = copy(bs, z.buf[z.rc:z.wc])
+ z.rc += uint(n)
+ z.n += uint(n)
+ if n == len(bs) {
+ return
+ }
+ bs = bs[n:]
+ goto BUFIO
}
-}
-func (z *ioDecReader) numread() uint {
- return z.n
+ // -------- NOT BUFIO ------
+
+ var nn uint
+ bs0 := bs
+READER:
+ n, err = z.r.Read(bs)
+ if n > 0 {
+ z.l = bs[n-1]
+ nn += uint(n)
+ bs = bs[n:]
+ }
+ if len(bs) != 0 && err == nil {
+ goto READER
+ }
+ if z.recording {
+ z.buf = append(z.buf, bs0[:nn]...)
+ }
+ z.n += nn
+ if len(bs) != 0 {
+ halt.onerror(err)
+ halt.errorf("ioDecReader.readb read %d out of %d bytes requested", nn, len(bs0))
+ }
+ return
}
func (z *ioDecReader) readn1() (b uint8) {
- b, err := z.br.ReadByte()
+ if z.bufio {
+ for z.rc == z.wc {
+ z.fillbuf(0)
+ }
+ b = z.buf[z.rc]
+ z.rc++
+ z.n++
+ return
+ }
+
+ // -------- NOT BUFIO ------
+
+ var err error
+ if z.rbr {
+ b, err = z.br.ReadByte()
+ } else {
+ b, err = z.readOne()
+ }
halt.onerror(err)
+ z.l = b
z.n++
+ if z.recording {
+ z.buf = append(z.buf, b)
+ }
return
}
-func (z *ioDecReader) readn2() (bs [2]byte) {
- z.readb(bs[:])
- return
-}
+func (z *ioDecReader) readxb(n uint) (out []byte, useBuf bool) {
+ if n == 0 {
+ return zeroByteSlice, false
+ }
-func (z *ioDecReader) readn3() (bs [3]byte) {
- z.readb(bs[:])
- return
-}
+ if z.bufio {
+ BUFIO:
+ nn := int(n+z.rc) - int(z.wc)
+ if nn > 0 {
+ z.fillbuf(decInferLen(nn, z.maxInitLen, 1))
+ goto BUFIO
+ }
+ pos := z.rc
+ z.rc += uint(n)
+ z.n += uint(n)
+ out = z.buf[pos:z.rc]
+ useBuf = true
+ return
+ }
-func (z *ioDecReader) readn4() (bs [4]byte) {
- z.readb(bs[:])
+ // -------- NOT BUFIO ------
+
+ useBuf = true
+ out = z.buf
+ r0 := uint(len(out))
+ r := r0
+ nn := int(n)
+ var n2 uint
+ for nn > 0 {
+ n2 = r + decInferLen(int(nn), z.maxInitLen, 1)
+ if cap(out) < int(n2) {
+ out2 := z.blist.putGet(out, int(n2))[:n2] // make([]byte, len2+len3)
+ copy(out2, out)
+ out = out2
+ } else {
+ out = out[:n2]
+ }
+ n3, err := z.r.Read(out[r:n2])
+ if n3 > 0 {
+ z.l = out[r+uint(n3)-1]
+ nn -= n3
+ r += uint(n3)
+ }
+ halt.onerror(err)
+ }
+ z.buf = out[:r0+n]
+ out = out[r0 : r0+n]
+ z.n += n
return
}
-func (z *ioDecReader) readn8() (bs [8]byte) {
- z.readb(bs[:])
+func (z *ioDecReader) skip(n uint) {
+ if n == 0 {
+ return
+ }
+
+ if z.bufio {
+ BUFIO:
+ n2 := min(n, z.wc-z.rc)
+ // handle in-line, so z.buf doesn't grow much (since we're skipping)
+ // ie by setting z.rc, fillbuf should keep shifting left (unless recording)
+ z.rc += n2
+ z.n += n2
+ n -= n2
+ if n > 0 {
+ z.fillbuf(decInferLen(int(n+z.rc)-int(z.wc), z.maxInitLen, 1))
+ goto BUFIO
+ }
+ return
+ }
+
+ // -------- NOT BUFIO ------
+
+ var out []byte
+ var fromBlist bool
+ if z.recording {
+ out = z.buf
+ } else {
+ nn := int(decInferLen(int(n), z.maxInitLen, 1))
+ if cap(z.buf) >= nn/2 {
+ out = z.buf[:cap(z.buf)]
+ } else {
+ fromBlist = true
+ out = z.blist.get(nn)
+ }
+ }
+
+ var r, n2 uint
+ nn := int(n)
+ for nn > 0 {
+ n2 = uint(nn)
+ if z.recording {
+ r = uint(len(out))
+ n2 = r + decInferLen(int(nn), z.maxInitLen, 1)
+ if cap(out) < int(n2) {
+ out2 := z.blist.putGet(out, int(n2))[:n2] // make([]byte, len2+len3)
+ copy(out2, out)
+ out = out2
+ } else {
+ out = out[:n2]
+ }
+ }
+ n3, err := z.r.Read(out[r:n2])
+ if n3 > 0 {
+ z.l = out[r+uint(n3)-1]
+ z.n += uint(n3)
+ nn -= n3
+ }
+ halt.onerror(err)
+ }
+ if z.recording {
+ z.buf = out
+ } else if fromBlist {
+ z.blist.put(out)
+ }
return
}
-func (z *ioDecReader) readx(n uint) (bs []byte) {
- if n == 0 {
- return zeroByteSlice
+// ---- JSON SPECIFIC HELPERS HERE ----
+
+func (z *ioDecReader) jsonReadNum() (bs []byte, token byte) {
+ var start, pos, end uint
+ if z.bufio {
+ // read and fill into buf, then take substring
+ start = z.rc - 1 // include last byte read
+ pos = start
+ BUFIO:
+ if pos == z.wc {
+ if z.done {
+ end = pos
+ goto END
+ }
+ numshift, numread := z.fillbuf(0)
+ start -= numshift
+ pos -= numshift
+ if numread == 0 {
+ end = pos
+ goto END
+ }
+ }
+ token = z.buf[pos]
+ pos++
+ if isNumberChar(token) {
+ goto BUFIO
+ }
+ end = pos - 1
+ END:
+ z.n += (pos - z.rc)
+ z.rc = pos
+ return z.buf[start:end], token
}
- if n < uint(len(z.x)) {
- bs = z.x[:n]
+
+ // if not recording, add the last read byte into buf
+ if !z.recording {
+ z.buf = append(z.buf[:0], z.l)
+ }
+ start = uint(len(z.buf) - 1) // incl last byte in z.buf
+ var b byte
+ var err error
+
+READER:
+ if z.rbr {
+ b, err = z.br.ReadByte()
} else {
- bs = make([]byte, n)
+ b, err = z.readOne()
+ }
+ if err == io.EOF {
+ return z.buf[start:], 0
}
- nn, err := readFull(z.br, bs)
- z.n += nn
halt.onerror(err)
- return
+ z.l = b
+ z.n++
+ z.buf = append(z.buf, b)
+ if isNumberChar(b) {
+ goto READER
+ }
+ return z.buf[start : len(z.buf)-1], b
}
-func (z *ioDecReader) readb(bs []byte) {
- if len(bs) == 0 {
- return
+func (z *ioDecReader) skipWhitespace() (tok byte) {
+ var pos uint
+ if z.bufio {
+ pos = z.rc
+ BUFIO:
+ if pos == z.wc {
+ if z.done {
+ halt.onerror(io.ErrUnexpectedEOF)
+ }
+ numshift, numread := z.fillbuf(0)
+ pos -= numshift
+ if numread == 0 {
+ halt.onerror(io.ErrUnexpectedEOF)
+ }
+ }
+ tok = z.buf[pos]
+ pos++
+ if isWhitespaceChar(tok) {
+ goto BUFIO
+ }
+ z.n += (pos - z.rc)
+ z.rc = pos
+ return tok
+ }
+
+ var err error
+READER:
+ if z.rbr {
+ tok, err = z.br.ReadByte()
+ } else {
+ tok, err = z.readOne()
}
- nn, err := readFull(z.br, bs)
- z.n += nn
halt.onerror(err)
-}
+ z.n++
+ z.l = tok
+ if z.recording {
+ z.buf = append(z.buf, tok)
+ }
+ if isWhitespaceChar(tok) {
+ goto READER
+ }
+ return tok
+}
+
+func (z *ioDecReader) readUntil(stop1, stop2 byte) (bs []byte, tok byte) {
+ var start, pos uint
+ if z.bufio {
+ start = z.rc
+ pos = start
+ BUFIO:
+ if pos == z.wc {
+ if z.done {
+ halt.onerror(io.ErrUnexpectedEOF)
+ }
+ numshift, numread := z.fillbuf(0)
+ start -= numshift
+ pos -= numshift
+ if numread == 0 {
+ halt.onerror(io.ErrUnexpectedEOF)
+ }
+ }
+ tok = z.buf[pos]
+ pos++
+ if tok == stop1 || tok == stop2 {
+ z.n += (pos - z.rc)
+ z.rc = pos
+ return z.buf[start : pos-1], tok
+ }
+ goto BUFIO
+ }
-// func (z *ioDecReader) readn1eof() (b uint8, eof bool) {
-// b, err := z.br.ReadByte()
-// if err == nil {
-// z.n++
-// } else if err == io.EOF {
-// eof = true
-// } else {
-// halt.onerror(err)
-// }
-// return
-// }
-
-func (z *ioDecReader) jsonReadNum() (bs []byte) {
- z.unreadn1()
- z.bufr = z.bufr[:0]
-LOOP:
- // i, eof := z.readn1eof()
- i, err := z.br.ReadByte()
- if err == io.EOF {
- return z.bufr
+ var err error
+ if !z.recording {
+ z.buf = z.buf[:0]
}
- if err != nil {
- halt.onerror(err)
+ start = uint(len(z.buf))
+READER:
+ if z.rbr {
+ tok, err = z.br.ReadByte()
+ } else {
+ tok, err = z.readOne()
}
+ halt.onerror(err)
z.n++
- if isNumberChar(i) {
- z.bufr = append(z.bufr, i)
- goto LOOP
+ z.l = tok
+ z.buf = append(z.buf, tok)
+ if tok == stop1 || tok == stop2 {
+ return z.buf[start : len(z.buf)-1], tok
}
- z.unreadn1()
- return z.bufr
+ goto READER
}
-func (z *ioDecReader) jsonReadAsisChars() (bs []byte) {
- z.bufr = z.bufr[:0]
-LOOP:
- i := z.readn1()
- z.bufr = append(z.bufr, i)
- if i == '"' || i == '\\' {
- return z.bufr
- }
- goto LOOP
+func (z *ioDecReader) jsonReadAsisChars() (bs []byte, tok byte) {
+ return z.readUntil('"', '\\')
}
-func (z *ioDecReader) skipWhitespace() (token byte) {
-LOOP:
- token = z.readn1()
- if isWhitespaceChar(token) {
- goto LOOP
- }
+func (z *ioDecReader) jsonReadUntilDblQuote() (bs []byte) {
+ bs, _ = z.readUntil('"', 0)
return
}
-// func (z *ioDecReader) readUntil(stop byte) []byte {
-// z.bufr = z.bufr[:0]
-// LOOP:
-// token := z.readn1()
-// z.bufr = append(z.bufr, token)
-// if token == stop {
-// return z.bufr[:len(z.bufr)-1]
-// }
-// goto LOOP
-// }
+// ---- start/stop recording ----
-func (z *ioDecReader) readUntil(stop byte) []byte {
- z.bufr = z.bufr[:0]
-LOOP:
- token := z.readn1()
- if token == stop {
- return z.bufr
+func (z *ioDecReader) startRecording() {
+ z.recording = true
+ // always include last byte read
+ if z.bufio {
+ z.recc = z.rc - 1
+ } else {
+ z.buf = append(z.buf[:0], z.l)
}
- z.bufr = append(z.bufr, token)
- goto LOOP
}
-func (z *ioDecReader) unreadn1() {
- err := z.br.UnreadByte()
- halt.onerror(err)
- z.n--
+func (z *ioDecReader) stopRecording() (v []byte) {
+ z.recording = false
+ if z.bufio {
+ v = z.buf[z.recc:z.rc]
+ z.recc = 0
+ } else {
+ v = z.buf
+ z.buf = z.buf[:0]
+ }
+ return
}
// ------------------------------------
@@ -359,11 +674,18 @@ func (z *ioDecReader) unreadn1() {
//
// see panicValToErr(...) function in helper.go.
type bytesDecReader struct {
- b []byte // data
- c uint // cursor
+ b []byte // data
+ c uint // cursor
+ r uint // recording cursor
+ xb []byte // buffer for readxb
}
-func (z *bytesDecReader) reset(in []byte) {
+func (z *bytesDecReader) resetIO(r io.Reader, bufsize int, maxInitLen int, blist *bytesFreeList) {
+ halt.errorStr("resetIO unsupported by bytesDecReader")
+}
+
+func (z *bytesDecReader) resetBytes(in []byte) {
+ // it's ok to resize a nil slice, so long as it's not past 0
z.b = in[:len(in):len(in)] // reslicing must not go past capacity
z.c = 0
}
@@ -377,40 +699,25 @@ func (z *bytesDecReader) numread() uint {
// However, we do it only once, and it's better than reslicing both z.b and return value.
func (z *bytesDecReader) readx(n uint) (bs []byte) {
- // x := z.c + n
- // bs = z.b[z.c:x]
- // z.c = x
bs = z.b[z.c : z.c+n]
z.c += n
return
}
-func (z *bytesDecReader) readb(bs []byte) {
- copy(bs, z.readx(uint(len(bs))))
+func (z *bytesDecReader) skip(n uint) {
+ if z.c+n > uint(cap(z.b)) {
+ halt.error(&outOfBoundsError{uint(cap(z.b)), z.c + n})
+ }
+ z.c += n
}
-// MARKER: do not use this - as it calls into memmove (as the size of data to move is unknown)
-// func (z *bytesDecReader) readnn(bs []byte, n uint) {
-// x := z.c
-// copy(bs, z.b[x:x+n])
-// z.c += n
-// }
-
-// func (z *bytesDecReader) readn(num uint8) (bs [8]byte) {
-// x := z.c + uint(num)
-// copy(bs[:], z.b[z.c:x]) // slice z.b completely, so we get bounds error if past
-// z.c = x
-// return
-// }
-
-// func (z *bytesDecReader) readn1() uint8 {
-// z.c++
-// return z.b[z.c-1]
-// }
+func (z *bytesDecReader) readxb(n uint) (out []byte, usingBuf bool) {
+ return z.readx(n), false
+}
-// MARKER: readn{1,2,3,4,8} should throw an out of bounds error if past length.
-// MARKER: readn1: explicitly ensure bounds check is done
-// MARKER: readn{2,3,4,8}: ensure you slice z.b completely so we get bounds error if past end.
+func (z *bytesDecReader) readb(bs []byte) {
+ copy(bs, z.readx(uint(len(bs))))
+}
func (z *bytesDecReader) readn1() (v uint8) {
v = z.b[z.c]
@@ -419,59 +726,58 @@ func (z *bytesDecReader) readn1() (v uint8) {
}
func (z *bytesDecReader) readn2() (bs [2]byte) {
- // copy(bs[:], z.b[z.c:z.c+2])
- // bs[1] = z.b[z.c+1]
- // bs[0] = z.b[z.c]
- bs = okBytes2(z.b[z.c : z.c+2])
+ bs = [2]byte(z.b[z.c:])
z.c += 2
return
}
func (z *bytesDecReader) readn3() (bs [3]byte) {
- // copy(bs[1:], z.b[z.c:z.c+3])
- bs = okBytes3(z.b[z.c : z.c+3])
+ bs = [3]byte(z.b[z.c:])
z.c += 3
return
}
func (z *bytesDecReader) readn4() (bs [4]byte) {
- // copy(bs[:], z.b[z.c:z.c+4])
- bs = okBytes4(z.b[z.c : z.c+4])
+ bs = [4]byte(z.b[z.c:])
z.c += 4
return
}
func (z *bytesDecReader) readn8() (bs [8]byte) {
- // copy(bs[:], z.b[z.c:z.c+8])
- bs = okBytes8(z.b[z.c : z.c+8])
+ bs = [8]byte(z.b[z.c:])
z.c += 8
return
}
-func (z *bytesDecReader) jsonReadNum() []byte {
- z.c-- // unread
- i := z.c
+func (z *bytesDecReader) jsonReadNum() (bs []byte, token byte) {
+ start := z.c - 1 // include last byte
+ i := start
LOOP:
- // gracefully handle end of slice, as end of stream is meaningful here
- if i < uint(len(z.b)) && isNumberChar(z.b[i]) {
- i++
- goto LOOP
+ // gracefully handle end of slice (~= EOF)
+ if i < uint(len(z.b)) {
+ if isNumberChar(z.b[i]) {
+ i++
+ goto LOOP
+ }
+ token = z.b[i]
}
- z.c, i = i, z.c
- // MARKER: 20230103: byteSliceOf here prevents inlining of jsonReadNum
- // return byteSliceOf(z.b, i, z.c)
- return z.b[i:z.c]
+ z.c = i + 1
+ bs = z.b[start:i] // byteSliceOf(z.b, start, i)
+ return
}
-func (z *bytesDecReader) jsonReadAsisChars() []byte {
+func (z *bytesDecReader) jsonReadAsisChars() (bs []byte, token byte) {
i := z.c
LOOP:
- token := z.b[i]
+ token = z.b[i]
i++
if token == '"' || token == '\\' {
- z.c, i = i, z.c
- return byteSliceOf(z.b, i, z.c)
- // return z.b[i:z.c]
+ // z.c, i = i, z.c
+ // return byteSliceOf(z.b, i, z.c-1), token
+ bs = z.b[z.c : i-1]
+ z.c = i
+ return
+ // return z.b[i : z.c-1], token
}
goto LOOP
}
@@ -479,21 +785,22 @@ LOOP:
func (z *bytesDecReader) skipWhitespace() (token byte) {
i := z.c
LOOP:
+ // setting token before check reduces inlining cost,
+ // making containerNext inlineable
token = z.b[i]
- if isWhitespaceChar(token) {
- i++
- goto LOOP
+ if !isWhitespaceChar(token) {
+ z.c = i + 1
+ return
}
- z.c = i + 1
- return
+ i++
+ goto LOOP
}
-func (z *bytesDecReader) readUntil(stop byte) (out []byte) {
+func (z *bytesDecReader) jsonReadUntilDblQuote() (out []byte) {
i := z.c
LOOP:
- if z.b[i] == stop {
- out = byteSliceOf(z.b, z.c, i)
- // out = z.b[z.c:i]
+ if z.b[i] == '"' {
+ out = z.b[z.c:i] // byteSliceOf(z.b, z.c, i)
z.c = i + 1
return
}
@@ -501,107 +808,23 @@ LOOP:
goto LOOP
}
-// --------------
-
-type decRd struct {
- rb bytesDecReader
- ri *ioDecReader
-
- decReader
-
- bytes bool // is bytes reader
-
- // MARKER: these fields below should belong directly in Encoder.
- // we pack them here for space efficiency and cache-line optimization.
-
- mtr bool // is maptype a known type?
- str bool // is slicetype a known type?
-
- be bool // is binary encoding
- js bool // is json handle
- jsms bool // is json handle, and MapKeyAsString
- cbor bool // is cbor handle
-
- cbreak bool // is a check breaker
-
+func (z *bytesDecReader) startRecording() {
+ z.r = z.c - 1
}
-// From out benchmarking, we see the following impact performance:
-//
-// - functions that are too big to inline
-// - interface calls (as no inlining can occur)
-//
-// decRd is designed to embed a decReader, and then re-implement some of the decReader
-// methods using a conditional branch.
-//
-// We only override the ones where the bytes version is inlined AND the wrapper method
-// (containing the bytes version alongside a conditional branch) is also inlined.
-//
-// We use ./run.sh -z to check.
-//
-// Right now, only numread and "carefully crafted" readn1 can be inlined.
-
-func (z *decRd) numread() uint {
- if z.bytes {
- return z.rb.numread()
- }
- return z.ri.numread()
-}
-
-func (z *decRd) readn1() (v uint8) {
- if z.bytes {
- // return z.rb.readn1()
- // MARKER: calling z.rb.readn1() prevents decRd.readn1 from being inlined.
- // copy code, to manually inline and explicitly return here.
- // Keep in sync with bytesDecReader.readn1
- v = z.rb.b[z.rb.c]
- z.rb.c++
- return
- }
- return z.ri.readn1()
+func (z *bytesDecReader) stopRecording() (v []byte) {
+ v = z.b[z.r:z.c]
+ z.r = 0
+ return
}
-// func (z *decRd) readn4() [4]byte {
-// if z.bytes {
-// return z.rb.readn4()
-// }
-// return z.ri.readn4()
-// }
-
-// func (z *decRd) readn3() [3]byte {
-// if z.bytes {
-// return z.rb.readn3()
-// }
-// return z.ri.readn3()
-// }
-
-// func (z *decRd) skipWhitespace() byte {
-// if z.bytes {
-// return z.rb.skipWhitespace()
-// }
-// return z.ri.skipWhitespace()
-// }
-
type devNullReader struct{}
func (devNullReader) Read(p []byte) (int, error) { return 0, io.EOF }
func (devNullReader) Close() error { return nil }
+func (devNullReader) ReadByte() (byte, error) { return 0, io.EOF }
+func (devNullReader) UnreadByte() error { return io.EOF }
-func readFull(r io.Reader, bs []byte) (n uint, err error) {
- var nn int
- 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 += uint(nn)
- }
- }
- // do not do this below - it serves no purpose
- // if n != len(bs) && err == io.EOF { err = io.ErrUnexpectedEOF }
- return
-}
-
-var _ decReader = (*decRd)(nil)
+// MARKER: readn{1,2,3,4,8} should throw an out of bounds error if past length.
+// MARKER: readn1: explicitly ensure bounds check is done
+// MARKER: readn{2,3,4,8}: ensure you slice z.b completely so we get bounds error if past end.