bufreader.go 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. // bufreader provides a buffered reader that reads from io.Reader, but caches
  2. // the data in a bytes.Buffer to allow peeking and discarding without re-reading.
  3. package bufreader
  4. import (
  5. "io"
  6. )
  7. // ReadPeeker is an interface that combines io.Reader and a method to peek at the next n bytes
  8. type ReadPeeker interface {
  9. io.Reader
  10. Peek(n int) ([]byte, error) // Peek returns the next n bytes without advancing
  11. }
  12. // Reader is a buffered reader that reads from an io.Reader and caches the data.
  13. type Reader struct {
  14. r io.Reader
  15. buf []byte
  16. pos int
  17. }
  18. // New creates new buffered reader
  19. func New(r io.Reader) *Reader {
  20. br := Reader{
  21. r: r,
  22. buf: nil,
  23. }
  24. return &br
  25. }
  26. // Read reads data into p from the buffered reader.
  27. func (br *Reader) Read(p []byte) (int, error) {
  28. if err := br.fetch(br.pos + len(p)); err != nil {
  29. return 0, err
  30. }
  31. n := copy(p, br.buf[br.pos:])
  32. br.pos += n
  33. return n, nil
  34. }
  35. // Peek returns the next n bytes from the buffered reader without advancing the position.
  36. func (br *Reader) Peek(n int) ([]byte, error) {
  37. err := br.fetch(br.pos + n)
  38. if err != nil && err != io.EOF {
  39. return nil, err
  40. }
  41. // Return slice of buffered data without advancing position
  42. available := br.buf[br.pos:]
  43. if len(available) == 0 && err == io.EOF {
  44. return nil, io.EOF
  45. }
  46. return available[:min(len(available), n)], nil
  47. }
  48. // Rewind seeks buffer to the beginning
  49. func (br *Reader) Rewind() {
  50. br.pos = 0
  51. }
  52. // fetch ensures the buffer contains at least 'need' bytes
  53. func (br *Reader) fetch(need int) error {
  54. if need-len(br.buf) <= 0 {
  55. return nil
  56. }
  57. b := make([]byte, need)
  58. n, err := io.ReadFull(br.r, b)
  59. if err != nil && err != io.ErrUnexpectedEOF {
  60. return err
  61. }
  62. // append only those which we read in fact
  63. br.buf = append(br.buf, b[:n]...)
  64. return nil
  65. }