* add check negative position to Read() and write tests * add tests for Write() method * add tests for Write() method * add checks of whence and negative position to Seek() and write tests * add tests for Rewind() * add tests for Close() * add tests for Reset() * add tests for Len() * add tests for Bytes() * tests polishing * tests polishing * tests polishing * tests polishing
		
			
				
	
	
		
			117 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package buffer
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| )
 | |
| 
 | |
| var _ interface {
 | |
| 	io.ReadCloser
 | |
| 	io.ReadSeeker
 | |
| } = (*SeekerBuffer)(nil)
 | |
| 
 | |
| // SeekerBuffer is a ReadWriteCloser that supports seeking. It's intended to
 | |
| // replicate the functionality of bytes.Buffer that I use in my projects.
 | |
| //
 | |
| // Note that the seeking is limited to the read marker; all writes are
 | |
| // append-only.
 | |
| type SeekerBuffer struct {
 | |
| 	data []byte
 | |
| 	pos  int64
 | |
| }
 | |
| 
 | |
| func NewSeekerBuffer(data []byte) *SeekerBuffer {
 | |
| 	return &SeekerBuffer{
 | |
| 		data: data,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Read reads up to len(p) bytes into p from the current read position.
 | |
| func (b *SeekerBuffer) Read(p []byte) (int, error) {
 | |
| 	if b.pos >= int64(len(b.data)) {
 | |
| 		return 0, io.EOF
 | |
| 	}
 | |
| 
 | |
| 	n := copy(p, b.data[b.pos:])
 | |
| 	b.pos += int64(n)
 | |
| 
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| // Write appends the contents of p to the end of the buffer. It does not affect the read position.
 | |
| func (b *SeekerBuffer) Write(p []byte) (int, error) {
 | |
| 	if len(p) == 0 {
 | |
| 		return 0, nil
 | |
| 	}
 | |
| 
 | |
| 	b.data = append(b.data, p...)
 | |
| 
 | |
| 	return len(p), nil
 | |
| }
 | |
| 
 | |
| // Seek sets the offset for the next Read operation.
 | |
| // The offset is interpreted according to whence:
 | |
| // - io.SeekStart: relative to the beginning of the buffer
 | |
| // - io.SeekCurrent: relative to the current position
 | |
| // - io.SeekEnd: relative to the end of the buffer
 | |
| //
 | |
| // Returns an error if the resulting position is negative or if whence is invalid.
 | |
| func (b *SeekerBuffer) Seek(offset int64, whence int) (int64, error) {
 | |
| 	var newPos int64
 | |
| 
 | |
| 	switch whence {
 | |
| 	case io.SeekStart:
 | |
| 		newPos = offset
 | |
| 	case io.SeekEnd:
 | |
| 		newPos = int64(len(b.data)) + offset
 | |
| 	case io.SeekCurrent:
 | |
| 		newPos = b.pos + offset
 | |
| 	default:
 | |
| 		return 0, fmt.Errorf("invalid whence: %d", whence)
 | |
| 	}
 | |
| 
 | |
| 	if newPos < 0 {
 | |
| 		return 0, fmt.Errorf("invalid seek: resulting position %d is negative", newPos)
 | |
| 	}
 | |
| 
 | |
| 	b.pos = newPos
 | |
| 	return b.pos, nil
 | |
| }
 | |
| 
 | |
| // Rewind resets the read position to 0.
 | |
| func (b *SeekerBuffer) Rewind() error {
 | |
| 	if _, err := b.Seek(0, io.SeekStart); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Close clears all the data out of the buffer and sets the read position to 0.
 | |
| func (b *SeekerBuffer) Close() error {
 | |
| 	b.data = nil
 | |
| 	b.pos = 0
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Reset clears all the data out of the buffer and sets the read position to 0.
 | |
| func (b *SeekerBuffer) Reset() {
 | |
| 	b.data = nil
 | |
| 	b.pos = 0
 | |
| }
 | |
| 
 | |
| // Len returns the length of data remaining to be read.
 | |
| func (b *SeekerBuffer) Len() int {
 | |
| 	if b.pos >= int64(len(b.data)) {
 | |
| 		return 0
 | |
| 	}
 | |
| 	return len(b.data[b.pos:])
 | |
| }
 | |
| 
 | |
| // Bytes returns the underlying bytes from the current position.
 | |
| func (b *SeekerBuffer) Bytes() []byte {
 | |
| 	if b.pos >= int64(len(b.data)) {
 | |
| 		return []byte{}
 | |
| 	}
 | |
| 	return b.data[b.pos:]
 | |
| }
 |