pax_global_header00006660000000000000000000000064146720421520014515gustar00rootroot0000000000000052 comment=20a13a1f6b7cb47a126dcb75152e21e1383bbaba fwd-master/000077500000000000000000000000001467204215200131325ustar00rootroot00000000000000fwd-master/LICENSE.md000066400000000000000000000020451467204215200145370ustar00rootroot00000000000000Copyright (c) 2014-2015, Philip Hofer 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.fwd-master/README.md000066400000000000000000000227541467204215200144230ustar00rootroot00000000000000 # fwd [![Go Reference](https://pkg.go.dev/badge/github.com/philhofer/fwd.svg)](https://pkg.go.dev/github.com/philhofer/fwd) `import "github.com/philhofer/fwd"` * [Overview](#pkg-overview) * [Index](#pkg-index) ## Overview Package fwd provides a buffered reader and writer. Each has methods that help improve the encoding/decoding performance of some binary protocols. The `Writer` and `Reader` type provide similar functionality to their counterparts in `bufio`, plus a few extra utility methods that simplify read-ahead and write-ahead. I wrote this package to improve serialization performance for [github.com/tinylib/msgp](https://github.com/tinylib/msgp), where it provided about a 2x speedup over `bufio` for certain workloads. However, care must be taken to understand the semantics of the extra methods provided by this package, as they allow the user to access and manipulate the buffer memory directly. The extra methods for `fwd.Reader` are `Peek`, `Skip` and `Next`. `(*fwd.Reader).Peek`, unlike `(*bufio.Reader).Peek`, will re-allocate the read buffer in order to accommodate arbitrarily large read-ahead. `(*fwd.Reader).Skip` skips the next `n` bytes in the stream, and uses the `io.Seeker` interface if the underlying stream implements it. `(*fwd.Reader).Next` returns a slice pointing to the next `n` bytes in the read buffer (like `Peek`), but also increments the read position. This allows users to process streams in arbitrary block sizes without having to manage appropriately-sized slices. Additionally, obviating the need to copy the data from the buffer to another location in memory can improve performance dramatically in CPU-bound applications. `fwd.Writer` only has one extra method, which is `(*fwd.Writer).Next`, which returns a slice pointing to the next `n` bytes of the writer, and increments the write position by the length of the returned slice. This allows users to write directly to the end of the buffer. ## Portability Because it uses the unsafe package, there are theoretically no promises about forward or backward portability. To stay compatible with tinygo 0.32, unsafestr() has been updated to use unsafe.Slice() as suggested by https://tinygo.org/docs/guides/compatibility, which also required bumping go.mod to require at least go 1.20. ## Index * [Constants](#pkg-constants) * [type Reader](#Reader) * [func NewReader(r io.Reader) *Reader](#NewReader) * [func NewReaderBuf(r io.Reader, buf []byte) *Reader](#NewReaderBuf) * [func NewReaderSize(r io.Reader, n int) *Reader](#NewReaderSize) * [func (r *Reader) BufferSize() int](#Reader.BufferSize) * [func (r *Reader) Buffered() int](#Reader.Buffered) * [func (r *Reader) Next(n int) ([]byte, error)](#Reader.Next) * [func (r *Reader) Peek(n int) ([]byte, error)](#Reader.Peek) * [func (r *Reader) Read(b []byte) (int, error)](#Reader.Read) * [func (r *Reader) ReadByte() (byte, error)](#Reader.ReadByte) * [func (r *Reader) ReadFull(b []byte) (int, error)](#Reader.ReadFull) * [func (r *Reader) Reset(rd io.Reader)](#Reader.Reset) * [func (r *Reader) Skip(n int) (int, error)](#Reader.Skip) * [func (r *Reader) WriteTo(w io.Writer) (int64, error)](#Reader.WriteTo) * [type Writer](#Writer) * [func NewWriter(w io.Writer) *Writer](#NewWriter) * [func NewWriterBuf(w io.Writer, buf []byte) *Writer](#NewWriterBuf) * [func NewWriterSize(w io.Writer, n int) *Writer](#NewWriterSize) * [func (w *Writer) BufferSize() int](#Writer.BufferSize) * [func (w *Writer) Buffered() int](#Writer.Buffered) * [func (w *Writer) Flush() error](#Writer.Flush) * [func (w *Writer) Next(n int) ([]byte, error)](#Writer.Next) * [func (w *Writer) ReadFrom(r io.Reader) (int64, error)](#Writer.ReadFrom) * [func (w *Writer) Write(p []byte) (int, error)](#Writer.Write) * [func (w *Writer) WriteByte(b byte) error](#Writer.WriteByte) * [func (w *Writer) WriteString(s string) (int, error)](#Writer.WriteString) ## Constants ``` go const ( // DefaultReaderSize is the default size of the read buffer DefaultReaderSize = 2048 ) ``` ``` go const ( // DefaultWriterSize is the // default write buffer size. DefaultWriterSize = 2048 ) ``` ## type Reader ``` go type Reader struct { // contains filtered or unexported fields } ``` Reader is a buffered look-ahead reader ### func NewReader ``` go func NewReader(r io.Reader) *Reader ``` NewReader returns a new *Reader that reads from 'r' ### func NewReaderSize ``` go func NewReaderSize(r io.Reader, n int) *Reader ``` NewReaderSize returns a new *Reader that reads from 'r' and has a buffer size 'n' ### func (\*Reader) BufferSize ``` go func (r *Reader) BufferSize() int ``` BufferSize returns the total size of the buffer ### func (\*Reader) Buffered ``` go func (r *Reader) Buffered() int ``` Buffered returns the number of bytes currently in the buffer ### func (\*Reader) Next ``` go func (r *Reader) Next(n int) ([]byte, error) ``` Next returns the next 'n' bytes in the stream. Unlike Peek, Next advances the reader position. The returned bytes point to the same data as the buffer, so the slice is only valid until the next reader method call. An EOF is considered an unexpected error. If an the returned slice is less than the length asked for, an error will be returned, and the reader position will not be incremented. ### func (\*Reader) Peek ``` go func (r *Reader) Peek(n int) ([]byte, error) ``` Peek returns the next 'n' buffered bytes, reading from the underlying reader if necessary. It will only return a slice shorter than 'n' bytes if it also returns an error. Peek does not advance the reader. EOF errors are *not* returned as io.ErrUnexpectedEOF. ### func (\*Reader) Read ``` go func (r *Reader) Read(b []byte) (int, error) ``` Read implements `io.Reader`. ### func (\*Reader) ReadByte ``` go func (r *Reader) ReadByte() (byte, error) ``` ReadByte implements `io.ByteReader`. ### func (\*Reader) ReadFull ``` go func (r *Reader) ReadFull(b []byte) (int, error) ``` ReadFull attempts to read len(b) bytes into 'b'. It returns the number of bytes read into 'b', and an error if it does not return len(b). EOF is considered an unexpected error. ### func (\*Reader) Reset ``` go func (r *Reader) Reset(rd io.Reader) ``` Reset resets the underlying reader and the read buffer. ### func (\*Reader) Skip ``` go func (r *Reader) Skip(n int) (int, error) ``` Skip moves the reader forward 'n' bytes. Returns the number of bytes skipped and any errors encountered. It is analogous to Seek(n, 1). If the underlying reader implements io.Seeker, then that method will be used to skip forward. If the reader encounters an EOF before skipping 'n' bytes, it returns `io.ErrUnexpectedEOF`. If the underlying reader implements `io.Seeker`, then those rules apply instead. (Many implementations will not return `io.EOF` until the next call to Read). ### func (\*Reader) WriteTo ``` go func (r *Reader) WriteTo(w io.Writer) (int64, error) ``` WriteTo implements `io.WriterTo`. ## type Writer ``` go type Writer struct { // contains filtered or unexported fields } ``` Writer is a buffered writer ### func NewWriter ``` go func NewWriter(w io.Writer) *Writer ``` NewWriter returns a new writer that writes to 'w' and has a buffer that is `DefaultWriterSize` bytes. ### func NewWriterBuf ``` go func NewWriterBuf(w io.Writer, buf []byte) *Writer ``` NewWriterBuf returns a new writer that writes to 'w' and has 'buf' as a buffer. 'buf' is not used when has smaller capacity than 18, custom buffer is allocated instead. ### func NewWriterSize ``` go func NewWriterSize(w io.Writer, n int) *Writer ``` NewWriterSize returns a new writer that writes to 'w' and has a buffer size 'n'. ### func (\*Writer) BufferSize ``` go func (w *Writer) BufferSize() int ``` BufferSize returns the maximum size of the buffer. ### func (\*Writer) Buffered ``` go func (w *Writer) Buffered() int ``` Buffered returns the number of buffered bytes in the reader. ### func (\*Writer) Flush ``` go func (w *Writer) Flush() error ``` Flush flushes any buffered bytes to the underlying writer. ### func (\*Writer) Next ``` go func (w *Writer) Next(n int) ([]byte, error) ``` Next returns the next 'n' free bytes in the write buffer, flushing the writer as necessary. Next will return `io.ErrShortBuffer` if 'n' is greater than the size of the write buffer. Calls to 'next' increment the write position by the size of the returned buffer. ### func (\*Writer) ReadFrom ``` go func (w *Writer) ReadFrom(r io.Reader) (int64, error) ``` ReadFrom implements `io.ReaderFrom` ### func (\*Writer) Write ``` go func (w *Writer) Write(p []byte) (int, error) ``` Write implements `io.Writer` ### func (\*Writer) WriteByte ``` go func (w *Writer) WriteByte(b byte) error ``` WriteByte implements `io.ByteWriter` ### func (\*Writer) WriteString ``` go func (w *Writer) WriteString(s string) (int, error) ``` WriteString is analogous to Write, but it takes a string. - - - Generated by [godoc2md](https://github.com/davecheney/godoc2md) fwd-master/go.mod000066400000000000000000000001271467204215200142400ustar00rootroot00000000000000module github.com/philhofer/fwd go 1.20 // unsafe.StringData requires go1.20 or later fwd-master/reader.go000066400000000000000000000254431467204215200147330ustar00rootroot00000000000000// Package fwd provides a buffered reader // and writer. Each has methods that help improve // the encoding/decoding performance of some binary // protocols. // // The [Writer] and [Reader] type provide similar // functionality to their counterparts in [bufio], plus // a few extra utility methods that simplify read-ahead // and write-ahead. I wrote this package to improve serialization // performance for http://github.com/tinylib/msgp, // where it provided about a 2x speedup over `bufio` for certain // workloads. However, care must be taken to understand the semantics of the // extra methods provided by this package, as they allow // the user to access and manipulate the buffer memory // directly. // // The extra methods for [Reader] are [Reader.Peek], [Reader.Skip] // and [Reader.Next]. (*fwd.Reader).Peek, unlike (*bufio.Reader).Peek, // will re-allocate the read buffer in order to accommodate arbitrarily // large read-ahead. (*fwd.Reader).Skip skips the next 'n' bytes // in the stream, and uses the [io.Seeker] interface if the underlying // stream implements it. (*fwd.Reader).Next returns a slice pointing // to the next 'n' bytes in the read buffer (like Reader.Peek), but also // increments the read position. This allows users to process streams // in arbitrary block sizes without having to manage appropriately-sized // slices. Additionally, obviating the need to copy the data from the // buffer to another location in memory can improve performance dramatically // in CPU-bound applications. // // [Writer] only has one extra method, which is (*fwd.Writer).Next, which // returns a slice pointing to the next 'n' bytes of the writer, and increments // the write position by the length of the returned slice. This allows users // to write directly to the end of the buffer. package fwd import ( "io" "os" ) const ( // DefaultReaderSize is the default size of the read buffer DefaultReaderSize = 2048 // minimum read buffer; straight from bufio minReaderSize = 16 ) // NewReader returns a new *Reader that reads from 'r' func NewReader(r io.Reader) *Reader { return NewReaderSize(r, DefaultReaderSize) } // NewReaderSize returns a new *Reader that // reads from 'r' and has a buffer size 'n'. func NewReaderSize(r io.Reader, n int) *Reader { buf := make([]byte, 0, max(n, minReaderSize)) return NewReaderBuf(r, buf) } // NewReaderBuf returns a new *Reader that // reads from 'r' and uses 'buf' as a buffer. // 'buf' is not used when has smaller capacity than 16, // custom buffer is allocated instead. func NewReaderBuf(r io.Reader, buf []byte) *Reader { if cap(buf) < minReaderSize { buf = make([]byte, 0, minReaderSize) } buf = buf[:0] rd := &Reader{ r: r, data: buf, } if s, ok := r.(io.Seeker); ok { rd.rs = s } return rd } // Reader is a buffered look-ahead reader type Reader struct { r io.Reader // underlying reader // data[n:len(data)] is buffered data; data[len(data):cap(data)] is free buffer space data []byte // data n int // read offset inputOffset int64 // offset in the input stream state error // last read error // if the reader past to NewReader was // also an io.Seeker, this is non-nil rs io.Seeker } // Reset resets the underlying reader // and the read buffer. func (r *Reader) Reset(rd io.Reader) { r.r = rd r.data = r.data[0:0] r.n = 0 r.inputOffset = 0 r.state = nil if s, ok := rd.(io.Seeker); ok { r.rs = s } else { r.rs = nil } } // more() does one read on the underlying reader func (r *Reader) more() { // move data backwards so that // the read offset is 0; this way // we can supply the maximum number of // bytes to the reader if r.n != 0 { if r.n < len(r.data) { r.data = r.data[:copy(r.data[0:], r.data[r.n:])] } else { r.data = r.data[:0] } r.n = 0 } var a int a, r.state = r.r.Read(r.data[len(r.data):cap(r.data)]) if a == 0 && r.state == nil { r.state = io.ErrNoProgress return } else if a > 0 && r.state == io.EOF { // discard the io.EOF if we read more than 0 bytes. // the next call to Read should return io.EOF again. r.state = nil } else if r.state != nil { return } r.data = r.data[:len(r.data)+a] } // pop error func (r *Reader) err() (e error) { e, r.state = r.state, nil return } // pop error; EOF -> io.ErrUnexpectedEOF func (r *Reader) noEOF() (e error) { e, r.state = r.state, nil if e == io.EOF { e = io.ErrUnexpectedEOF } return } // buffered bytes func (r *Reader) buffered() int { return len(r.data) - r.n } // Buffered returns the number of bytes currently in the buffer func (r *Reader) Buffered() int { return len(r.data) - r.n } // BufferSize returns the total size of the buffer func (r *Reader) BufferSize() int { return cap(r.data) } // InputOffset returns the input stream byte offset of the current reader position func (r *Reader) InputOffset() int64 { return r.inputOffset } // Peek returns the next 'n' buffered bytes, // reading from the underlying reader if necessary. // It will only return a slice shorter than 'n' bytes // if it also returns an error. Peek does not advance // the reader. EOF errors are *not* returned as // io.ErrUnexpectedEOF. func (r *Reader) Peek(n int) ([]byte, error) { // in the degenerate case, // we may need to realloc // (the caller asked for more // bytes than the size of the buffer) if cap(r.data) < n { old := r.data[r.n:] r.data = make([]byte, n+r.buffered()) r.data = r.data[:copy(r.data, old)] r.n = 0 } // keep filling until // we hit an error or // read enough bytes for r.buffered() < n && r.state == nil { r.more() } // we must have hit an error if r.buffered() < n { return r.data[r.n:], r.err() } return r.data[r.n : r.n+n], nil } func (r *Reader) PeekByte() (b byte, err error) { if len(r.data)-r.n >= 1 { b = r.data[r.n] } else { b, err = r.peekByte() } return } func (r *Reader) peekByte() (byte, error) { const n = 1 if cap(r.data) < n { old := r.data[r.n:] r.data = make([]byte, n+r.buffered()) r.data = r.data[:copy(r.data, old)] r.n = 0 } // keep filling until // we hit an error or // read enough bytes for r.buffered() < n && r.state == nil { r.more() } // we must have hit an error if r.buffered() < n { return 0, r.err() } return r.data[r.n], nil } // discard(n) discards up to 'n' buffered bytes, and // and returns the number of bytes discarded func (r *Reader) discard(n int) int { inbuf := r.buffered() if inbuf <= n { r.n = 0 r.inputOffset += int64(inbuf) r.data = r.data[:0] return inbuf } r.n += n r.inputOffset += int64(n) return n } // Skip moves the reader forward 'n' bytes. // Returns the number of bytes skipped and any // errors encountered. It is analogous to Seek(n, 1). // If the underlying reader implements io.Seeker, then // that method will be used to skip forward. // // If the reader encounters // an EOF before skipping 'n' bytes, it // returns [io.ErrUnexpectedEOF]. If the // underlying reader implements [io.Seeker], then // those rules apply instead. (Many implementations // will not return [io.EOF] until the next call // to Read). func (r *Reader) Skip(n int) (int, error) { if n < 0 { return 0, os.ErrInvalid } // discard some or all of the current buffer skipped := r.discard(n) // if we can Seek() through the remaining bytes, do that if n > skipped && r.rs != nil { nn, err := r.rs.Seek(int64(n-skipped), 1) r.inputOffset += nn return int(nn) + skipped, err } // otherwise, keep filling the buffer // and discarding it up to 'n' for skipped < n && r.state == nil { r.more() skipped += r.discard(n - skipped) } return skipped, r.noEOF() } // Next returns the next 'n' bytes in the stream. // Unlike Peek, Next advances the reader position. // The returned bytes point to the same // data as the buffer, so the slice is // only valid until the next reader method call. // An EOF is considered an unexpected error. // If an the returned slice is less than the // length asked for, an error will be returned, // and the reader position will not be incremented. func (r *Reader) Next(n int) (b []byte, err error) { if r.state == nil && len(r.data)-r.n >= n { b = r.data[r.n : r.n+n] r.n += n r.inputOffset += int64(n) } else { b, err = r.next(n) } return } func (r *Reader) next(n int) ([]byte, error) { // in case the buffer is too small if cap(r.data) < n { old := r.data[r.n:] r.data = make([]byte, n+r.buffered()) r.data = r.data[:copy(r.data, old)] r.n = 0 } // fill at least 'n' bytes for r.buffered() < n && r.state == nil { r.more() } if r.buffered() < n { return r.data[r.n:], r.noEOF() } out := r.data[r.n : r.n+n] r.n += n r.inputOffset += int64(n) return out, nil } // Read implements [io.Reader]. func (r *Reader) Read(b []byte) (int, error) { // if we have data in the buffer, just // return that. if r.buffered() != 0 { x := copy(b, r.data[r.n:]) r.n += x r.inputOffset += int64(x) return x, nil } var n int // we have no buffered data; determine // whether or not to buffer or call // the underlying reader directly if len(b) >= cap(r.data) { n, r.state = r.r.Read(b) } else { r.more() n = copy(b, r.data) r.n = n } if n == 0 { return 0, r.err() } r.inputOffset += int64(n) return n, nil } // ReadFull attempts to read len(b) bytes into // 'b'. It returns the number of bytes read into // 'b', and an error if it does not return len(b). // EOF is considered an unexpected error. func (r *Reader) ReadFull(b []byte) (int, error) { var n int // read into b var nn int // scratch l := len(b) // either read buffered data, // or read directly for the underlying // buffer, or fetch more buffered data. for n < l && r.state == nil { if r.buffered() != 0 { nn = copy(b[n:], r.data[r.n:]) n += nn r.n += nn r.inputOffset += int64(nn) } else if l-n > cap(r.data) { nn, r.state = r.r.Read(b[n:]) n += nn r.inputOffset += int64(nn) } else { r.more() } } if n < l { return n, r.noEOF() } return n, nil } // ReadByte implements [io.ByteReader]. func (r *Reader) ReadByte() (byte, error) { for r.buffered() < 1 && r.state == nil { r.more() } if r.buffered() < 1 { return 0, r.err() } b := r.data[r.n] r.n++ r.inputOffset++ return b, nil } // WriteTo implements [io.WriterTo]. func (r *Reader) WriteTo(w io.Writer) (int64, error) { var ( i int64 ii int err error ) // first, clear buffer if r.buffered() > 0 { ii, err = w.Write(r.data[r.n:]) i += int64(ii) if err != nil { return i, err } r.data = r.data[0:0] r.n = 0 r.inputOffset += int64(ii) } for r.state == nil { // here we just do // 1:1 reads and writes r.more() if r.buffered() > 0 { ii, err = w.Write(r.data) i += int64(ii) if err != nil { return i, err } r.data = r.data[0:0] r.n = 0 r.inputOffset += int64(ii) } } if r.state != io.EOF { return i, r.err() } return i, nil } func max(a int, b int) int { if a < b { return b } return a } fwd-master/reader_test.go000066400000000000000000000271711467204215200157720ustar00rootroot00000000000000package fwd import ( "bytes" "io" "io/ioutil" "math/rand" "testing" "unsafe" ) // partialReader reads into only // part of the supplied byte slice // to the underlying reader type partialReader struct { r io.Reader } func (p partialReader) Read(b []byte) (int, error) { n := max(1, rand.Intn(len(b))) return p.r.Read(b[:n]) } func randomBts(sz int) []byte { o := make([]byte, sz) for i := 0; i < len(o); i += 8 { j := (*int64)(unsafe.Pointer(&o[i])) *j = rand.Int63() } return o } func TestRead(t *testing.T) { bts := randomBts(512) // make the buffer much // smaller than the underlying // bytes to incur multiple fills rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 128) if rd.BufferSize() != cap(rd.data) { t.Errorf("BufferSize() returned %d; should return %d", rd.BufferSize(), cap(rd.data)) } // starting Buffered() should be 0 if rd.Buffered() != 0 { t.Errorf("Buffered() should return 0 at initialization; got %d", rd.Buffered()) } some := make([]byte, 32) n, err := rd.Read(some) if err != nil { t.Fatal(err) } if n == 0 { t.Fatal("read 0 bytes w/ a non-nil error!") } some = some[:n] more := make([]byte, 64) j, err := rd.Read(more) if err != nil { t.Fatal(err) } if j == 0 { t.Fatal("read 0 bytes w/ a non-nil error") } more = more[:j] out, err := ioutil.ReadAll(rd) if err != nil { t.Fatal(err) } all := append(some, more...) all = append(all, out...) if !bytes.Equal(bts, all) { t.Errorf("bytes not equal; %d bytes in and %d bytes out", len(bts), len(out)) } // test filling out of the underlying reader big := randomBts(1 << 21) rd = NewReaderSize(partialReader{bytes.NewReader(big)}, 2048) buf := make([]byte, 3100) n, err = rd.ReadFull(buf) if err != nil { t.Fatal(err) } if n != 3100 { t.Errorf("expected 3100 bytes read by ReadFull; got %d", n) } if !bytes.Equal(buf[:n], big[:n]) { t.Error("data parity") } rest := make([]byte, (1<<21)-3100) n, err = io.ReadFull(rd, rest) if err != nil { t.Fatal(err) } if n != len(rest) { t.Errorf("expected %d bytes read by io.ReadFull; got %d", len(rest), n) } if !bytes.Equal(append(buf, rest...), big) { t.Fatal("data parity") } } func TestReadByte(t *testing.T) { bts := randomBts(512) rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 98) var ( err error i int b byte ) // scan through the whole // array byte-by-byte for err != io.EOF { b, err = rd.ReadByte() if err == nil { if b != bts[i] { t.Fatalf("offset %d: %d in; %d out", i, b, bts[i]) } } i++ } if err != io.EOF { t.Fatal(err) } } func remaining(r *Reader) int { return r.Buffered() + r.r.(partialReader).r.(*bytes.Reader).Len() } func TestSkipNoSeek(t *testing.T) { bts := randomBts(1024) rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200) n, err := rd.Skip(512) if err != nil { t.Fatal(err) } if n != 512 { t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 512) } if remaining(rd) != 512 { t.Errorf("expected 512 remaining; got %d", remaining(rd)) } var b byte b, err = rd.ReadByte() if err != nil { t.Fatal(err) } if b != bts[512] { t.Errorf("at index %d: %d in; %d out", 512, bts[512], b) } n, err = rd.Skip(10) if err != nil { t.Fatal(err) } if n != 10 { t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 10) } // the number of bytes remaining in the buffer needs // to comport with the number of bytes we expect to have skipped if want := 1024 - 512 - 10 - 1; remaining(rd) != want { t.Errorf("only %d bytes remaining (want %d)?", remaining(rd), want) } n, err = rd.Skip(10) if err != nil { t.Fatal(err) } if n != 10 { t.Fatalf("Skip(10) a second time returned %d", n) } if want := 1024 - 512 - 10 - 10 - 1; remaining(rd) != want { t.Errorf("only %d bytes remaining (want %d)?", remaining(rd), want) } b, err = rd.ReadByte() if err != nil { t.Fatalf("second ReadByte(): %s", err) } if b != bts[512+10+10+1] { t.Errorf("expected %d but got %d", bts[512+10+10], b) } // now try to skip past the end; we expect // only to skip the number of bytes remaining want := remaining(rd) n, err = rd.Skip(2000) if err != io.ErrUnexpectedEOF { t.Fatalf("expected error %q; got %q", io.EOF, err) } if n != want { t.Fatalf("expected to skip only %d bytes; skipped %d", want, n) } } func TestSkipSeek(t *testing.T) { bts := randomBts(1024) // bytes.Reader implements io.Seeker rd := NewReaderSize(bytes.NewReader(bts), 200) n, err := rd.Skip(512) if err != nil { t.Fatal(err) } if n != 512 { t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 512) } var b byte b, err = rd.ReadByte() if err != nil { t.Fatal(err) } if b != bts[512] { t.Fatalf("at index %d: %d in; %d out", 512, bts[512], b) } n, err = rd.Skip(10) if err != nil { t.Fatal(err) } if n != 10 { t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 10) } // now try to skip past the end rd.Reset(bytes.NewReader(bts)) // because of how bytes.Reader // implements Seek, this should // return (2000, nil) n, err = rd.Skip(2000) if err != nil { t.Fatal(err) } if n != 2000 { t.Fatalf("should have returned %d bytes; returned %d", 2000, n) } // the next call to Read() // should return io.EOF n, err = rd.Read([]byte{0, 0, 0}) if err != io.EOF { t.Errorf("expected %q; got %q", io.EOF, err) } if n != 0 { t.Errorf("expected 0 bytes read; got %d", n) } } func TestPeek(t *testing.T) { bts := randomBts(1024) rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200) // first, a peek < buffer size var ( peek []byte err error ) peek, err = rd.Peek(100) if err != nil { t.Fatal(err) } if len(peek) != 100 { t.Fatalf("asked for %d bytes; got %d", 100, len(peek)) } if !bytes.Equal(peek, bts[:100]) { t.Fatal("peeked bytes not equal") } // now, a peek > buffer size peek, err = rd.Peek(256) if err != nil { t.Fatal(err) } if len(peek) != 256 { t.Fatalf("asked for %d bytes; got %d", 100, len(peek)) } if !bytes.Equal(peek, bts[:256]) { t.Fatal("peeked bytes not equal") } // now try to peek past EOF peek, err = rd.Peek(2048) if err != io.EOF { t.Fatalf("expected error %q; got %q", io.EOF, err) } if len(peek) != 1024 { t.Fatalf("expected %d bytes peek-able; got %d", 1024, len(peek)) } } func TestPeekByte(t *testing.T) { bts := randomBts(1024) rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200) // first, a peek < buffer size var ( peek byte err error ) rd.Skip(100) peek, err = rd.PeekByte() if err != nil { t.Fatal(err) } if peek != bts[100] { t.Fatalf("peeked byte not equal: want %d got %d", bts[100], peek) } // now, a peek > buffer size rd.Skip(156) peek, err = rd.PeekByte() if err != nil { t.Fatal(err) } if peek != bts[256] { t.Fatalf("peeked byte not equal: want %d got %d", bts[256], peek) } } func TestNext(t *testing.T) { size := 1024 bts := randomBts(size) rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200) chunksize := 256 chunks := size / chunksize for i := 0; i < chunks; i++ { out, err := rd.Next(chunksize) if err != nil { t.Fatal(err) } start := chunksize * i if !bytes.Equal(bts[start:start+chunksize], out) { t.Fatalf("chunk %d: chunks not equal", i+1) } } } func TestWriteTo(t *testing.T) { bts := randomBts(2048) rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200) // cause the buffer // to fill a little, just // to complicate things rd.Peek(25) var out bytes.Buffer n, err := rd.WriteTo(&out) if err != nil { t.Fatal(err) } if n != 2048 { t.Fatalf("should have written %d bytes; wrote %d", 2048, n) } if !bytes.Equal(out.Bytes(), bts) { t.Fatal("bytes not equal") } } func TestReadFull(t *testing.T) { bts := randomBts(1024) rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 256) // try to ReadFull() the whole thing out := make([]byte, 1024) n, err := rd.ReadFull(out) if err != nil { t.Fatal(err) } if n != 1024 { t.Fatalf("expected to read %d bytes; read %d", 1024, n) } if !bytes.Equal(bts, out) { t.Fatal("bytes not equal") } // we've read everything; this should EOF n, err = rd.Read(out) if err != io.EOF { t.Fatalf("expected %q; got %q", io.EOF, err) } rd.Reset(partialReader{bytes.NewReader(bts)}) // now try to read *past* EOF out = make([]byte, 1500) n, err = rd.ReadFull(out) if err != io.ErrUnexpectedEOF { t.Fatalf("expected error %q; got %q", io.EOF, err) } if n != 1024 { t.Fatalf("expected to read %d bytes; read %d", 1024, n) } } func TestInputOffset(t *testing.T) { bts := randomBts(1024) rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 128) if rd.InputOffset() != 0 { t.Errorf("expected offset 0; got %d", rd.InputOffset()) } // read a few bytes rd.ReadFull(make([]byte, 10)) if rd.InputOffset() != 10 { t.Errorf("expected offset 10; got %d", rd.InputOffset()) } rd.Peek(384) // peeking doesn't advance the offset if rd.InputOffset() != 10 { t.Errorf("expected offset 10; got %d", rd.InputOffset()) } rd.Next(246) if rd.InputOffset() != 256 { t.Errorf("expected offset 256; got %d", rd.InputOffset()) } rd.Skip(128) if rd.InputOffset() != 384 { t.Errorf("expected offset 384; got %d", rd.InputOffset()) } rd.ReadByte() if rd.InputOffset() != 385 { t.Errorf("expected offset 385; got %d", rd.InputOffset()) } n, _ := rd.Read(make([]byte, 128)) if rd.InputOffset() != int64(385+n) { t.Errorf("expected offset %d; got %d", 385+n, rd.InputOffset()) } rd.WriteTo(ioutil.Discard) if rd.InputOffset() != 1024 { t.Errorf("expected offset 1024; got %d", rd.InputOffset()) } // try to read more _, err := rd.Read(make([]byte, 32)) if err != io.EOF { t.Fatalf("expected error %q; got %q", io.EOF, err) } if rd.InputOffset() != 1024 { t.Errorf("expected offset 1024; got %d", rd.InputOffset()) } // reset the reader rd.Reset(bytes.NewReader(bts)) if rd.InputOffset() != 0 { t.Errorf("expected offset 0; got %d", rd.InputOffset()) } rd.Skip(768 + 32) if rd.InputOffset() != 800 { t.Errorf("expected offset 800; got %d", rd.InputOffset()) } rd.WriteTo(ioutil.Discard) if rd.InputOffset() != 1024 { t.Errorf("expected offset 1024; got %d", rd.InputOffset()) } } type readCounter struct { r io.Reader count int } func (r *readCounter) Read(p []byte) (int, error) { r.count++ return r.r.Read(p) } func TestReadFullPerf(t *testing.T) { const size = 1 << 22 data := randomBts(size) c := readCounter{ r: &partialReader{ r: bytes.NewReader(data), }, } r := NewReader(&c) const segments = 4 out := make([]byte, size/segments) for i := 0; i < segments; i++ { // force an unaligned read _, err := r.Peek(5) if err != nil { t.Fatal(err) } n, err := r.ReadFull(out) if err != nil { t.Fatal(err) } if n != size/segments { t.Fatalf("read %d bytes, not %d", n, size/segments) } } t.Logf("called Read() on the underlying reader %d times to fill %d buffers", c.count, size/r.BufferSize()) } func TestReaderBufCreation(t *testing.T) { tests := []struct { name string buffer []byte size int }{ {name: "nil", buffer: nil, size: minReaderSize}, {name: "empty", buffer: []byte{}, size: minReaderSize}, {name: "allocated", buffer: make([]byte, 0, 200), size: 200}, {name: "filled", buffer: make([]byte, 200), size: 200}, } for _, test := range tests { var b bytes.Buffer r := NewReaderBuf(&b, test.buffer) if r.BufferSize() != test.size { t.Errorf("%s: unequal buffer size (got: %d, expected: %d)", test.name, r.BufferSize(), test.size) } if r.Buffered() != 0 { t.Errorf("%s: unequal buffered bytes (got: %d, expected: 0)", test.name, r.Buffered()) } } } fwd-master/writer.go000066400000000000000000000116361467204215200150040ustar00rootroot00000000000000package fwd import "io" const ( // DefaultWriterSize is the // default write buffer size. DefaultWriterSize = 2048 minWriterSize = minReaderSize ) // Writer is a buffered writer type Writer struct { w io.Writer // writer buf []byte // 0:len(buf) is bufered data } // NewWriter returns a new writer // that writes to 'w' and has a buffer // that is `DefaultWriterSize` bytes. func NewWriter(w io.Writer) *Writer { if wr, ok := w.(*Writer); ok { return wr } return &Writer{ w: w, buf: make([]byte, 0, DefaultWriterSize), } } // NewWriterSize returns a new writer that // writes to 'w' and has a buffer size 'n'. func NewWriterSize(w io.Writer, n int) *Writer { if wr, ok := w.(*Writer); ok && cap(wr.buf) >= n { return wr } buf := make([]byte, 0, max(n, minWriterSize)) return NewWriterBuf(w, buf) } // NewWriterBuf returns a new writer // that writes to 'w' and has 'buf' as a buffer. // 'buf' is not used when has smaller capacity than 18, // custom buffer is allocated instead. func NewWriterBuf(w io.Writer, buf []byte) *Writer { if cap(buf) < minWriterSize { buf = make([]byte, 0, minWriterSize) } buf = buf[:0] return &Writer{ w: w, buf: buf, } } // Buffered returns the number of buffered bytes // in the reader. func (w *Writer) Buffered() int { return len(w.buf) } // BufferSize returns the maximum size of the buffer. func (w *Writer) BufferSize() int { return cap(w.buf) } // Flush flushes any buffered bytes // to the underlying writer. func (w *Writer) Flush() error { l := len(w.buf) if l > 0 { n, err := w.w.Write(w.buf) // if we didn't write the whole // thing, copy the unwritten // bytes to the beginnning of the // buffer. if n < l && n > 0 { w.pushback(n) if err == nil { err = io.ErrShortWrite } } if err != nil { return err } w.buf = w.buf[:0] return nil } return nil } // Write implements `io.Writer` func (w *Writer) Write(p []byte) (int, error) { c, l, ln := cap(w.buf), len(w.buf), len(p) avail := c - l // requires flush if avail < ln { if err := w.Flush(); err != nil { return 0, err } l = len(w.buf) } // too big to fit in buffer; // write directly to w.w if c < ln { return w.w.Write(p) } // grow buf slice; copy; return w.buf = w.buf[:l+ln] return copy(w.buf[l:], p), nil } // WriteString is analogous to Write, but it takes a string. func (w *Writer) WriteString(s string) (int, error) { c, l, ln := cap(w.buf), len(w.buf), len(s) avail := c - l // requires flush if avail < ln { if err := w.Flush(); err != nil { return 0, err } l = len(w.buf) } // too big to fit in buffer; // write directly to w.w // // yes, this is unsafe. *but* // io.Writer is not allowed // to mutate its input or // maintain a reference to it, // per the spec in package io. // // plus, if the string is really // too big to fit in the buffer, then // creating a copy to write it is // expensive (and, strictly speaking, // unnecessary) if c < ln { return w.w.Write(unsafestr(s)) } // grow buf slice; copy; return w.buf = w.buf[:l+ln] return copy(w.buf[l:], s), nil } // WriteByte implements `io.ByteWriter` func (w *Writer) WriteByte(b byte) error { if len(w.buf) == cap(w.buf) { if err := w.Flush(); err != nil { return err } } w.buf = append(w.buf, b) return nil } // Next returns the next 'n' free bytes // in the write buffer, flushing the writer // as necessary. Next will return `io.ErrShortBuffer` // if 'n' is greater than the size of the write buffer. // Calls to 'next' increment the write position by // the size of the returned buffer. func (w *Writer) Next(n int) ([]byte, error) { c, l := cap(w.buf), len(w.buf) if n > c { return nil, io.ErrShortBuffer } avail := c - l if avail < n { if err := w.Flush(); err != nil { return nil, err } l = len(w.buf) } w.buf = w.buf[:l+n] return w.buf[l:], nil } // take the bytes from w.buf[n:len(w.buf)] // and put them at the beginning of w.buf, // and resize to the length of the copied segment. func (w *Writer) pushback(n int) { w.buf = w.buf[:copy(w.buf, w.buf[n:])] } // ReadFrom implements `io.ReaderFrom` func (w *Writer) ReadFrom(r io.Reader) (int64, error) { // anticipatory flush if err := w.Flush(); err != nil { return 0, err } w.buf = w.buf[0:cap(w.buf)] // expand buffer var nn int64 // written var err error // error var x int // read // 1:1 reads and writes for err == nil { x, err = r.Read(w.buf) if x > 0 { n, werr := w.w.Write(w.buf[:x]) nn += int64(n) if err != nil { if n < x && n > 0 { w.pushback(n - x) } return nn, werr } if n < x { w.pushback(n - x) return nn, io.ErrShortWrite } } else if err == nil { err = io.ErrNoProgress break } } if err != io.EOF { return nn, err } // we only clear here // because we are sure // the writes have // succeeded. otherwise, // we retain the data in case // future writes succeed. w.buf = w.buf[0:0] return nn, nil } fwd-master/writer_appengine.go000066400000000000000000000001541467204215200170230ustar00rootroot00000000000000//go:build appengine // +build appengine package fwd func unsafestr(s string) []byte { return []byte(s) } fwd-master/writer_test.go000066400000000000000000000124251467204215200160400ustar00rootroot00000000000000package fwd import ( "bytes" "io" "math/rand" "testing" ) type chunkedWriter struct { w *Writer } // writes 'p' in randomly-sized chunks func (c chunkedWriter) Write(p []byte) (int, error) { l := len(p) n := 0 for n < l { amt := max(rand.Intn(l-n), 1) // number of bytes to write; at least 1 nn, err := c.w.Write(p[n : n+amt]) // n += nn if err == nil && nn < amt { err = io.ErrShortWrite } if err != nil { return n, err } } return n, nil } // analogous to Write(), but w/ str func (c chunkedWriter) WriteString(s string) (int, error) { l := len(s) n := 0 for n < l { amt := max(rand.Intn(l-n), 1) // number of bytes to write; at least 1 nn, err := c.w.WriteString(s[n : n+amt]) // n += nn if err == nil && nn < amt { err = io.ErrShortWrite } if err != nil { return n, err } } return n, nil } // writes via random calls to Next() type nextWriter struct { wr *Writer } func (c nextWriter) Write(p []byte) (int, error) { l := len(p) n := 0 for n < l { amt := max(rand.Intn(l-n), 1) // at least 1 byte fwd, err := c.wr.Next(amt) // get next (amt) bytes if err != nil { // this may happen occasionally if err == io.ErrShortBuffer { if cap(c.wr.buf) >= amt { panic("bad io.ErrShortBuffer") } continue } return n, err } if len(fwd) != amt { panic("bad Next() len") } n += copy(fwd, p[n:]) } return n, nil } func TestWrite(t *testing.T) { nbts := 4096 bts := randomBts(nbts) var buf bytes.Buffer wr := NewWriterSize(&buf, 512) if wr.BufferSize() != 512 { t.Fatalf("expected BufferSize() to be %d; found %d", 512, wr.BufferSize()) } cwr := chunkedWriter{wr} nb, err := cwr.Write(bts) if err != nil { t.Fatal(err) } if nb != nbts { t.Fatalf("expected to write %d bytes; wrote %d bytes", nbts, nb) } err = wr.Flush() if err != nil { t.Fatal(err) } if wr.Buffered() != 0 { t.Fatalf("expected 0 buffered bytes; found %d", wr.Buffered()) } if buf.Len() != nbts { t.Fatalf("wrote %d bytes, but buffer is %d bytes long", nbts, buf.Len()) } if !bytes.Equal(bts, buf.Bytes()) { t.Fatal("buf.Bytes() is not the same as the input bytes") } } func TestWriteString(t *testing.T) { nbts := 3998 str := string(randomBts(nbts)) var buf bytes.Buffer wr := NewWriterSize(&buf, 1137) if wr.BufferSize() != 1137 { t.Fatalf("expected BufferSize() to return %d; returned %d", 1137, wr.BufferSize()) } cwr := chunkedWriter{wr} nb, err := cwr.WriteString(str) if err != nil { t.Fatal(err) } if nb != nbts { t.Fatalf("expected to write %d bytes; wrote %d bytes", nbts, nb) } err = wr.Flush() if err != nil { t.Fatal(err) } if wr.Buffered() != 0 { t.Fatalf("expected 0 buffered bytes; found %d", wr.Buffered()) } if buf.Len() != nbts { t.Fatalf("wrote %d bytes, buf buffer is %d bytes long", nbts, buf.Len()) } if buf.String() != str { t.Fatal("buf.String() is not the same as input string") } } func TestWriteByte(t *testing.T) { nbts := 3200 bts := randomBts(nbts) var buf bytes.Buffer wr := NewWriter(&buf) if wr.BufferSize() != DefaultWriterSize { t.Fatalf("expected BufferSize() to return %d; returned %d", DefaultWriterSize, wr.BufferSize()) } // write byte-by-byte for _, b := range bts { if err := wr.WriteByte(b); err != nil { t.Fatal(err) } } err := wr.Flush() if err != nil { t.Fatal(err) } if buf.Len() != nbts { t.Fatalf("expected buf.Len() to be %d; got %d", nbts, buf.Len()) } if !bytes.Equal(buf.Bytes(), bts) { t.Fatal("buf.Bytes() and input are not equal") } } func TestWriterNext(t *testing.T) { nbts := 1871 bts := randomBts(nbts) var buf bytes.Buffer wr := NewWriterSize(&buf, 500) nwr := nextWriter{wr} nb, err := nwr.Write(bts) if err != nil { t.Fatal(err) } if nb != nbts { t.Fatalf("expected to write %d bytes; wrote %d", nbts, nb) } err = wr.Flush() if err != nil { t.Fatal(err) } if buf.Len() != nbts { t.Fatalf("expected buf.Len() to be %d; got %d", nbts, buf.Len()) } if !bytes.Equal(buf.Bytes(), bts) { t.Fatal("buf.Bytes() and input are not equal") } } func TestReadFrom(t *testing.T) { nbts := 2139 bts := randomBts(nbts) var buf bytes.Buffer wr := NewWriterSize(&buf, 987) rd := partialReader{bytes.NewReader(bts)} nb, err := wr.ReadFrom(rd) if err != nil { t.Fatal(err) } if nb != int64(nbts) { t.Fatalf("expeted to write %d bytes; wrote %d", nbts, nb) } err = wr.Flush() if err != nil { t.Fatal(err) } if buf.Len() != nbts { t.Fatalf("expected buf.Len() to be %d; got %d", nbts, buf.Len()) } if !bytes.Equal(buf.Bytes(), bts) { t.Fatal("buf.Bytes() and input are not equal") } } func TestWriterBufCreation(t *testing.T) { tests := []struct { name string buffer []byte size int }{ {name: "nil", buffer: nil, size: minWriterSize}, {name: "empty", buffer: []byte{}, size: minWriterSize}, {name: "allocated", buffer: make([]byte, 0, 200), size: 200}, {name: "filled", buffer: make([]byte, 200), size: 200}, } for _, test := range tests { var b bytes.Buffer w := NewWriterBuf(&b, test.buffer) if w.BufferSize() != test.size { t.Errorf("%s: unequal buffer size (got: %d, expected: %d)", test.name, w.BufferSize(), test.size) } if w.Buffered() != 0 { t.Errorf("%s: unequal buffered bytes (got: %d, expected: 0)", test.name, w.Buffered()) } } } fwd-master/writer_tinygo.go000066400000000000000000000002761467204215200163730ustar00rootroot00000000000000//go:build tinygo // +build tinygo package fwd import ( "unsafe" ) // unsafe cast string as []byte func unsafestr(b string) []byte { return unsafe.Slice(unsafe.StringData(b), len(b)) } fwd-master/writer_unsafe.go000066400000000000000000000005631467204215200163420ustar00rootroot00000000000000//go:build !appengine && !tinygo // +build !appengine,!tinygo package fwd import ( "reflect" "unsafe" ) // unsafe cast string as []byte func unsafestr(s string) []byte { var b []byte sHdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) bHdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) bHdr.Data = sHdr.Data bHdr.Len = sHdr.Len bHdr.Cap = sHdr.Len return b }