Skip to content

Commit 7c14ffe

Browse files
Add fetchBytes
1 parent ef6268e commit 7c14ffe

File tree

1 file changed

+36
-57
lines changed

1 file changed

+36
-57
lines changed

bson/value_reader.go

Lines changed: 36 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -259,26 +259,7 @@ func (vr *valueReader) readBytes(length int32) ([]byte, error) {
259259
return nil, fmt.Errorf("invalid length: %d", length)
260260
}
261261

262-
// If we can peek and discard the bytes, we can avoid an allocation.
263-
if buf, err := vr.src.peek(int(length)); err == nil {
264-
_, _ = vr.src.discard(int(length)) // Discard the bytes from the source.
265-
return buf, nil
266-
}
267-
268-
data := make([]byte, length)
269-
reader, ok := vr.src.(io.Reader)
270-
if !ok {
271-
return nil, fmt.Errorf("source does not implement io.Reader: %T", vr.src)
272-
}
273-
274-
if _, err := io.ReadFull(reader, data); err != nil {
275-
if errors.Is(err, io.ErrUnexpectedEOF) {
276-
err = io.EOF // Convert io.ErrUnexpectedEOF to io.EOF for consistency.
277-
}
278-
return nil, err
279-
}
280-
281-
return data, nil
262+
return fetchBytes(vr.src, int(length))
282263
}
283264

284265
func (vr *valueReader) typeError(t Type) error {
@@ -900,32 +881,25 @@ func (vr *valueReader) readString() (string, error) {
900881
return "", fmt.Errorf("invalid string length: %d", length)
901882
}
902883

903-
// Peek the next length bytes without advancing.
904-
buf, err := vr.src.peek(int(length))
884+
raw, err := fetchBytes(vr.src, int(length))
905885
if err != nil {
906886
return "", err
907887
}
908888

909889
// Check that the last byte is the NUL terminator.
910-
if buf[len(buf)-1] != 0x00 {
911-
return "", fmt.Errorf("string does not end with null byte, but with %v", buf[len(buf)-1])
912-
}
913-
914-
// Advance past the length bytes.
915-
if _, err := vr.src.discard(int(length)); err != nil {
916-
return "", err
890+
if raw[len(raw)-1] != 0x00 {
891+
return "", fmt.Errorf("string does not end with null byte, but with %v", raw[len(raw)-1])
917892
}
918893

919894
// Convert and strip the trailing NUL.
920-
return string(buf[:len(buf)-1]), nil
895+
return string(raw[:len(raw)-1]), nil
921896
}
922897

923898
func (vr *valueReader) peekLength() (int32, error) {
924899
buf, err := vr.src.peek(4)
925900
if err != nil {
926901
return 0, err
927902
}
928-
929903
return int32(binary.LittleEndian.Uint32(buf)), nil
930904
}
931905

@@ -934,57 +908,62 @@ func (vr *valueReader) readLength() (int32, error) {
934908
}
935909

936910
func (vr *valueReader) readi32() (int32, error) {
937-
buf, err := vr.src.peek(4)
911+
raw, err := fetchBytes(vr.src, 4)
938912
if err != nil {
939913
return 0, err
940914
}
941915

942-
// Advance the cursor past the 4 bytes.
943-
if _, err := vr.src.discard(4); err != nil {
944-
return 0, err
945-
}
946-
947-
return int32(binary.LittleEndian.Uint32(buf)), nil
916+
return int32(binary.LittleEndian.Uint32(raw)), nil
948917
}
949918

950919
func (vr *valueReader) readu32() (uint32, error) {
951-
buf, err := vr.src.peek(4)
920+
raw, err := fetchBytes(vr.src, 4)
952921
if err != nil {
953922
return 0, err
954923
}
955924

956-
// Advance the cursor past the 4 bytes.
957-
if _, err := vr.src.discard(4); err != nil {
958-
return 0, err
959-
}
960-
961-
return binary.LittleEndian.Uint32(buf), nil
925+
return binary.LittleEndian.Uint32(raw), nil
962926
}
963927

964928
func (vr *valueReader) readi64() (int64, error) {
965-
buf, err := vr.src.peek(8)
929+
raw, err := fetchBytes(vr.src, 8)
966930
if err != nil {
967931
return 0, err
968932
}
969933

970-
// Advance the cursor past the 8 bytes.
971-
if _, err := vr.src.discard(8); err != nil {
972-
return 0, err
973-
}
974-
975-
return int64(binary.LittleEndian.Uint64(buf)), nil
934+
return int64(binary.LittleEndian.Uint64(raw)), nil
976935
}
977936

978937
func (vr *valueReader) readu64() (uint64, error) {
979-
buf, err := vr.src.peek(8)
938+
raw, err := fetchBytes(vr.src, 8)
980939
if err != nil {
981940
return 0, err
982941
}
983942

984-
// Advance the cursor past the 8 bytes.
985-
if _, err := vr.src.discard(8); err != nil {
986-
return 0, err
943+
return binary.LittleEndian.Uint64(raw), nil
944+
}
945+
946+
// fetchBytes tries to grab the next n bytes zero-allocation using peek+discard.
947+
// If peek fails (e.g. bufio buffer full), it falls back to io.ReadFull.
948+
func fetchBytes(src valueReaderByteSrc, n int) ([]byte, error) {
949+
if src.streamable() {
950+
data := make([]byte, n)
951+
if _, err := io.ReadFull(src, data); err != nil {
952+
if errors.Is(err, io.ErrUnexpectedEOF) {
953+
err = io.EOF // Convert io.ErrUnexpectedEOF to io.EOF for consistency.
954+
}
955+
return nil, err
956+
}
957+
958+
return data, nil
959+
}
960+
961+
// Zero-allocation path.
962+
buf, err := src.peek(n)
963+
if err != nil {
964+
return nil, err
987965
}
988966

989-
return binary.LittleEndian.Uint64(buf), nil
967+
_, _ = src.discard(n) // Discard the bytes from the source.
968+
return buf, nil
990969
}

0 commit comments

Comments
 (0)