diff options
| author | 2025-06-30 15:19:09 +0200 | |
|---|---|---|
| committer | 2025-06-30 15:19:09 +0200 | |
| commit | 8b0ea560279a5bf4479555d3924c763ddeecfcad (patch) | |
| tree | 005e26d4a658e565594fb259cc17948659195822 /vendor/github.com/ugorji/go/codec/reader.go | |
| parent | [chore] bumps ncruces/go-sqlite3 v0.26.1 => v0.26.3 (#4302) (diff) | |
| download | gotosocial-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.go | 985 |
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. |
