@@ -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
284265func (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
923898func (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
936910func (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
950919func (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
964928func (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
978937func (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