I was looking at Go source code and found that, in bufio.Reader.collectFragments
(a helper used by ReadSlice
and ReadString
), the a seemingly serious ErrBufferFull
is explicitly ignored, specifically:
func (b *Reader) collectFragments(delim byte) (fullBuffers [][]byte, finalFragment []byte, totalLen int, err error) {
var frag []byte
// Use ReadSlice to look for delim, accumulating full buffers.
for {
var e error
frag, e = b.ReadSlice(delim)
if e == nil { // got final fragment
break
}
if e != ErrBufferFull { // unexpected error
^^^^^^^^^^^^^^^^^^^^^ This ensures that ErrBufferFull will not break the loop
err = e
break
}
// Make a copy of the buffer.
buf := bytes.Clone(frag)
fullBuffers = append(fullBuffers, buf)
totalLen += len(buf)
}
totalLen += len(frag)
return fullBuffers, frag, totalLen, err
}
(go/src/bufio/bufio.go, line 446, Go 1.22)
If ReadSlice
returns an ErrBufferNull
, it is ignored. The full buffer is copied, and a new iteration starts, which calls ReadSlice
again.
However, ReadSlice
‘fails’ when the buffer is full, and doesn’t seem to have any logic to grow or clear a full buffer.
func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
s := 0 // search start index
for {
// Search buffer.
if i := bytes.IndexByte(b.buf[b.r+s:b.w], delim); i >= 0 {
i += s
line = b.buf[b.r : b.r+i+1]
b.r += i + 1
break
}
// Pending error?
if b.err != nil {
line = b.buf[b.r:b.w]
b.r = b.w
err = b.readErr()
break
}
// Buffer full?
if b.Buffered() >= len(b.buf) {
b.r = b.w
line = b.buf
err = ErrBufferFull
break
}
s = b.w - b.r // do not rescan area we scanned before
b.fill() // buffer is not full
}
// Handle last byte, if any.
if i := len(line) - 1; i >= 0 {
b.lastByte = int(line[i])
b.lastRuneSize = -1
}
return
}
(go/src/bufio/bufio.go, line 347, Go 1.22)
As far as I understand, this can cause the loop in collectFragments
to run forever. However, since ErrBufferFull
is explicitly ignored, I feel this is by design, and I’m missing something.
Can someone help me understand? Thanks!