pax_global_header00006660000000000000000000000064136546703700014525gustar00rootroot0000000000000052 comment=1bf7f4916be4784162fe9511c735cf562cf7f1e4 filebuffer-1.0.1/000077500000000000000000000000001365467037000136355ustar00rootroot00000000000000filebuffer-1.0.1/.gitignore000066400000000000000000000004121365467037000156220ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof filebuffer-1.0.1/.travis.yml000066400000000000000000000001501365467037000157420ustar00rootroot00000000000000language: go go: - 1.8.x - 1.9.x - 1.10.x - 1.11.x - 1.12.x - 1.13.x - 1.14.x - stable filebuffer-1.0.1/LICENSE000066400000000000000000000020711365467037000146420ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Matt Aimonetti Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. filebuffer-1.0.1/README.md000066400000000000000000000011151365467037000151120ustar00rootroot00000000000000# filebuffer filebuffer is a package implementing a few file-like interfaces such as `io.Reader`, `io.ReaderAt`, `io.Seeker` and more. The implementation is backed by a byte buffer and the main purpose is to have in-memory alternative to using an io.File. More information can be found on the [GoDoc page](https://godoc.org/github.com/mattetti/filebuffer). [![GoDoc](http://godoc.org/github.com/mattetti/filebuffer?status.svg)](http://godoc.org/github.com/mattetti/filebuffer) [![Build Status](https://travis-ci.org/mattetti/filebuffer.png)](https://travis-ci.org/mattetti/filebuffer) filebuffer-1.0.1/filebuffer.go000066400000000000000000000121121365467037000162720ustar00rootroot00000000000000// Package filebuffer is a package implementing a few file like interfaces // backed by a byte buffer. // Implemented interfaces: // // * Reader // * ReaderAt // * Writer // * Seeker // * Closer package filebuffer import ( "bytes" "errors" "io" "io/ioutil" "os" ) // Buffer implements interfaces implemented by files. // The main purpose of this type is to have an in memory replacement for a // file. type Buffer struct { // Buff is the backing buffer Buff *bytes.Buffer // Index indicates where in the buffer we are at Index int64 isClosed bool } // New returns a new populated Buffer func New(b []byte) *Buffer { return &Buffer{Buff: bytes.NewBuffer(b)} } // NewFromReader is a convenience method that returns a new populated Buffer // whose contents are sourced from a supplied reader by loading it entirely // into memory. func NewFromReader(reader io.Reader) (*Buffer, error) { data, err := ioutil.ReadAll(reader) if err != nil { return nil, err } return New(data), nil } // Bytes returns the bytes available until the end of the buffer. func (f *Buffer) Bytes() []byte { if f.isClosed || f.Index >= int64(f.Buff.Len()) { return []byte{} } return f.Buff.Bytes()[f.Index:] } // String implements the Stringer interface func (f *Buffer) String() string { return string(f.Buff.Bytes()[f.Index:]) } // Read implements io.Reader https://golang.org/pkg/io/#Reader // Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) // and any error encountered. Even if Read returns n < len(p), it may use all of p as scratch // space during the call. If some data is available but not len(p) bytes, Read conventionally // returns what is available instead of waiting for more. // When Read encounters an error or end-of-file condition after successfully reading n > 0 bytes, // it returns the number of bytes read. It may return the (non-nil) error from the same call or // return the error (and n == 0) from a subsequent call. An instance of this general case is // that a Reader returning a non-zero number of bytes at the end of the input stream may return // either err == EOF or err == nil. The next Read should return 0, EOF. func (f *Buffer) Read(b []byte) (n int, err error) { if f.isClosed { return 0, os.ErrClosed } if len(b) == 0 { return 0, nil } if f.Index >= int64(f.Buff.Len()) { return 0, io.EOF } n, err = bytes.NewBuffer(f.Buff.Bytes()[f.Index:]).Read(b) f.Index += int64(n) return n, err } // ReadAt implements io.ReaderAt https://golang.org/pkg/io/#ReaderAt // ReadAt reads len(p) bytes into p starting at offset off in the underlying input source. // It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. // // When ReadAt returns n < len(p), it returns a non-nil error explaining why more bytes were not returned. // In this respect, ReadAt is stricter than Read. // // Even if ReadAt returns n < len(p), it may use all of p as scratch space during the call. // If some data is available but not len(p) bytes, ReadAt blocks until either all the data is available or an error occurs. // In this respect ReadAt is different from Read. // // If the n = len(p) bytes returned by ReadAt are at the end of the input source, // ReadAt may return either err == EOF or err == nil. // // If ReadAt is reading from an input source with a seek offset, // ReadAt should not affect nor be affected by the underlying seek offset. // Clients of ReadAt can execute parallel ReadAt calls on the same input source. func (f *Buffer) ReadAt(p []byte, off int64) (n int, err error) { if f.isClosed { return 0, os.ErrClosed } if off < 0 { return 0, errors.New("filebuffer.ReadAt: negative offset") } reqLen := len(p) buffLen := int64(f.Buff.Len()) if off >= buffLen { return 0, io.EOF } n = copy(p, f.Buff.Bytes()[off:]) if n < reqLen { err = io.EOF } return n, err } // Write implements io.Writer https://golang.org/pkg/io/#Writer // by appending the passed bytes to the buffer unless the buffer is closed or index negative. func (f *Buffer) Write(p []byte) (n int, err error) { if f.isClosed { return 0, os.ErrClosed } if f.Index < 0 { return 0, io.EOF } // we might have rewinded, let's reset the buffer before appending to it idx := int(f.Index) buffLen := f.Buff.Len() if idx != buffLen && idx <= buffLen { f.Buff = bytes.NewBuffer(f.Bytes()[:f.Index]) } n, err = f.Buff.Write(p) f.Index += int64(n) return n, err } // Seek implements io.Seeker https://golang.org/pkg/io/#Seeker func (f *Buffer) Seek(offset int64, whence int) (idx int64, err error) { if f.isClosed { return 0, os.ErrClosed } var abs int64 switch whence { case 0: abs = offset case 1: abs = int64(f.Index) + offset case 2: abs = int64(f.Buff.Len()) + offset default: return 0, errors.New("filebuffer.Seek: invalid whence") } if abs < 0 { return 0, errors.New("filebuffer.Seek: negative position") } f.Index = abs return abs, nil } // Close implements io.Closer https://golang.org/pkg/io/#Closer // It closes the buffer, rendering it unusable for I/O. It returns an error, if any. func (f *Buffer) Close() error { f.isClosed = true return nil } filebuffer-1.0.1/filebuffer_test.go000066400000000000000000000122361365467037000173400ustar00rootroot00000000000000package filebuffer import ( "bytes" "fmt" "io" "os" "sync" "testing" ) func TestNewFromReader(t *testing.T) { content := []byte("test") reader := bytes.NewReader(content) file := New(content) fileFromReader, err := NewFromReader(reader) if err != nil { t.Fatal(err) } expected := file.Bytes() actual := fileFromReader.Bytes() if !bytes.Equal(expected, actual) { t.Fatalf("expected %s to be equal to %s", expected, actual) } } func TestReader(t *testing.T) { r := New([]byte("0123456789")) tests := []struct { off int64 seek int n int want string wantpos int64 seekerr string }{ {seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"}, {seek: os.SEEK_SET, off: 1, n: 1, want: "1"}, {seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"}, {seek: os.SEEK_SET, off: -1, seekerr: "filebuffer.Seek: negative position"}, {seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33}, {seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1}, {seek: os.SEEK_SET, n: 5, want: "01234"}, {seek: os.SEEK_CUR, n: 5, want: "56789"}, {seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"}, } for i, tt := range tests { pos, err := r.Seek(tt.off, tt.seek) if err == nil && tt.seekerr != "" { t.Errorf("%d. want seek error %q", i, tt.seekerr) continue } if err != nil && err.Error() != tt.seekerr { t.Errorf("%d. seek error = %q; want %q", i, err.Error(), tt.seekerr) continue } if tt.wantpos != 0 && tt.wantpos != pos { t.Errorf("%d. pos = %d, want %d", i, pos, tt.wantpos) } buf := make([]byte, tt.n) n, err := r.Read(buf) if err != nil { t.Errorf("%d. read = %v", i, err) continue } got := string(buf[:n]) if got != tt.want { t.Errorf("%d. got %q; want %q", i, got, tt.want) } } } func TestReadAfterBigSeek(t *testing.T) { r := New([]byte("0123456789")) if _, err := r.Seek(1<<31+5, os.SEEK_SET); err != nil { t.Fatal(err) } if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF { t.Errorf("Read = %d, %v; want 0, EOF", n, err) } } func TestReaderAt(t *testing.T) { r := New([]byte("0123456789")) tests := []struct { off int64 n int want string wanterr interface{} }{ {0, 10, "0123456789", nil}, {1, 10, "123456789", io.EOF}, {1, 9, "123456789", nil}, {11, 10, "", io.EOF}, {0, 0, "", nil}, {-1, 0, "", "filebuffer.ReadAt: negative offset"}, } for i, tt := range tests { b := make([]byte, tt.n) rn, err := r.ReadAt(b, tt.off) got := string(b[:rn]) if got != tt.want { t.Errorf("%d. got %q; want %q", i, got, tt.want) } if fmt.Sprintf("%v", err) != fmt.Sprintf("%v", tt.wanterr) { t.Errorf("%d. got error = %v; want %v", i, err, tt.wanterr) } } } func TestReaderAtConcurrent(t *testing.T) { // Test for the race detector, to verify ReadAt doesn't mutate // any state. r := New([]byte("0123456789")) var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func(i int) { defer wg.Done() var buf [1]byte r.ReadAt(buf[:], int64(i)) }(i) } wg.Wait() } func TestEmptyReaderConcurrent(t *testing.T) { // Test for the race detector, to verify a Read that doesn't yield any bytes // is okay to use from multiple goroutines. r := New([]byte("")) var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(2) go func() { defer wg.Done() var buf [1]byte r.Read(buf[:]) }() go func() { defer wg.Done() r.Read(nil) }() } wg.Wait() } func TestClose(t *testing.T) { b := New(nil) err := b.Close() if err != nil { t.Fatalf("expected closing to not return an error but returned %v", err) } n, err := b.Write([]byte{0x42, 0x42, 0x42}) if err != os.ErrClosed { t.Fatalf("Expecting ErrClosed got: %v", err) } if n != 0 { t.Fatalf("expected 0 bytes to be written but %d were reported written", n) } } func TestWriter(t *testing.T) { b := New(nil) n, err := b.Write([]byte("this is a test")) if n != 14 { t.Fatalf("expected 14 characters written, reported %d", n) } if err != nil { t.Fatal(err) } testString := `this is a test` dst := make([]byte, 14) _, err = b.Read(dst) if err != io.EOF { t.Fatalf("expected an EOF error but got %v", err) } b.Seek(0, 0) _, err = b.Read(dst) if err != nil { t.Fatalf("Error reading file: %v", err) } if string(dst) != testString { t.Fatalf("expected `%s` but got `%s`", testString, string(dst)) } // testing Bytes() b.Seek(0, 0) content := string(b.Bytes()) if content != testString { t.Fatalf("expected a rewinded buffer calling Bytes to output `%s` but got `%s`", testString, content) } // testing overwriting content b.Seek(0, 0) altTestString := `maybe, this is the real test` b.Write([]byte(altTestString)) b.Seek(0, 0) _, err = b.Read(dst) if err != nil { t.Fatal(err) } if string(dst) != altTestString[:14] { t.Fatalf("expected overwriting the buffer content to read as `%s` but got `%s`", altTestString[:14], string(dst)) } // reset the index to the end of the buffer b.Seek(0, 2) // testing appending _, err = b.Write([]byte(` or maybe it's not`)) if err != nil { t.Fatal(err) } b.Seek(0, 0) content = string(b.Bytes()) if content != altTestString+` or maybe it's not` { t.Fatalf("unexpected appended buffer, content: `%s`", content) } }