├── .gitignore ├── .travis.yml ├── Circular_Buffer_Animation.gif ├── LICENSE ├── README.md ├── example_test.go ├── go.mod ├── pipe.go ├── pipe_test.go ├── ring_buffer.go ├── ring_buffer_benchmark_test.go └── ring_buffer_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | 4 | go: 5 | - tip 6 | - 1.15.x 7 | - 1.16.x 8 | 9 | before_script: 10 | - go get -v github.com/smallnest/ringbuffer 11 | - go get github.com/mattn/goveralls 12 | 13 | script: 14 | - go test -v ./... 15 | - goveralls -service=travis-ci 16 | 17 | notifications: 18 | email: 19 | recipients: smallnest@gmail.com 20 | on_success: change 21 | on_failure: always -------------------------------------------------------------------------------- /Circular_Buffer_Animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallnest/ringbuffer/0da97b586904d34ba85256c88294ad045ed52b2b/Circular_Buffer_Animation.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 smallnest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ringbuffer 2 | 3 | [![License](https://img.shields.io/:license-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![GoDoc](https://godoc.org/github.com/smallnest/ringbuffer?status.png)](http://godoc.org/github.com/smallnest/ringbuffer) [![Go Report Card](https://goreportcard.com/badge/github.com/smallnest/ringbuffer)](https://goreportcard.com/report/github.com/smallnest/ringbuffer) [![coveralls](https://coveralls.io/repos/smallnest/ringbuffer/badge.svg?branch=master&service=github)](https://coveralls.io/github/smallnest/ringbuffer?branch=master) 4 | 5 | A circular buffer (ring buffer) in Go, implemented io.ReaderWriter interface 6 | 7 | [![wikipedia](Circular_Buffer_Animation.gif)](https://github.com/smallnest/ringbuffer) 8 | 9 | # Usage 10 | 11 | ```go 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | 17 | "github.com/smallnest/ringbuffer" 18 | ) 19 | 20 | func main() { 21 | rb := ringbuffer.New(1024) 22 | 23 | // write 24 | rb.Write([]byte("abcd")) 25 | fmt.Println(rb.Length()) 26 | fmt.Println(rb.Free()) 27 | 28 | // read 29 | buf := make([]byte, 4) 30 | rb.Read(buf) 31 | fmt.Println(string(buf)) 32 | } 33 | ``` 34 | 35 | It is possible to use an existing buffer with by replacing `New` with `NewBuffer`. 36 | 37 | 38 | # Blocking vs Non-blocking 39 | 40 | The default behavior of the ring buffer is non-blocking, 41 | meaning that reads and writes will return immediately with an error if the operation cannot be completed. 42 | If you want to block when reading or writing, you must enable it: 43 | 44 | ```go 45 | rb := ringbuffer.New(1024).SetBlocking(true) 46 | ``` 47 | 48 | Enabling blocking will cause the ring buffer to behave like a buffered [io.Pipe](https://pkg.go.dev/io#Pipe). 49 | 50 | Regular Reads will block until data is available, but not wait for a full buffer. 51 | Writes will block until there is space available and writes bigger than the buffer will wait for reads to make space. 52 | 53 | `TryRead` and `TryWrite` are still available for non-blocking reads and writes. 54 | 55 | To signify the end of the stream, close the ring buffer from the writer side with `rb.CloseWriter()` 56 | 57 | Either side can use `rb.CloseWithError(err error)` to signal an error and close the ring buffer. 58 | Any reads or writes will return the error on next call. 59 | 60 | In blocking mode errors are stateful and the same error will be returned until `rb.Reset()` is called. 61 | 62 | It is possible to set a deadline for blocking Read/Write operations using `rb.WithDeadline(time.Duration)`. 63 | 64 | # io.Copy replacement 65 | 66 | The ring buffer can replace `io.Copy` and `io.CopyBuffer` to do async copying through the ring buffer. 67 | 68 | The copy operation will happen directly on the buffer, so between reads and writes there is no memory copy. 69 | 70 | Here is a simple example where the copy operation is replaced by a ring buffer: 71 | 72 | ```go 73 | func saveWebsite(url, file string) { 74 | in, _ := http.Get(url) 75 | out, _ := os.Create(file) 76 | 77 | // Copy with regular buffered copy 78 | // n, err := io.Copy(out, in.Body) 79 | 80 | // Copy with ring buffer 81 | n, err := ringbuffer.New(1024).Copy(out, in.Body) 82 | fmt.Println(n, err) 83 | } 84 | ``` 85 | 86 | The ring buffer implements `io.ReaderFrom` and `io.WriterTo` interfaces, which allows to fill either or both 87 | the write and read side respectively. 88 | 89 | This will provide an async method for writing or reading directly into the ring buffer. 90 | These functions require that "blocking" is set on the pipe. 91 | 92 | Example: 93 | 94 | ```go 95 | func readWebsite(url string) io.ReadCloser { 96 | in, _ := http.Get(url) 97 | 98 | // Create blocking ring buffer 99 | ring := ringbuffer.New(1024).SetBlocking(true) 100 | 101 | // Read from the input in a goroutine into the ring buffer 102 | go func() { 103 | ring.ReadFrom(in.Body) 104 | ring.CloseWriter() 105 | }() 106 | return ring.ReadCloser() 107 | } 108 | ``` 109 | 110 | # io.Pipe replacement 111 | 112 | The ring buffer can be used as a compatible, but *asynchronous* replacement of `io.Pipe`. 113 | 114 | That means that Reads and Writes will go to the ring buffer. 115 | Writes will complete as long as the data fits within the ring buffer. 116 | 117 | Reads will attempt to satisfy reads with data from the ring buffer. 118 | The read will only block if the ring buffer is empty. 119 | 120 | In the common case, where the Read and Write side can run concurrently, 121 | it is safe to replace `io.Pipe()` with `(*Ringbuffer).Pipe()`. 122 | 123 | Compare the following to the [io.Pipe example](https://pkg.go.dev/io#example-Pipe): 124 | 125 | ```go 126 | func main() { 127 | // Create pipe from a 4KB ring buffer. 128 | r, w := ringbuffer.New(4 << 10).Pipe() 129 | 130 | go func() { 131 | fmt.Fprint(w, "some io.Reader stream to be read\n") 132 | w.Close() 133 | }() 134 | 135 | if _, err := io.Copy(os.Stdout, r); err != nil { 136 | log.Fatal(err) 137 | } 138 | } 139 | ``` 140 | 141 | When creating the pipe, the ring buffer is internally switched to blocking mode. 142 | 143 | Error reporting on Close and CloseWithError functions is similar to `io.Pipe`. 144 | 145 | It is possible to use the original ring buffer alongside the pipe functions. 146 | So for example it is possible to "seed" the ring buffer with data, 147 | so reads can complete at once. -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package ringbuffer 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "os" 8 | ) 9 | 10 | func ExampleRingBuffer() { 11 | rb := New(1024) 12 | rb.Write([]byte("abcd")) 13 | fmt.Println(rb.Length()) 14 | fmt.Println(rb.Free()) 15 | buf := make([]byte, 4) 16 | 17 | rb.Read(buf) 18 | fmt.Println(string(buf)) 19 | // Output: 4 20 | // 1020 21 | // abcd 22 | } 23 | 24 | func ExampleRingBuffer_Pipe() { 25 | // Create pipe from a 4KB ring buffer. 26 | r, w := New(4 << 10).Pipe() 27 | 28 | go func() { 29 | fmt.Fprint(w, "some io.Reader stream to be read\n") 30 | w.Close() 31 | }() 32 | 33 | if _, err := io.Copy(os.Stdout, r); err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | // Output: 38 | // some io.Reader stream to be read 39 | } 40 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/smallnest/ringbuffer 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /pipe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 smallnest. All rights reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ringbuffer 6 | 7 | import "io" 8 | 9 | // Pipe creates an asynchronous in-memory pipe compatible with io.Pipe 10 | // It can be used to connect code expecting an [io.Reader] 11 | // with code expecting an [io.Writer]. 12 | // 13 | // Reads and Writes will go to the ring buffer. 14 | // Writes will complete as long as the data fits within the ring buffer. 15 | // Reads will attempt to satisfy reads with data from the ring buffer. 16 | // Only if the ring buffer is empty will the read block. 17 | // 18 | // It is safe (and intended) to call Read and Write in parallel with each other or with Close. 19 | func (r *RingBuffer) Pipe() (*PipeReader, *PipeWriter) { 20 | r.SetBlocking(true) 21 | pr := PipeReader{pipe: r} 22 | return &pr, &PipeWriter{pipe: r} 23 | } 24 | 25 | // A PipeReader is the read half of a pipe. 26 | type PipeReader struct{ pipe *RingBuffer } 27 | 28 | // Read implements the standard Read interface: 29 | // it reads data from the pipe, blocking until a writer 30 | // arrives or the write end is closed. 31 | // If the write end is closed with an error, that error is 32 | // returned as err; otherwise err is io.EOF. 33 | func (r *PipeReader) Read(data []byte) (n int, err error) { 34 | return r.pipe.Read(data) 35 | } 36 | 37 | // Close closes the reader; subsequent writes to the 38 | // write half of the pipe will return the error [io.ErrClosedPipe]. 39 | func (r *PipeReader) Close() error { 40 | r.pipe.setErr(io.ErrClosedPipe, false) 41 | return nil 42 | } 43 | 44 | // CloseWithError closes the reader; subsequent writes 45 | // to the write half of the pipe will return the error err. 46 | // 47 | // CloseWithError never overwrites the previous error if it exists 48 | // and always returns nil. 49 | func (r *PipeReader) CloseWithError(err error) error { 50 | if err == nil { 51 | return r.Close() 52 | } 53 | r.pipe.setErr(err, false) 54 | return nil 55 | } 56 | 57 | // A PipeWriter is the write half of a pipe. 58 | type PipeWriter struct{ pipe *RingBuffer } 59 | 60 | // Write implements the standard Write interface: 61 | // it writes data to the pipe. 62 | // The Write will block until all data has been written to the ring buffer. 63 | // If the read end is closed with an error, that err is 64 | // returned as err; otherwise err is [io.ErrClosedPipe]. 65 | func (w *PipeWriter) Write(data []byte) (n int, err error) { 66 | if n, err = w.pipe.Write(data); err == ErrWriteOnClosed { 67 | // Replace error. 68 | err = io.ErrClosedPipe 69 | } 70 | return n, err 71 | } 72 | 73 | // Close closes the writer; subsequent reads from the 74 | // read half of the pipe will return no bytes and EOF. 75 | func (w *PipeWriter) Close() error { 76 | w.pipe.setErr(io.EOF, false) 77 | return nil 78 | } 79 | 80 | // CloseWithError closes the writer; subsequent reads from the 81 | // read half of the pipe will return no bytes and the error err, 82 | // or EOF if err is nil. 83 | // 84 | // CloseWithError never overwrites the previous error if it exists 85 | // and always returns nil. 86 | func (w *PipeWriter) CloseWithError(err error) error { 87 | if err == nil { 88 | return w.Close() 89 | } 90 | w.pipe.setErr(err, false) 91 | return nil 92 | } 93 | -------------------------------------------------------------------------------- /pipe_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Adapted from stdlib tests to check compatibility. 6 | 7 | package ringbuffer 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "io" 13 | "sort" 14 | "strings" 15 | "testing" 16 | "time" 17 | ) 18 | 19 | func checkWrite(t *testing.T, w io.Writer, data []byte, c chan int) { 20 | n, err := w.Write(data) 21 | if err != nil { 22 | t.Errorf("write: %v", err) 23 | } 24 | if n != len(data) { 25 | t.Errorf("short write: %d != %d", n, len(data)) 26 | } 27 | c <- 0 28 | } 29 | 30 | // Test a single read/write pair. 31 | func TestPipe1(t *testing.T) { 32 | c := make(chan int) 33 | r, w := New(256).Pipe() 34 | var buf = make([]byte, 64) 35 | go checkWrite(t, w, []byte("hello, world"), c) 36 | n, err := r.Read(buf) 37 | if err != nil { 38 | t.Errorf("read: %v", err) 39 | } else if n != 12 || string(buf[0:12]) != "hello, world" { 40 | t.Errorf("bad read: got %q", buf[0:n]) 41 | } 42 | <-c 43 | r.Close() 44 | w.Close() 45 | } 46 | 47 | func reader(t *testing.T, r io.Reader, c chan int) { 48 | var buf = make([]byte, 64) 49 | for { 50 | n, err := r.Read(buf) 51 | if err == io.EOF { 52 | c <- 0 53 | break 54 | } 55 | if err != nil { 56 | t.Errorf("read: %v", err) 57 | } 58 | c <- n 59 | } 60 | } 61 | 62 | // Test a sequence of read/write pairs. 63 | func TestPipe2(t *testing.T) { 64 | c := make(chan int) 65 | r, w := New(256).Pipe() 66 | go reader(t, r, c) 67 | var buf = make([]byte, 64) 68 | for i := 0; i < 5; i++ { 69 | p := buf[0 : 5+i*10] 70 | n, err := w.Write(p) 71 | if n != len(p) { 72 | t.Errorf("wrote %d, got %d", len(p), n) 73 | } 74 | if err != nil { 75 | t.Errorf("write: %v", err) 76 | } 77 | nn := <-c 78 | if nn != n { 79 | t.Errorf("wrote %d, read got %d", n, nn) 80 | } 81 | } 82 | w.Close() 83 | nn := <-c 84 | if nn != 0 { 85 | t.Errorf("final read got %d", nn) 86 | } 87 | } 88 | 89 | type pipeReturn struct { 90 | n int 91 | err error 92 | } 93 | 94 | // Test a large write that requires multiple reads to satisfy. 95 | func writer(w io.WriteCloser, buf []byte, c chan pipeReturn) { 96 | n, err := w.Write(buf) 97 | w.Close() 98 | c <- pipeReturn{n, err} 99 | } 100 | 101 | func TestPipe3(t *testing.T) { 102 | c := make(chan pipeReturn) 103 | r, w := New(256).Pipe() 104 | var wdat = make([]byte, 128) 105 | for i := 0; i < len(wdat); i++ { 106 | wdat[i] = byte(i) 107 | } 108 | go writer(w, wdat, c) 109 | var rdat = make([]byte, 1024) 110 | tot := 0 111 | for n := 1; n <= 256; n *= 2 { 112 | nn, err := r.Read(rdat[tot : tot+n]) 113 | if err != nil && err != io.EOF { 114 | t.Fatalf("read: %v", err) 115 | } 116 | 117 | // only final two reads should be short - 1 byte, then 0 118 | expect := n 119 | if n == 128 { 120 | expect = 1 121 | } else if n == 256 { 122 | expect = 0 123 | if err != io.EOF { 124 | t.Fatalf("read at end: %v", err) 125 | } 126 | } 127 | if nn != expect { 128 | t.Fatalf("read %d, expected %d, got %d", n, expect, nn) 129 | } 130 | tot += nn 131 | } 132 | pr := <-c 133 | if pr.n != 128 || pr.err != nil { 134 | t.Fatalf("write 128: %d, %v", pr.n, pr.err) 135 | } 136 | if tot != 128 { 137 | t.Fatalf("total read %d != 128", tot) 138 | } 139 | for i := 0; i < 128; i++ { 140 | if rdat[i] != byte(i) { 141 | t.Fatalf("rdat[%d] = %d", i, rdat[i]) 142 | } 143 | } 144 | } 145 | 146 | // Test read after/before writer close. 147 | 148 | type closer interface { 149 | CloseWithError(error) error 150 | Close() error 151 | } 152 | 153 | type pipeTest struct { 154 | async bool 155 | err error 156 | closeWithError bool 157 | } 158 | 159 | func (p pipeTest) String() string { 160 | return fmt.Sprintf("async=%v err=%v closeWithError=%v", p.async, p.err, p.closeWithError) 161 | } 162 | 163 | var pipeTests = []pipeTest{ 164 | {true, nil, false}, 165 | {true, nil, true}, 166 | {true, io.ErrShortWrite, true}, 167 | {false, nil, false}, 168 | {false, nil, true}, 169 | {false, io.ErrShortWrite, true}, 170 | } 171 | 172 | func delayClose(t *testing.T, cl closer, ch chan int, tt pipeTest) { 173 | time.Sleep(1 * time.Millisecond) 174 | var err error 175 | if tt.closeWithError { 176 | err = cl.CloseWithError(tt.err) 177 | } else { 178 | err = cl.Close() 179 | } 180 | if err != nil { 181 | t.Errorf("delayClose: %v", err) 182 | } 183 | ch <- 0 184 | } 185 | 186 | func TestPipeReadClose(t *testing.T) { 187 | for _, tt := range pipeTests { 188 | t.Logf("%+v", tt) 189 | c := make(chan int, 1) 190 | r, w := New(256).Pipe() 191 | if tt.async { 192 | go delayClose(t, w, c, tt) 193 | } else { 194 | delayClose(t, w, c, tt) 195 | } 196 | var buf = make([]byte, 64) 197 | n, err := r.Read(buf) 198 | <-c 199 | want := tt.err 200 | if want == nil { 201 | want = io.EOF 202 | } 203 | if err != want { 204 | t.Errorf("read from closed pipe: %v want %v", err, want) 205 | } 206 | if n != 0 { 207 | t.Errorf("read on closed pipe returned %d", n) 208 | } 209 | if err = r.Close(); err != nil { 210 | t.Errorf("r.Close: %v", err) 211 | } 212 | } 213 | } 214 | 215 | // Test close on Read side during Read. 216 | func TestPipeReadClose2(t *testing.T) { 217 | c := make(chan int, 1) 218 | r, _ := New(256).Pipe() 219 | go delayClose(t, r, c, pipeTest{}) 220 | n, err := r.Read(make([]byte, 64)) 221 | <-c 222 | if n != 0 || err != io.ErrClosedPipe { 223 | t.Errorf("read from closed pipe: %v, %v want %v, %v", n, err, 0, io.ErrClosedPipe) 224 | } 225 | } 226 | 227 | // Test write after/before reader close. 228 | 229 | func TestPipeWriteClose(t *testing.T) { 230 | for _, tt := range pipeTests { 231 | c := make(chan int, 1) 232 | r, w := New(256).Pipe() 233 | if tt.async { 234 | go delayClose(t, r, c, tt) 235 | } else { 236 | delayClose(t, r, c, tt) 237 | } 238 | <-c 239 | n, err := io.WriteString(w, "hello, world") 240 | expect := tt.err 241 | if expect == nil { 242 | expect = io.ErrClosedPipe 243 | } 244 | if err != expect { 245 | t.Errorf("write on closed pipe: %v want %v", err, expect) 246 | } 247 | if n != 0 { 248 | t.Errorf("write on closed pipe returned %d", n) 249 | } 250 | if err = w.Close(); err != nil { 251 | t.Errorf("w.Close: %v", err) 252 | } 253 | } 254 | } 255 | 256 | // Test close on Write side during Write. 257 | func TestPipeWriteClose2(t *testing.T) { 258 | c := make(chan int, 1) 259 | _, w := New(256).Pipe() 260 | go delayClose(t, w, c, pipeTest{}) 261 | <-c 262 | n, err := w.Write(make([]byte, 64)) 263 | if n != 0 || err != io.ErrClosedPipe { 264 | t.Errorf("write to closed pipe: %v, %v want %v, %v", n, err, 0, io.ErrClosedPipe) 265 | } 266 | } 267 | 268 | func TestWriteEmpty(t *testing.T) { 269 | r, w := New(256).Pipe() 270 | go func() { 271 | w.Write([]byte{}) 272 | w.Close() 273 | }() 274 | var b [2]byte 275 | io.ReadFull(r, b[0:2]) 276 | r.Close() 277 | } 278 | 279 | func TestWriteNil(t *testing.T) { 280 | r, w := New(256).Pipe() 281 | go func() { 282 | w.Write(nil) 283 | w.Close() 284 | }() 285 | var b [2]byte 286 | io.ReadFull(r, b[0:2]) 287 | r.Close() 288 | } 289 | 290 | func TestPipeWriteAfterWriterClose(t *testing.T) { 291 | r, w := io.Pipe() 292 | 293 | done := make(chan bool) 294 | var writeErr error 295 | go func() { 296 | _, err := w.Write([]byte("hello")) 297 | if err != nil { 298 | t.Errorf("got error: %q; expected none", err) 299 | } 300 | w.Close() 301 | _, writeErr = w.Write([]byte("world")) 302 | done <- true 303 | }() 304 | 305 | buf := make([]byte, 100) 306 | var result string 307 | n, err := io.ReadFull(r, buf) 308 | if err != nil && err != io.ErrUnexpectedEOF { 309 | t.Fatalf("got: %q; want: %q", err, io.ErrUnexpectedEOF) 310 | } 311 | result = string(buf[0:n]) 312 | <-done 313 | 314 | if result != "hello" { 315 | t.Errorf("got: %q; want: %q", result, "hello") 316 | } 317 | if writeErr != io.ErrClosedPipe { 318 | t.Errorf("got: %q; want: %q", writeErr, io.ErrClosedPipe) 319 | } 320 | } 321 | 322 | func TestPipeCloseError(t *testing.T) { 323 | type testError1 struct{ error } 324 | type testError2 struct{ error } 325 | 326 | r, w := New(256).Pipe() 327 | r.CloseWithError(testError1{}) 328 | if _, err := w.Write(nil); err != (testError1{}) { 329 | t.Errorf("Write error: got %T, want testError1", err) 330 | } 331 | r.CloseWithError(testError2{}) 332 | if _, err := w.Write(nil); err != (testError1{}) { 333 | t.Errorf("Write error: got %T, want testError1", err) 334 | } 335 | 336 | r, w = New(256).Pipe() 337 | w.CloseWithError(testError1{}) 338 | if _, err := r.Read(nil); err != (testError1{}) { 339 | t.Errorf("Read error: got %T, want testError1", err) 340 | } 341 | w.CloseWithError(testError2{}) 342 | if _, err := r.Read(nil); err != (testError1{}) { 343 | t.Errorf("Read error: got %T, want testError1", err) 344 | } 345 | } 346 | 347 | func TestPipeConcurrent(t *testing.T) { 348 | const ( 349 | input = "0123456789abcdef" 350 | count = 8 351 | readSize = 2 352 | ) 353 | 354 | t.Run("Write", func(t *testing.T) { 355 | r, w := New(256).Pipe() 356 | 357 | for i := 0; i < count; i++ { 358 | go func() { 359 | time.Sleep(time.Millisecond) // Increase probability of race 360 | if n, err := w.Write([]byte(input)); n != len(input) || err != nil { 361 | t.Errorf("Write() = (%d, %v); want (%d, nil)", n, err, len(input)) 362 | } 363 | }() 364 | } 365 | 366 | buf := make([]byte, count*len(input)) 367 | for i := 0; i < len(buf); i += readSize { 368 | if n, err := r.Read(buf[i : i+readSize]); n != readSize || err != nil { 369 | t.Errorf("Read() = (%d, %v); want (%d, nil)", n, err, readSize) 370 | } 371 | } 372 | 373 | // Since each Write is fully gated, if multiple Read calls were needed, 374 | // the contents of Write should still appear together in the output. 375 | got := string(buf) 376 | want := strings.Repeat(input, count) 377 | if got != want { 378 | t.Errorf("got: %q; want: %q", got, want) 379 | } 380 | }) 381 | 382 | t.Run("Read", func(t *testing.T) { 383 | r, w := New(256).Pipe() 384 | 385 | c := make(chan []byte, count*len(input)/readSize) 386 | for i := 0; i < cap(c); i++ { 387 | go func() { 388 | time.Sleep(time.Millisecond) // Increase probability of race 389 | buf := make([]byte, readSize) 390 | if n, err := r.Read(buf); n != readSize || err != nil { 391 | t.Errorf("Read() = (%d, %v); want (%d, nil)", n, err, readSize) 392 | } 393 | c <- buf 394 | }() 395 | } 396 | 397 | for i := 0; i < count; i++ { 398 | if n, err := w.Write([]byte(input)); n != len(input) || err != nil { 399 | t.Errorf("Write() = (%d, %v); want (%d, nil)", n, err, len(input)) 400 | } 401 | } 402 | 403 | // Since each read is independent, the only guarantee about the output 404 | // is that it is a permutation of the input in readSized groups. 405 | got := make([]byte, 0, count*len(input)) 406 | for i := 0; i < cap(c); i++ { 407 | got = append(got, (<-c)...) 408 | } 409 | got = sortBytesInGroups(got, readSize) 410 | want := bytes.Repeat([]byte(input), count) 411 | want = sortBytesInGroups(want, readSize) 412 | if string(got) != string(want) { 413 | t.Errorf("got: %q; want: %q", got, want) 414 | } 415 | }) 416 | } 417 | 418 | func sortBytesInGroups(b []byte, n int) []byte { 419 | var groups [][]byte 420 | for len(b) > 0 { 421 | groups = append(groups, b[:n]) 422 | b = b[n:] 423 | } 424 | sort.Slice(groups, func(i, j int) bool { return bytes.Compare(groups[i], groups[j]) < 0 }) 425 | return bytes.Join(groups, nil) 426 | } 427 | -------------------------------------------------------------------------------- /ring_buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 smallnest. All rights reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ringbuffer 6 | 7 | import ( 8 | "context" 9 | "errors" 10 | "io" 11 | "sync" 12 | "time" 13 | "unsafe" 14 | ) 15 | 16 | var ( 17 | // ErrTooMuchDataToWrite is returned when the data to write is more than the buffer size. 18 | ErrTooMuchDataToWrite = errors.New("too much data to write") 19 | 20 | // ErrIsFull is returned when the buffer is full and not blocking. 21 | ErrIsFull = errors.New("ringbuffer is full") 22 | 23 | // ErrIsEmpty is returned when the buffer is empty and not blocking. 24 | ErrIsEmpty = errors.New("ringbuffer is empty") 25 | 26 | // ErrIsNotEmpty is returned when the buffer is not empty and not blocking. 27 | ErrIsNotEmpty = errors.New("ringbuffer is not empty") 28 | 29 | // ErrAcquireLock is returned when the lock is not acquired on Try operations. 30 | ErrAcquireLock = errors.New("unable to acquire lock") 31 | 32 | // ErrWriteOnClosed is returned when write on a closed ringbuffer. 33 | ErrWriteOnClosed = errors.New("write on closed ringbuffer") 34 | 35 | // ErrReaderClosed is returned when a ReadClosed closed the ringbuffer. 36 | ErrReaderClosed = errors.New("reader closed") 37 | ) 38 | 39 | // RingBuffer is a circular buffer that implements io.ReaderWriter interface. 40 | // It operates like a buffered pipe, where data is written to a RingBuffer 41 | // and can be read back from another goroutine. 42 | // It is safe to concurrently read and write RingBuffer. 43 | type RingBuffer struct { 44 | buf []byte 45 | size int 46 | r int // next position to read 47 | w int // next position to write 48 | isFull bool 49 | err error 50 | block bool 51 | rTimeout time.Duration // Applies to writes (waits for the read condition) 52 | wTimeout time.Duration // Applies to read (wait for the write condition) 53 | mu sync.Mutex 54 | wg sync.WaitGroup 55 | readCond *sync.Cond // Signaled when data has been read. 56 | writeCond *sync.Cond // Signaled when data has been written. 57 | } 58 | 59 | // New returns a new RingBuffer whose buffer has the given size. 60 | func New(size int) *RingBuffer { 61 | return &RingBuffer{ 62 | buf: make([]byte, size), 63 | size: size, 64 | } 65 | } 66 | 67 | // NewBuffer returns a new RingBuffer whose buffer is provided. 68 | func NewBuffer(b []byte) *RingBuffer { 69 | return &RingBuffer{ 70 | buf: b, 71 | size: len(b), 72 | } 73 | } 74 | 75 | // SetBlocking sets the blocking mode of the ring buffer. 76 | // If block is true, Read and Write will block when there is no data to read or no space to write. 77 | // If block is false, Read and Write will return ErrIsEmpty or ErrIsFull immediately. 78 | // By default, the ring buffer is not blocking. 79 | // This setting should be called before any Read or Write operation or after a Reset. 80 | func (r *RingBuffer) SetBlocking(block bool) *RingBuffer { 81 | r.block = block 82 | if block { 83 | r.readCond = sync.NewCond(&r.mu) 84 | r.writeCond = sync.NewCond(&r.mu) 85 | } 86 | return r 87 | } 88 | 89 | // WithCancel sets a context to cancel the ring buffer. 90 | // When the context is canceled, the ring buffer will be closed with the context error. 91 | // A goroutine will be started and run until the provided context is canceled. 92 | func (r *RingBuffer) WithCancel(ctx context.Context) *RingBuffer { 93 | go func() { 94 | select { 95 | case <-ctx.Done(): 96 | r.CloseWithError(ctx.Err()) 97 | } 98 | }() 99 | return r 100 | } 101 | 102 | // WithTimeout will set a blocking read/write timeout. 103 | // If no reads or writes occur within the timeout, 104 | // the ringbuffer will be closed and context.DeadlineExceeded will be returned. 105 | // A timeout of 0 or less will disable timeouts (default). 106 | func (r *RingBuffer) WithTimeout(d time.Duration) *RingBuffer { 107 | r.mu.Lock() 108 | r.rTimeout = d 109 | r.wTimeout = d 110 | r.mu.Unlock() 111 | return r 112 | } 113 | 114 | // WithReadTimeout will set a blocking read timeout. 115 | // Reads refers to any call that reads data from the buffer. 116 | // If no writes occur within the timeout, 117 | // the ringbuffer will be closed and context.DeadlineExceeded will be returned. 118 | // A timeout of 0 or less will disable timeouts (default). 119 | func (r *RingBuffer) WithReadTimeout(d time.Duration) *RingBuffer { 120 | r.mu.Lock() 121 | // Read operations wait for writes to complete, 122 | // therefore we set the wTimeout. 123 | r.wTimeout = d 124 | r.mu.Unlock() 125 | return r 126 | } 127 | 128 | // WithWriteTimeout will set a blocking write timeout. 129 | // Write refers to any call that writes data into the buffer. 130 | // If no reads occur within the timeout, 131 | // the ringbuffer will be closed and context.DeadlineExceeded will be returned. 132 | // A timeout of 0 or less will disable timeouts (default). 133 | func (r *RingBuffer) WithWriteTimeout(d time.Duration) *RingBuffer { 134 | r.mu.Lock() 135 | // Write operations wait for reads to complete, 136 | // therefore we set the rTimeout. 137 | r.rTimeout = d 138 | r.mu.Unlock() 139 | return r 140 | } 141 | 142 | func (r *RingBuffer) setErr(err error, locked bool) error { 143 | if !locked { 144 | r.mu.Lock() 145 | defer r.mu.Unlock() 146 | } 147 | if r.err != nil && r.err != io.EOF { 148 | return r.err 149 | } 150 | 151 | switch err { 152 | // Internal errors are transient 153 | case nil, ErrIsEmpty, ErrIsFull, ErrAcquireLock, ErrTooMuchDataToWrite, ErrIsNotEmpty: 154 | return err 155 | default: 156 | r.err = err 157 | if r.block { 158 | r.readCond.Broadcast() 159 | r.writeCond.Broadcast() 160 | } 161 | } 162 | return err 163 | } 164 | 165 | func (r *RingBuffer) readErr(locked bool) error { 166 | if !locked { 167 | r.mu.Lock() 168 | defer r.mu.Unlock() 169 | } 170 | if r.err != nil { 171 | if r.err == io.EOF { 172 | if r.w == r.r && !r.isFull { 173 | return io.EOF 174 | } 175 | return nil 176 | } 177 | return r.err 178 | } 179 | return nil 180 | } 181 | 182 | // Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. 183 | // Even if Read returns n < len(p), it may use all of p as scratch space during the call. 184 | // If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more. 185 | // When Read encounters an error or end-of-file condition after successfully reading n > 0 bytes, it returns the number of bytes read. 186 | // It may return the (non-nil) error from the same call or return the error (and n == 0) from a subsequent call. 187 | // Callers should always process the n > 0 bytes returned before considering the error err. 188 | // Doing so correctly handles I/O errors that happen after reading some bytes and also both of the allowed EOF behaviors. 189 | func (r *RingBuffer) Read(p []byte) (n int, err error) { 190 | if len(p) == 0 { 191 | return 0, r.readErr(false) 192 | } 193 | 194 | r.mu.Lock() 195 | defer r.mu.Unlock() 196 | if err := r.readErr(true); err != nil { 197 | return 0, err 198 | } 199 | 200 | r.wg.Add(1) 201 | defer r.wg.Done() 202 | n, err = r.read(p) 203 | for err == ErrIsEmpty && r.block { 204 | if !r.waitWrite() { 205 | return 0, context.DeadlineExceeded 206 | } 207 | if err = r.readErr(true); err != nil { 208 | break 209 | } 210 | n, err = r.read(p) 211 | } 212 | if r.block && n > 0 { 213 | r.readCond.Broadcast() 214 | } 215 | return n, err 216 | } 217 | 218 | // TryRead read up to len(p) bytes into p like Read, but it is never blocking. 219 | // If it does not succeed to acquire the lock, it returns ErrAcquireLock. 220 | func (r *RingBuffer) TryRead(p []byte) (n int, err error) { 221 | ok := r.mu.TryLock() 222 | if !ok { 223 | return 0, ErrAcquireLock 224 | } 225 | defer r.mu.Unlock() 226 | if err := r.readErr(true); err != nil { 227 | return 0, err 228 | } 229 | if len(p) == 0 { 230 | return 0, r.readErr(true) 231 | } 232 | 233 | n, err = r.read(p) 234 | if r.block && n > 0 { 235 | r.readCond.Broadcast() 236 | } 237 | return n, err 238 | } 239 | 240 | func (r *RingBuffer) read(p []byte) (n int, err error) { 241 | if r.w == r.r && !r.isFull { 242 | return 0, ErrIsEmpty 243 | } 244 | 245 | if r.w > r.r { 246 | n = r.w - r.r 247 | if n > len(p) { 248 | n = len(p) 249 | } 250 | copy(p, r.buf[r.r:r.r+n]) 251 | r.r = (r.r + n) % r.size 252 | return 253 | } 254 | 255 | n = r.size - r.r + r.w 256 | if n > len(p) { 257 | n = len(p) 258 | } 259 | 260 | if r.r+n <= r.size { 261 | copy(p, r.buf[r.r:r.r+n]) 262 | } else { 263 | c1 := r.size - r.r 264 | copy(p, r.buf[r.r:r.size]) 265 | c2 := n - c1 266 | copy(p[c1:], r.buf[0:c2]) 267 | } 268 | r.r = (r.r + n) % r.size 269 | 270 | r.isFull = false 271 | 272 | return n, r.readErr(true) 273 | } 274 | 275 | // Returns true if a read may have happened. 276 | // Returns false if waited longer than rTimeout. 277 | // Must be called when locked and returns locked. 278 | func (r *RingBuffer) waitRead() (ok bool) { 279 | if r.rTimeout <= 0 { 280 | r.readCond.Wait() 281 | return true 282 | } 283 | start := time.Now() 284 | defer time.AfterFunc(r.rTimeout, r.readCond.Broadcast).Stop() 285 | 286 | r.readCond.Wait() 287 | if time.Since(start) >= r.rTimeout { 288 | r.setErr(context.DeadlineExceeded, true) 289 | return false 290 | } 291 | return true 292 | } 293 | 294 | // ReadByte reads and returns the next byte from the input or ErrIsEmpty. 295 | func (r *RingBuffer) ReadByte() (b byte, err error) { 296 | r.mu.Lock() 297 | defer r.mu.Unlock() 298 | if err = r.readErr(true); err != nil { 299 | return 0, err 300 | } 301 | for r.w == r.r && !r.isFull { 302 | if r.block { 303 | if !r.waitWrite() { 304 | return 0, context.DeadlineExceeded 305 | } 306 | err = r.readErr(true) 307 | if err != nil { 308 | return 0, err 309 | } 310 | continue 311 | } 312 | return 0, ErrIsEmpty 313 | } 314 | b = r.buf[r.r] 315 | r.r++ 316 | if r.r == r.size { 317 | r.r = 0 318 | } 319 | 320 | r.isFull = false 321 | return b, r.readErr(true) 322 | } 323 | 324 | // Write writes len(p) bytes from p to the underlying buf. 325 | // It returns the number of bytes written from p (0 <= n <= len(p)) 326 | // and any error encountered that caused the write to stop early. 327 | // If blocking n < len(p) will be returned only if an error occurred. 328 | // Write returns a non-nil error if it returns n < len(p). 329 | // Write will not modify the slice data, even temporarily. 330 | func (r *RingBuffer) Write(p []byte) (n int, err error) { 331 | if len(p) == 0 { 332 | return 0, r.setErr(nil, false) 333 | } 334 | r.mu.Lock() 335 | defer r.mu.Unlock() 336 | if err := r.err; err != nil { 337 | if err == io.EOF { 338 | err = ErrWriteOnClosed 339 | } 340 | return 0, err 341 | } 342 | wrote := 0 343 | for len(p) > 0 { 344 | n, err = r.write(p) 345 | wrote += n 346 | if !r.block || err == nil { 347 | break 348 | } 349 | err = r.setErr(err, true) 350 | if r.block && (err == ErrIsFull || err == ErrTooMuchDataToWrite) { 351 | r.writeCond.Broadcast() 352 | r.waitRead() 353 | p = p[n:] 354 | err = nil 355 | continue 356 | } 357 | break 358 | } 359 | if r.block && wrote > 0 { 360 | r.writeCond.Broadcast() 361 | } 362 | 363 | return wrote, r.setErr(err, true) 364 | } 365 | 366 | // waitWrite will wait for a write event. 367 | // Returns true if a write may have happened. 368 | // Returns false if waited longer than wTimeout. 369 | // Must be called when locked and returns locked. 370 | func (r *RingBuffer) waitWrite() (ok bool) { 371 | if r.wTimeout <= 0 { 372 | r.writeCond.Wait() 373 | return true 374 | } 375 | 376 | start := time.Now() 377 | defer time.AfterFunc(r.wTimeout, r.writeCond.Broadcast).Stop() 378 | 379 | r.writeCond.Wait() 380 | if time.Since(start) >= r.wTimeout { 381 | r.setErr(context.DeadlineExceeded, true) 382 | return false 383 | } 384 | return true 385 | } 386 | 387 | // ReadFrom will fulfill the write side of the ringbuffer. 388 | // This will do writes directly into the buffer, 389 | // therefore avoiding a mem-copy when using the Write. 390 | // 391 | // ReadFrom will not automatically close the buffer even after returning. 392 | // For that call CloseWriter(). 393 | // 394 | // ReadFrom reads data from r until EOF or error. 395 | // The return value n is the number of bytes read. 396 | // Any error except EOF encountered during the read is also returned, 397 | // and the error will cause the Read side to fail as well. 398 | // ReadFrom only available in blocking mode. 399 | func (r *RingBuffer) ReadFrom(rd io.Reader) (n int64, err error) { 400 | if !r.block { 401 | return 0, errors.New("RingBuffer: ReadFrom only available in blocking mode") 402 | } 403 | zeroReads := 0 404 | r.mu.Lock() 405 | defer r.mu.Unlock() 406 | for { 407 | if err = r.readErr(true); err != nil { 408 | return n, err 409 | } 410 | if r.isFull { 411 | // Wait for a read 412 | if !r.waitRead() { 413 | return 0, context.DeadlineExceeded 414 | } 415 | continue 416 | } 417 | 418 | var toRead []byte 419 | if r.w >= r.r { 420 | // After reader, read until end of buffer 421 | toRead = r.buf[r.w:] 422 | } else { 423 | // Before reader, read until reader. 424 | toRead = r.buf[r.w:r.r] 425 | } 426 | // Unlock while reading 427 | r.mu.Unlock() 428 | nr, rerr := rd.Read(toRead) 429 | r.mu.Lock() 430 | if rerr != nil && rerr != io.EOF { 431 | err = r.setErr(rerr, true) 432 | break 433 | } 434 | if nr == 0 && rerr == nil { 435 | zeroReads++ 436 | if zeroReads >= 100 { 437 | err = r.setErr(io.ErrNoProgress, true) 438 | } 439 | continue 440 | } 441 | zeroReads = 0 442 | r.w += nr 443 | if r.w == r.size { 444 | r.w = 0 445 | } 446 | r.isFull = r.r == r.w && nr > 0 447 | n += int64(nr) 448 | r.writeCond.Broadcast() 449 | if rerr == io.EOF { 450 | // We do not close. 451 | break 452 | } 453 | } 454 | return n, err 455 | } 456 | 457 | // WriteTo writes data to w until there's no more data to write or 458 | // when an error occurs. The return value n is the number of bytes 459 | // written. Any error encountered during the write is also returned. 460 | // 461 | // If a non-nil error is returned the write side will also see the error. 462 | func (r *RingBuffer) WriteTo(w io.Writer) (n int64, err error) { 463 | if !r.block { 464 | return 0, errors.New("RingBuffer: WriteTo only available in blocking mode") 465 | } 466 | r.mu.Lock() 467 | defer r.mu.Unlock() 468 | 469 | // Don't write more than half, to unblock reads earlier. 470 | maxWrite := len(r.buf) / 2 471 | // But write at least 8K if possible 472 | if maxWrite < 8<<10 { 473 | maxWrite = len(r.buf) 474 | } 475 | for { 476 | if err = r.readErr(true); err != nil { 477 | break 478 | } 479 | if r.r == r.w && !r.isFull { 480 | // Wait for a write to make space 481 | if !r.waitWrite() { 482 | return 0, context.DeadlineExceeded 483 | } 484 | continue 485 | } 486 | 487 | var toWrite []byte 488 | if r.r >= r.w { 489 | // After writer, we can write until end of buffer 490 | toWrite = r.buf[r.r:] 491 | } else { 492 | // Before reader, we can read until writer. 493 | toWrite = r.buf[r.r:r.w] 494 | } 495 | if len(toWrite) > maxWrite { 496 | toWrite = toWrite[:maxWrite] 497 | } 498 | // Unlock while reading 499 | r.mu.Unlock() 500 | nr, werr := w.Write(toWrite) 501 | r.mu.Lock() 502 | if werr != nil { 503 | n += int64(nr) 504 | err = r.setErr(werr, true) 505 | break 506 | } 507 | if nr != len(toWrite) { 508 | err = r.setErr(io.ErrShortWrite, true) 509 | break 510 | } 511 | r.r += nr 512 | if r.r == r.size { 513 | r.r = 0 514 | } 515 | r.isFull = false 516 | n += int64(nr) 517 | r.readCond.Broadcast() 518 | } 519 | if err == io.EOF { 520 | err = nil 521 | } 522 | return n, err 523 | } 524 | 525 | // Copy will pipe all data from the reader to the writer through the ringbuffer. 526 | // The ringbuffer will switch to blocking mode. 527 | // Reads and writes will be done async. 528 | // No internal mem-copies are used for the transfer. 529 | // 530 | // Calling CloseWithError will cancel the transfer and make the function return when 531 | // any ongoing reads or writes have finished. 532 | // 533 | // Calling Read or Write functions concurrently with running this will lead to unpredictable results. 534 | func (r *RingBuffer) Copy(dst io.Writer, src io.Reader) (written int64, err error) { 535 | r.SetBlocking(true) 536 | var wg sync.WaitGroup 537 | wg.Add(1) 538 | go func() { 539 | defer wg.Done() 540 | r.ReadFrom(src) 541 | r.CloseWriter() 542 | }() 543 | defer wg.Wait() 544 | return r.WriteTo(dst) 545 | } 546 | 547 | // TryWrite writes len(p) bytes from p to the underlying buf like Write, but it is not blocking. 548 | // If it does not succeed to acquire the lock, it returns ErrAcquireLock. 549 | func (r *RingBuffer) TryWrite(p []byte) (n int, err error) { 550 | if len(p) == 0 { 551 | return 0, r.setErr(nil, false) 552 | } 553 | ok := r.mu.TryLock() 554 | if !ok { 555 | return 0, ErrAcquireLock 556 | } 557 | defer r.mu.Unlock() 558 | if err := r.err; err != nil { 559 | if err == io.EOF { 560 | err = ErrWriteOnClosed 561 | } 562 | return 0, err 563 | } 564 | 565 | n, err = r.write(p) 566 | if r.block && n > 0 { 567 | r.writeCond.Broadcast() 568 | } 569 | return n, r.setErr(err, true) 570 | } 571 | 572 | func (r *RingBuffer) write(p []byte) (n int, err error) { 573 | if r.isFull { 574 | return 0, ErrIsFull 575 | } 576 | 577 | var avail int 578 | if r.w >= r.r { 579 | avail = r.size - r.w + r.r 580 | } else { 581 | avail = r.r - r.w 582 | } 583 | 584 | if len(p) > avail { 585 | err = ErrTooMuchDataToWrite 586 | p = p[:avail] 587 | } 588 | n = len(p) 589 | 590 | if r.w >= r.r { 591 | c1 := r.size - r.w 592 | if c1 >= n { 593 | copy(r.buf[r.w:], p) 594 | r.w += n 595 | } else { 596 | copy(r.buf[r.w:], p[:c1]) 597 | c2 := n - c1 598 | copy(r.buf[0:], p[c1:]) 599 | r.w = c2 600 | } 601 | } else { 602 | copy(r.buf[r.w:], p) 603 | r.w += n 604 | } 605 | 606 | if r.w == r.size { 607 | r.w = 0 608 | } 609 | if r.w == r.r { 610 | r.isFull = true 611 | } 612 | 613 | return n, err 614 | } 615 | 616 | // WriteByte writes one byte into buffer, and returns ErrIsFull if the buffer is full. 617 | func (r *RingBuffer) WriteByte(c byte) error { 618 | r.mu.Lock() 619 | defer r.mu.Unlock() 620 | if err := r.err; err != nil { 621 | if err == io.EOF { 622 | err = ErrWriteOnClosed 623 | } 624 | return err 625 | } 626 | err := r.writeByte(c) 627 | for err == ErrIsFull && r.block { 628 | if !r.waitRead() { 629 | return context.DeadlineExceeded 630 | } 631 | err = r.setErr(r.writeByte(c), true) 632 | } 633 | if r.block && err == nil { 634 | r.writeCond.Broadcast() 635 | } 636 | return err 637 | } 638 | 639 | // TryWriteByte writes one byte into buffer without blocking. 640 | // If it does not succeed to acquire the lock, it returns ErrAcquireLock. 641 | func (r *RingBuffer) TryWriteByte(c byte) error { 642 | ok := r.mu.TryLock() 643 | if !ok { 644 | return ErrAcquireLock 645 | } 646 | defer r.mu.Unlock() 647 | if err := r.err; err != nil { 648 | if err == io.EOF { 649 | err = ErrWriteOnClosed 650 | } 651 | return err 652 | } 653 | 654 | err := r.writeByte(c) 655 | if err == nil && r.block { 656 | r.writeCond.Broadcast() 657 | } 658 | return err 659 | } 660 | 661 | func (r *RingBuffer) writeByte(c byte) error { 662 | if r.err != nil { 663 | return r.err 664 | } 665 | if r.w == r.r && r.isFull { 666 | return ErrIsFull 667 | } 668 | r.buf[r.w] = c 669 | r.w++ 670 | 671 | if r.w == r.size { 672 | r.w = 0 673 | } 674 | if r.w == r.r { 675 | r.isFull = true 676 | } 677 | 678 | return nil 679 | } 680 | 681 | // Length returns the number of bytes that can be read without blocking. 682 | func (r *RingBuffer) Length() int { 683 | r.mu.Lock() 684 | defer r.mu.Unlock() 685 | 686 | if r.w == r.r { 687 | if r.isFull { 688 | return r.size 689 | } 690 | return 0 691 | } 692 | 693 | if r.w > r.r { 694 | return r.w - r.r 695 | } 696 | 697 | return r.size - r.r + r.w 698 | } 699 | 700 | // Capacity returns the size of the underlying buffer. 701 | func (r *RingBuffer) Capacity() int { 702 | return r.size 703 | } 704 | 705 | // Free returns the number of bytes that can be written without blocking. 706 | func (r *RingBuffer) Free() int { 707 | r.mu.Lock() 708 | defer r.mu.Unlock() 709 | 710 | if r.w == r.r { 711 | if r.isFull { 712 | return 0 713 | } 714 | return r.size 715 | } 716 | 717 | if r.w < r.r { 718 | return r.r - r.w 719 | } 720 | 721 | return r.size - r.w + r.r 722 | } 723 | 724 | // WriteString writes the contents of the string s to buffer, which accepts a slice of bytes. 725 | func (r *RingBuffer) WriteString(s string) (n int, err error) { 726 | x := (*[2]uintptr)(unsafe.Pointer(&s)) 727 | h := [3]uintptr{x[0], x[1], x[1]} 728 | buf := *(*[]byte)(unsafe.Pointer(&h)) 729 | return r.Write(buf) 730 | } 731 | 732 | // Bytes returns all available read bytes. 733 | // It does not move the read pointer and only copy the available data. 734 | // If the dst is big enough, it will be used as destination, 735 | // otherwise a new buffer will be allocated. 736 | func (r *RingBuffer) Bytes(dst []byte) []byte { 737 | r.mu.Lock() 738 | defer r.mu.Unlock() 739 | getDst := func(n int) []byte { 740 | if cap(dst) < n { 741 | return make([]byte, n) 742 | } 743 | return dst[:n] 744 | } 745 | 746 | if r.w == r.r { 747 | if r.isFull { 748 | buf := getDst(r.size) 749 | copy(buf, r.buf[r.r:]) 750 | copy(buf[r.size-r.r:], r.buf[:r.w]) 751 | return buf 752 | } 753 | return nil 754 | } 755 | 756 | if r.w > r.r { 757 | buf := getDst(r.w - r.r) 758 | copy(buf, r.buf[r.r:r.w]) 759 | return buf 760 | } 761 | 762 | n := r.size - r.r + r.w 763 | buf := getDst(n) 764 | 765 | if r.r+n < r.size { 766 | copy(buf, r.buf[r.r:r.r+n]) 767 | } else { 768 | c1 := r.size - r.r 769 | copy(buf, r.buf[r.r:r.size]) 770 | c2 := n - c1 771 | copy(buf[c1:], r.buf[0:c2]) 772 | } 773 | 774 | return buf 775 | } 776 | 777 | // IsFull returns true when the ringbuffer is full. 778 | func (r *RingBuffer) IsFull() bool { 779 | r.mu.Lock() 780 | defer r.mu.Unlock() 781 | 782 | return r.isFull 783 | } 784 | 785 | // IsEmpty returns true when the ringbuffer is empty. 786 | func (r *RingBuffer) IsEmpty() bool { 787 | r.mu.Lock() 788 | defer r.mu.Unlock() 789 | 790 | return !r.isFull && r.w == r.r 791 | } 792 | 793 | // CloseWithError closes the writer; reads will return 794 | // no bytes and the error err, or EOF if err is nil. 795 | // 796 | // CloseWithError never overwrites the previous error if it exists 797 | // and always returns nil. 798 | func (r *RingBuffer) CloseWithError(err error) { 799 | if err == nil { 800 | err = io.EOF 801 | } 802 | r.setErr(err, false) 803 | } 804 | 805 | // CloseWriter closes the writer. 806 | // Reads will return any remaining bytes and io.EOF. 807 | func (r *RingBuffer) CloseWriter() { 808 | r.setErr(io.EOF, false) 809 | } 810 | 811 | // Flush waits for the buffer to be empty and fully read. 812 | // If not blocking ErrIsNotEmpty will be returned if the buffer still contains data. 813 | func (r *RingBuffer) Flush() error { 814 | r.mu.Lock() 815 | defer r.mu.Unlock() 816 | for r.w != r.r || r.isFull { 817 | err := r.readErr(true) 818 | if err != nil { 819 | if err == io.EOF { 820 | err = nil 821 | } 822 | return err 823 | } 824 | if !r.block { 825 | return ErrIsNotEmpty 826 | } 827 | if !r.waitRead() { 828 | return context.DeadlineExceeded 829 | } 830 | } 831 | 832 | err := r.readErr(true) 833 | if err == io.EOF { 834 | return nil 835 | } 836 | return err 837 | } 838 | 839 | // Reset the read pointer and writer pointer to zero. 840 | func (r *RingBuffer) Reset() { 841 | r.mu.Lock() 842 | defer r.mu.Unlock() 843 | 844 | // Set error so any readers/writers will return immediately. 845 | r.setErr(errors.New("reset called"), true) 846 | if r.block { 847 | r.readCond.Broadcast() 848 | r.writeCond.Broadcast() 849 | } 850 | 851 | // Unlock the mutex so readers/writers can finish. 852 | r.mu.Unlock() 853 | r.wg.Wait() 854 | r.mu.Lock() 855 | r.r = 0 856 | r.w = 0 857 | r.err = nil 858 | r.isFull = false 859 | } 860 | 861 | // WriteCloser returns a WriteCloser that writes to the ring buffer. 862 | // When the returned WriteCloser is closed, it will wait for all data to be read before returning. 863 | func (r *RingBuffer) WriteCloser() io.WriteCloser { 864 | return &writeCloser{RingBuffer: r} 865 | } 866 | 867 | type writeCloser struct { 868 | *RingBuffer 869 | } 870 | 871 | // Close provides a close method for the WriteCloser. 872 | func (wc *writeCloser) Close() error { 873 | wc.CloseWriter() 874 | return wc.Flush() 875 | } 876 | 877 | // ReadCloser returns a io.ReadCloser that reads to the ring buffer. 878 | // When the returned ReadCloser is closed, ErrReaderClosed will be returned on any writes done afterwards. 879 | func (r *RingBuffer) ReadCloser() io.ReadCloser { 880 | return &readCloser{RingBuffer: r} 881 | } 882 | 883 | type readCloser struct { 884 | *RingBuffer 885 | } 886 | 887 | // Close provides a close method for the ReadCloser. 888 | func (rc *readCloser) Close() error { 889 | rc.CloseWithError(ErrReaderClosed) 890 | err := rc.readErr(false) 891 | if err == ErrReaderClosed { 892 | err = nil 893 | } 894 | return err 895 | } 896 | 897 | // Peek reads up to len(p) bytes into p without moving the read pointer. 898 | func (r *RingBuffer) Peek(p []byte) (n int, err error) { 899 | if len(p) == 0 { 900 | return 0, r.readErr(false) 901 | } 902 | 903 | r.mu.Lock() 904 | defer r.mu.Unlock() 905 | if err := r.readErr(true); err != nil { 906 | return 0, err 907 | } 908 | 909 | return r.peek(p) 910 | } 911 | 912 | func (r *RingBuffer) peek(p []byte) (n int, err error) { 913 | if r.w == r.r && !r.isFull { 914 | return 0, ErrIsEmpty 915 | } 916 | 917 | if r.w > r.r { 918 | n = r.w - r.r 919 | if n > len(p) { 920 | n = len(p) 921 | } 922 | copy(p, r.buf[r.r:r.r+n]) 923 | return 924 | } 925 | 926 | n = r.size - r.r + r.w 927 | if n > len(p) { 928 | n = len(p) 929 | } 930 | 931 | if r.r+n <= r.size { 932 | copy(p, r.buf[r.r:r.r+n]) 933 | } else { 934 | c1 := r.size - r.r 935 | copy(p, r.buf[r.r:r.size]) 936 | c2 := n - c1 937 | copy(p[c1:], r.buf[0:c2]) 938 | } 939 | 940 | return n, r.readErr(true) 941 | } 942 | -------------------------------------------------------------------------------- /ring_buffer_benchmark_test.go: -------------------------------------------------------------------------------- 1 | package ringbuffer 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func BenchmarkRingBuffer_Sync(b *testing.B) { 11 | rb := New(1024) 12 | data := []byte(strings.Repeat("a", 512)) 13 | buf := make([]byte, 512) 14 | 15 | b.ResetTimer() 16 | for i := 0; i < b.N; i++ { 17 | rb.Write(data) 18 | rb.Read(buf) 19 | } 20 | } 21 | 22 | func BenchmarkRingBuffer_AsyncRead(b *testing.B) { 23 | // Pretty useless benchmark, but it's here for completeness. 24 | rb := New(1024) 25 | data := []byte(strings.Repeat("a", 512)) 26 | buf := make([]byte, 512) 27 | 28 | go func() { 29 | for { 30 | rb.Read(buf) 31 | } 32 | }() 33 | 34 | b.ResetTimer() 35 | for i := 0; i < b.N; i++ { 36 | rb.Write(data) 37 | } 38 | } 39 | 40 | func BenchmarkRingBuffer_AsyncReadBlocking(b *testing.B) { 41 | const sz = 512 42 | const buffers = 10 43 | rb := New(sz * buffers) 44 | rb.SetBlocking(true) 45 | data := []byte(strings.Repeat("a", sz)) 46 | buf := make([]byte, sz) 47 | 48 | go func() { 49 | for { 50 | rb.Read(buf) 51 | } 52 | }() 53 | 54 | b.ResetTimer() 55 | for i := 0; i < b.N; i++ { 56 | rb.Write(data) 57 | } 58 | } 59 | 60 | func BenchmarkRingBuffer_AsyncWrite(b *testing.B) { 61 | rb := New(1024) 62 | data := []byte(strings.Repeat("a", 512)) 63 | buf := make([]byte, 512) 64 | 65 | go func() { 66 | for { 67 | rb.Write(data) 68 | } 69 | }() 70 | 71 | b.ResetTimer() 72 | for i := 0; i < b.N; i++ { 73 | rb.Read(buf) 74 | } 75 | } 76 | 77 | func BenchmarkRingBuffer_AsyncWriteBlocking(b *testing.B) { 78 | const sz = 512 79 | const buffers = 10 80 | rb := New(sz * buffers) 81 | rb.SetBlocking(true) 82 | data := []byte(strings.Repeat("a", sz)) 83 | buf := make([]byte, sz) 84 | 85 | go func() { 86 | for { 87 | rb.Write(data) 88 | } 89 | }() 90 | 91 | b.ResetTimer() 92 | for i := 0; i < b.N; i++ { 93 | rb.Read(buf) 94 | } 95 | } 96 | 97 | type repeatReader struct { 98 | b []byte 99 | doCopy bool // Actually copy data... 100 | } 101 | 102 | func (r repeatReader) Read(b []byte) (n int, err error) { 103 | n = len(b) 104 | for r.doCopy && len(b) > 0 { 105 | n2 := copy(b, r.b) 106 | b = b[n2:] 107 | } 108 | return n, nil 109 | } 110 | 111 | func BenchmarkRingBuffer_ReadFrom(b *testing.B) { 112 | const sz = 512 113 | const buffers = 10 114 | rb := New(sz * buffers) 115 | rb.SetBlocking(true) 116 | data := []byte(strings.Repeat("a", sz)) 117 | buf := make([]byte, sz) 118 | 119 | go func() { 120 | rb.ReadFrom(repeatReader{b: data}) 121 | }() 122 | 123 | b.ResetTimer() 124 | b.SetBytes(sz) 125 | for i := 0; i < b.N; i++ { 126 | io.ReadFull(rb, buf) 127 | } 128 | rb.CloseWithError(context.Canceled) 129 | } 130 | 131 | func BenchmarkRingBuffer_WriteTo(b *testing.B) { 132 | const sz = 512 133 | const buffers = 10 134 | rb := New(sz * buffers) 135 | rb.SetBlocking(true) 136 | data := []byte(strings.Repeat("a", sz)) 137 | 138 | go func() { 139 | rb.WriteTo(io.Discard) 140 | }() 141 | 142 | b.ResetTimer() 143 | b.SetBytes(sz) 144 | for i := 0; i < b.N; i++ { 145 | _, err := rb.Write(data) 146 | if err != nil { 147 | b.Fatal(err) 148 | } 149 | } 150 | rb.CloseWithError(context.Canceled) 151 | } 152 | 153 | func BenchmarkIoPipeReader(b *testing.B) { 154 | pr, pw := io.Pipe() 155 | data := []byte(strings.Repeat("a", 512)) 156 | buf := make([]byte, 512) 157 | 158 | go func() { 159 | for { 160 | pw.Write(data) 161 | } 162 | }() 163 | 164 | b.ResetTimer() 165 | b.SetBytes(int64(len(data))) 166 | for i := 0; i < b.N; i++ { 167 | pr.Read(buf) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /ring_buffer_test.go: -------------------------------------------------------------------------------- 1 | package ringbuffer 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "errors" 7 | "fmt" 8 | "hash/crc32" 9 | "io" 10 | "math/rand" 11 | "os" 12 | "runtime" 13 | "strings" 14 | "sync" 15 | "testing" 16 | "time" 17 | ) 18 | 19 | func TestRingBuffer_interface(t *testing.T) { 20 | rb := New(1) 21 | var _ io.Writer = rb 22 | var _ io.Reader = rb 23 | // var _ io.StringWriter = rb 24 | var _ io.ByteReader = rb 25 | var _ io.ByteWriter = rb 26 | } 27 | 28 | func TestRingBuffer_Write(t *testing.T) { 29 | rb := New(64) 30 | 31 | // check empty or full 32 | if !rb.IsEmpty() { 33 | t.Fatalf("expect IsEmpty is true but got false") 34 | } 35 | if rb.IsFull() { 36 | t.Fatalf("expect IsFull is false but got true") 37 | } 38 | if rb.Length() != 0 { 39 | t.Fatalf("expect len 0 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 40 | } 41 | if rb.Free() != 64 { 42 | t.Fatalf("expect free 64 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 43 | } 44 | 45 | // write 4 * 4 = 16 bytes 46 | n, err := rb.Write([]byte(strings.Repeat("abcd", 4))) 47 | if err != nil { 48 | t.Fatalf("write failed: %v", err) 49 | } 50 | if n != 16 { 51 | t.Fatalf("expect write 16 bytes but got %d", n) 52 | } 53 | if rb.Length() != 16 { 54 | t.Fatalf("expect len 16 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 55 | } 56 | if rb.Free() != 48 { 57 | t.Fatalf("expect free 48 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 58 | } 59 | if !bytes.Equal(rb.Bytes(nil), []byte(strings.Repeat("abcd", 4))) { 60 | t.Fatalf("expect 4 abcd but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 61 | } 62 | 63 | // check empty or full 64 | if rb.IsEmpty() { 65 | t.Fatalf("expect IsEmpty is false but got true") 66 | } 67 | if rb.IsFull() { 68 | t.Fatalf("expect IsFull is false but got true") 69 | } 70 | 71 | // write 48 bytes, should full 72 | n, err = rb.Write([]byte(strings.Repeat("abcd", 12))) 73 | if err != nil { 74 | t.Fatalf("write failed: %v", err) 75 | } 76 | if n != 48 { 77 | t.Fatalf("expect write 48 bytes but got %d", n) 78 | } 79 | if rb.Length() != 64 { 80 | t.Fatalf("expect len 64 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 81 | } 82 | if rb.Free() != 0 { 83 | t.Fatalf("expect free 0 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 84 | } 85 | if rb.w != 0 { 86 | t.Fatalf("expect r.w=0 but got %d. r.r=%d", rb.w, rb.r) 87 | } 88 | if !bytes.Equal(rb.Bytes(nil), []byte(strings.Repeat("abcd", 16))) { 89 | t.Fatalf("expect 16 abcd but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 90 | } 91 | 92 | // check empty or full 93 | if rb.IsEmpty() { 94 | t.Fatalf("expect IsEmpty is false but got true") 95 | } 96 | if !rb.IsFull() { 97 | t.Fatalf("expect IsFull is true but got false") 98 | } 99 | 100 | // write more 4 bytes, should reject 101 | n, err = rb.Write([]byte(strings.Repeat("abcd", 1))) 102 | if err == nil { 103 | t.Fatalf("expect an error but got nil. n=%d, r.w=%d, r.r=%d", n, rb.w, rb.r) 104 | } 105 | if err != ErrIsFull { 106 | t.Fatalf("expect ErrIsFull but got nil") 107 | } 108 | if n != 0 { 109 | t.Fatalf("expect write 0 bytes but got %d", n) 110 | } 111 | if rb.Length() != 64 { 112 | t.Fatalf("expect len 64 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 113 | } 114 | if rb.Free() != 0 { 115 | t.Fatalf("expect free 0 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 116 | } 117 | 118 | // check empty or full 119 | if rb.IsEmpty() { 120 | t.Fatalf("expect IsEmpty is false but got true") 121 | } 122 | if !rb.IsFull() { 123 | t.Fatalf("expect IsFull is true but got false") 124 | } 125 | 126 | // reset this ringbuffer and set a long slice 127 | rb.Reset() 128 | n, err = rb.Write([]byte(strings.Repeat("abcd", 20))) 129 | if err == nil { 130 | t.Fatalf("expect ErrTooManyDataToWrite but got nil") 131 | } 132 | if n != 64 { 133 | t.Fatalf("expect write 64 bytes but got %d", n) 134 | } 135 | if rb.Length() != 64 { 136 | t.Fatalf("expect len 64 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 137 | } 138 | if rb.Free() != 0 { 139 | t.Fatalf("expect free 0 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 140 | } 141 | if rb.w != 0 { 142 | t.Fatalf("expect r.w=0 but got %d. r.r=%d", rb.w, rb.r) 143 | } 144 | 145 | // check empty or full 146 | if rb.IsEmpty() { 147 | t.Fatalf("expect IsEmpty is false but got true") 148 | } 149 | if !rb.IsFull() { 150 | t.Fatalf("expect IsFull is true but got false") 151 | } 152 | 153 | if !bytes.Equal(rb.Bytes(nil), []byte(strings.Repeat("abcd", 16))) { 154 | t.Fatalf("expect 16 abcd but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 155 | } 156 | 157 | rb.Reset() 158 | // write 4 * 2 = 8 bytes 159 | n, err = rb.Write([]byte(strings.Repeat("abcd", 2))) 160 | if err != nil { 161 | t.Fatalf("write failed: %v", err) 162 | } 163 | if n != 8 { 164 | t.Fatalf("expect write 16 bytes but got %d", n) 165 | } 166 | if rb.Length() != 8 { 167 | t.Fatalf("expect len 16 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 168 | } 169 | if rb.Free() != 56 { 170 | t.Fatalf("expect free 48 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 171 | } 172 | buf := make([]byte, 5) 173 | rb.Read(buf) 174 | if rb.Length() != 3 { 175 | t.Fatalf("expect len 3 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 176 | } 177 | rb.Write([]byte(strings.Repeat("abcd", 15))) 178 | 179 | if !bytes.Equal(rb.Bytes(nil), []byte("bcd"+strings.Repeat("abcd", 15))) { 180 | t.Fatalf("expect 63 ... but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 181 | } 182 | 183 | rb.Reset() 184 | n, err = rb.Write([]byte(strings.Repeat("abcd", 16))) 185 | if err != nil { 186 | t.Fatalf("write failed: %v", err) 187 | } 188 | if n != 64 { 189 | t.Fatalf("expect write 64 bytes but got %d", n) 190 | } 191 | if rb.Free() != 0 { 192 | t.Fatalf("expect free 0 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 193 | } 194 | buf = make([]byte, 16) 195 | rb.Read(buf) 196 | n, err = rb.Write([]byte(strings.Repeat("1234", 4))) 197 | if err != nil { 198 | t.Fatalf("write failed: %v", err) 199 | } 200 | if n != 16 { 201 | t.Fatalf("expect write 16 bytes but got %d", n) 202 | } 203 | if rb.Free() != 0 { 204 | t.Fatalf("expect free 0 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 205 | } 206 | if !bytes.Equal(append(buf, rb.Bytes(nil)...), []byte(strings.Repeat("abcd", 16)+strings.Repeat("1234", 4))) { 207 | t.Fatalf("expect 16 abcd and 4 1234 but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 208 | } 209 | } 210 | 211 | func TestRingBuffer_WriteBlocking(t *testing.T) { 212 | rb := New(64).SetBlocking(true) 213 | 214 | // check empty or full 215 | if !rb.IsEmpty() { 216 | t.Fatalf("expect IsEmpty is true but got false") 217 | } 218 | if rb.IsFull() { 219 | t.Fatalf("expect IsFull is false but got true") 220 | } 221 | if rb.Length() != 0 { 222 | t.Fatalf("expect len 0 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 223 | } 224 | if rb.Free() != 64 { 225 | t.Fatalf("expect free 64 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 226 | } 227 | 228 | // write 4 * 4 = 16 bytes 229 | n, err := rb.Write([]byte(strings.Repeat("abcd", 4))) 230 | if err != nil { 231 | t.Fatalf("write failed: %v", err) 232 | } 233 | if n != 16 { 234 | t.Fatalf("expect write 16 bytes but got %d", n) 235 | } 236 | if rb.Length() != 16 { 237 | t.Fatalf("expect len 16 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 238 | } 239 | if rb.Free() != 48 { 240 | t.Fatalf("expect free 48 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 241 | } 242 | if !bytes.Equal(rb.Bytes(nil), []byte(strings.Repeat("abcd", 4))) { 243 | t.Fatalf("expect 4 abcd but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 244 | } 245 | 246 | // check empty or full 247 | if rb.IsEmpty() { 248 | t.Fatalf("expect IsEmpty is false but got true") 249 | } 250 | if rb.IsFull() { 251 | t.Fatalf("expect IsFull is false but got true") 252 | } 253 | 254 | // write 48 bytes, should full 255 | n, err = rb.Write([]byte(strings.Repeat("abcd", 12))) 256 | if err != nil { 257 | t.Fatalf("write failed: %v", err) 258 | } 259 | if n != 48 { 260 | t.Fatalf("expect write 48 bytes but got %d", n) 261 | } 262 | if rb.Length() != 64 { 263 | t.Fatalf("expect len 64 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 264 | } 265 | if rb.Free() != 0 { 266 | t.Fatalf("expect free 0 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 267 | } 268 | if rb.w != 0 { 269 | t.Fatalf("expect r.w=0 but got %d. r.r=%d", rb.w, rb.r) 270 | } 271 | if !bytes.Equal(rb.Bytes(nil), []byte(strings.Repeat("abcd", 16))) { 272 | t.Fatalf("expect 16 abcd but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 273 | } 274 | 275 | // check empty or full 276 | if rb.IsEmpty() { 277 | t.Fatalf("expect IsEmpty is false but got true") 278 | } 279 | if !rb.IsFull() { 280 | t.Fatalf("expect IsFull is true but got false") 281 | } 282 | 283 | rb.Reset() 284 | // write 4 * 2 = 8 bytes 285 | n, err = rb.Write([]byte(strings.Repeat("abcd", 2))) 286 | if err != nil { 287 | t.Fatalf("write failed: %v", err) 288 | } 289 | if n != 8 { 290 | t.Fatalf("expect write 16 bytes but got %d", n) 291 | } 292 | if rb.Length() != 8 { 293 | t.Fatalf("expect len 16 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 294 | } 295 | if rb.Free() != 56 { 296 | t.Fatalf("expect free 48 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 297 | } 298 | buf := make([]byte, 5) 299 | rb.Read(buf) 300 | if rb.Length() != 3 { 301 | t.Fatalf("expect len 3 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 302 | } 303 | rb.Write([]byte(strings.Repeat("abcd", 15))) 304 | 305 | if !bytes.Equal(rb.Bytes(nil), []byte("bcd"+strings.Repeat("abcd", 15))) { 306 | t.Fatalf("expect 63 ... but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 307 | } 308 | 309 | rb.Reset() 310 | n, err = rb.Write([]byte(strings.Repeat("abcd", 16))) 311 | if err != nil { 312 | t.Fatalf("write failed: %v", err) 313 | } 314 | if n != 64 { 315 | t.Fatalf("expect write 64 bytes but got %d", n) 316 | } 317 | if rb.Free() != 0 { 318 | t.Fatalf("expect free 0 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 319 | } 320 | buf = make([]byte, 16) 321 | rb.Read(buf) 322 | n, err = rb.Write([]byte(strings.Repeat("1234", 4))) 323 | if err != nil { 324 | t.Fatalf("write failed: %v", err) 325 | } 326 | if n != 16 { 327 | t.Fatalf("expect write 16 bytes but got %d", n) 328 | } 329 | if rb.Free() != 0 { 330 | t.Fatalf("expect free 0 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 331 | } 332 | if !bytes.Equal(append(buf, rb.Bytes(nil)...), []byte(strings.Repeat("abcd", 16)+strings.Repeat("1234", 4))) { 333 | t.Fatalf("expect 16 abcd and 4 1234 but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 334 | } 335 | } 336 | 337 | func TestRingBuffer_Read(t *testing.T) { 338 | defer timeout(5 * time.Second)() 339 | rb := New(64) 340 | 341 | // check empty or full 342 | if !rb.IsEmpty() { 343 | t.Fatalf("expect IsEmpty is true but got false") 344 | } 345 | if rb.IsFull() { 346 | t.Fatalf("expect IsFull is false but got true") 347 | } 348 | if rb.Length() != 0 { 349 | t.Fatalf("expect len 0 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 350 | } 351 | if rb.Free() != 64 { 352 | t.Fatalf("expect free 64 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 353 | } 354 | 355 | // read empty 356 | buf := make([]byte, 1024) 357 | n, err := rb.Read(buf) 358 | if err == nil { 359 | t.Fatalf("expect an error but got nil") 360 | } 361 | if err != ErrIsEmpty { 362 | t.Fatalf("expect ErrIsEmpty but got nil") 363 | } 364 | if n != 0 { 365 | t.Fatalf("expect read 0 bytes but got %d", n) 366 | } 367 | if rb.Length() != 0 { 368 | t.Fatalf("expect len 0 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 369 | } 370 | if rb.Free() != 64 { 371 | t.Fatalf("expect free 64 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 372 | } 373 | if rb.r != 0 { 374 | t.Fatalf("expect r.r=0 but got %d. r.w=%d", rb.r, rb.w) 375 | } 376 | 377 | // write 16 bytes to read 378 | rb.Write([]byte(strings.Repeat("abcd", 4))) 379 | n, err = rb.Read(buf) 380 | if err != nil { 381 | t.Fatalf("read failed: %v", err) 382 | } 383 | if n != 16 { 384 | t.Fatalf("expect read 16 bytes but got %d", n) 385 | } 386 | if rb.Length() != 0 { 387 | t.Fatalf("expect len 0 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 388 | } 389 | if rb.Free() != 64 { 390 | t.Fatalf("expect free 64 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 391 | } 392 | if rb.r != 16 { 393 | t.Fatalf("expect r.r=16 but got %d. r.w=%d", rb.r, rb.w) 394 | } 395 | 396 | // write long slice to read 397 | rb.Write([]byte(strings.Repeat("abcd", 20))) 398 | n, err = rb.Read(buf) 399 | if err != nil { 400 | t.Fatalf("read failed: %v", err) 401 | } 402 | if n != 64 { 403 | t.Fatalf("expect read 64 bytes but got %d", n) 404 | } 405 | if rb.Length() != 0 { 406 | t.Fatalf("expect len 0 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 407 | } 408 | if rb.Free() != 64 { 409 | t.Fatalf("expect free 64 bytes but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 410 | } 411 | if rb.r != 16 { 412 | t.Fatalf("expect r.r=16 but got %d. r.w=%d", rb.r, rb.w) 413 | } 414 | } 415 | 416 | func TestRingBuffer_Blocking(t *testing.T) { 417 | // Typical runtime is ~5-10s. 418 | defer timeout(60 * time.Second)() 419 | const debug = false 420 | 421 | var readBytes int 422 | var wroteBytes int 423 | var readBuf bytes.Buffer 424 | var wroteBuf bytes.Buffer 425 | readHash := crc32.NewIEEE() 426 | wroteHash := crc32.NewIEEE() 427 | read := io.Writer(readHash) 428 | wrote := io.Writer(wroteHash) 429 | if debug { 430 | read = io.MultiWriter(read, &readBuf) 431 | wrote = io.MultiWriter(wrote, &wroteBuf) 432 | } 433 | debugln := func(args ...interface{}) { 434 | if debug { 435 | fmt.Println(args...) 436 | } 437 | } 438 | // Inject random reader/writer sleeps. 439 | const maxSleep = int(1 * time.Millisecond) 440 | doSleep := !testing.Short() 441 | rb := New(4 << 10).SetBlocking(true) 442 | 443 | // Reader 444 | var readErr error 445 | var wg sync.WaitGroup 446 | wg.Add(1) 447 | go func() { 448 | readRng := rand.New(rand.NewSource(1)) 449 | defer wg.Done() 450 | defer rb.CloseWithError(readErr) 451 | buf := make([]byte, 1024) 452 | for { 453 | // Read 454 | n, err := rb.Read(buf[:readRng.Intn(len(buf))]) 455 | readBytes += n 456 | read.Write(buf[:n]) 457 | debugln("READ 1\t", n, readBytes) 458 | if err != nil { 459 | readErr = err 460 | break 461 | } 462 | 463 | // ReadByte 464 | b, err := rb.ReadByte() 465 | if err != nil { 466 | readErr = err 467 | break 468 | } 469 | readBytes++ 470 | read.Write([]byte{b}) 471 | debugln("READ 2\t", 1, readBytes) 472 | 473 | // TryRead 474 | n, err = rb.TryRead(buf[:readRng.Intn(len(buf))]) 475 | readBytes += n 476 | read.Write(buf[:n]) 477 | debugln("READ 3\t", n, readBytes) 478 | if err != nil && err != ErrAcquireLock && err != ErrIsEmpty { 479 | readErr = err 480 | break 481 | } 482 | if doSleep && readRng.Intn(20) == 0 { 483 | time.Sleep(time.Duration(readRng.Intn(maxSleep))) 484 | } 485 | } 486 | }() 487 | 488 | // Writer 489 | { 490 | buf := make([]byte, 1024) 491 | writeRng := rand.New(rand.NewSource(2)) 492 | for i := 0; i < 2500; i++ { 493 | writeRng.Read(buf) 494 | // Write 495 | n, err := rb.Write(buf[:writeRng.Intn(len(buf))]) 496 | if err != nil { 497 | t.Fatalf("write failed: %v", err) 498 | } 499 | wroteBytes += n 500 | wrote.Write(buf[:n]) 501 | debugln("WRITE 1\t", n, wroteBytes) 502 | 503 | // WriteString 504 | n, err = rb.WriteString(string(buf[:writeRng.Intn(len(buf))])) 505 | if err != nil { 506 | t.Fatalf("write failed: %v", err) 507 | } 508 | wroteBytes += n 509 | wrote.Write(buf[:n]) 510 | debugln("WRITE 2\t", writeRng.Intn(len(buf)), wroteBytes) 511 | 512 | // WriteByte 513 | err = rb.WriteByte(buf[0]) 514 | if err != nil { 515 | t.Fatalf("write failed: %v", err) 516 | } 517 | wroteBytes++ 518 | wrote.Write(buf[:1]) 519 | debugln("WRITE 3\t", 1, wroteBytes) 520 | 521 | // TryWrite 522 | n, err = rb.TryWrite(buf[:writeRng.Intn(len(buf))]) 523 | if err != nil && err != ErrAcquireLock && err != ErrTooMuchDataToWrite && err != ErrIsFull { 524 | t.Fatalf("write failed: %v", err) 525 | } 526 | wroteBytes += n 527 | wrote.Write(buf[:n]) 528 | debugln("WRITE 4\t", n, wroteBytes) 529 | 530 | // TryWriteByte 531 | err = rb.TryWriteByte(buf[0]) 532 | if err != nil && err != ErrAcquireLock && err != ErrTooMuchDataToWrite && err != ErrIsFull { 533 | t.Fatalf("write failed: %v", err) 534 | } 535 | if err == nil { 536 | wroteBytes++ 537 | wrote.Write(buf[:1]) 538 | debugln("WRITE 5\t", 1, wroteBytes) 539 | } 540 | if doSleep && writeRng.Intn(10) == 0 { 541 | time.Sleep(time.Duration(writeRng.Intn(maxSleep))) 542 | } 543 | } 544 | if err := rb.Flush(); err != nil { 545 | t.Fatalf("flush failed: %v", err) 546 | } 547 | rb.CloseWriter() 548 | } 549 | wg.Wait() 550 | if !errors.Is(readErr, io.EOF) { 551 | t.Fatalf("expect io.EOF but got %v", readErr) 552 | } 553 | if readBytes != wroteBytes { 554 | a, b := readBuf.Bytes(), wroteBuf.Bytes() 555 | if debug && !bytes.Equal(a, b) { 556 | common := len(a) 557 | for i := range a { 558 | if a[i] != b[i] { 559 | common = i 560 | break 561 | } 562 | } 563 | a, b = a[common:], b[common:] 564 | if len(a) > 64 { 565 | a = a[:64] 566 | } 567 | if len(b) > 64 { 568 | b = b[:64] 569 | } 570 | t.Errorf("after %d common bytes, difference \nread: %x\nwrote:%x", common, a, b) 571 | } 572 | t.Fatalf("expect read %d bytes but got %d", wroteBytes, readBytes) 573 | } 574 | if readHash.Sum32() != wroteHash.Sum32() { 575 | t.Fatalf("expect read hash 0x%08x but got 0x%08x", readHash.Sum32(), wroteHash.Sum32()) 576 | } 577 | } 578 | 579 | func TestRingBuffer_BlockingBig(t *testing.T) { 580 | // Typical runtime is ~5-10s. 581 | defer timeout(60 * time.Second)() 582 | const debug = false 583 | 584 | var readBytes int 585 | var wroteBytes int 586 | readHash := crc32.NewIEEE() 587 | wroteHash := crc32.NewIEEE() 588 | var readBuf bytes.Buffer 589 | var wroteBuf bytes.Buffer 590 | read := io.Writer(readHash) 591 | wrote := io.Writer(wroteHash) 592 | if debug { 593 | read = io.MultiWriter(read, &readBuf) 594 | wrote = io.MultiWriter(wrote, &wroteBuf) 595 | } 596 | debugln := func(args ...interface{}) { 597 | if debug { 598 | fmt.Println(args...) 599 | } 600 | } 601 | // Inject random reader/writer sleeps. 602 | const maxSleep = int(1 * time.Millisecond) 603 | doSleep := !testing.Short() 604 | rb := New(4 << 10).SetBlocking(true) 605 | 606 | // Reader 607 | var readErr error 608 | var wg sync.WaitGroup 609 | wg.Add(1) 610 | go func() { 611 | defer wg.Done() 612 | defer rb.CloseWithError(readErr) 613 | readRng := rand.New(rand.NewSource(1)) 614 | buf := make([]byte, 64<<10) 615 | for { 616 | // Read 617 | n, err := rb.Read(buf[:readRng.Intn(len(buf))]) 618 | readBytes += n 619 | read.Write(buf[:n]) 620 | if err != nil { 621 | readErr = err 622 | break 623 | } 624 | debugln("READ 1\t", n, readBytes) 625 | 626 | // ReadByte 627 | b, err := rb.ReadByte() 628 | if err != nil { 629 | readErr = err 630 | break 631 | } 632 | readBytes++ 633 | read.Write([]byte{b}) 634 | debugln("READ 2\t", 1, readBytes) 635 | 636 | // TryRead 637 | n, err = rb.TryRead(buf[:readRng.Intn(len(buf))]) 638 | readBytes += n 639 | read.Write(buf[:n]) 640 | if err != nil && err != ErrAcquireLock && err != ErrIsEmpty { 641 | readErr = err 642 | break 643 | } 644 | debugln("READ 3\t", n, readBytes) 645 | if doSleep && readRng.Intn(20) == 0 { 646 | time.Sleep(time.Duration(readRng.Intn(maxSleep))) 647 | } 648 | } 649 | }() 650 | 651 | // Writer 652 | { 653 | writeRng := rand.New(rand.NewSource(2)) 654 | buf := make([]byte, 64<<10) 655 | for i := 0; i < 500; i++ { 656 | writeRng.Read(buf) 657 | // Write 658 | n, err := rb.Write(buf[:writeRng.Intn(len(buf))]) 659 | if err != nil { 660 | t.Fatalf("write failed: %v", err) 661 | } 662 | wroteBytes += n 663 | wrote.Write(buf[:n]) 664 | debugln("WRITE 1\t", n, wroteBytes) 665 | 666 | // WriteString 667 | n, err = rb.WriteString(string(buf[:writeRng.Intn(len(buf))])) 668 | if err != nil { 669 | t.Fatalf("write failed: %v", err) 670 | } 671 | wroteBytes += n 672 | wrote.Write(buf[:n]) 673 | debugln("WRITE 2\t", writeRng.Intn(len(buf)), wroteBytes) 674 | 675 | // WriteByte 676 | err = rb.WriteByte(buf[0]) 677 | if err != nil { 678 | t.Fatalf("write failed: %v", err) 679 | } 680 | wroteBytes++ 681 | wrote.Write(buf[:1]) 682 | debugln("WRITE 3\t", 1, wroteBytes) 683 | 684 | // TryWrite 685 | n, err = rb.TryWrite(buf[:writeRng.Intn(len(buf))]) 686 | if err != nil && err != ErrAcquireLock && err != ErrTooMuchDataToWrite && err != ErrIsFull { 687 | t.Fatalf("write failed: %v", err) 688 | } 689 | wroteBytes += n 690 | wrote.Write(buf[:n]) 691 | debugln("WRITE 4\t", n, wroteBytes) 692 | 693 | // TryWriteByte 694 | err = rb.TryWriteByte(buf[0]) 695 | if err != nil && err != ErrAcquireLock && err != ErrTooMuchDataToWrite && err != ErrIsFull { 696 | t.Fatalf("write failed: %v", err) 697 | } 698 | if err == nil { 699 | wroteBytes++ 700 | wrote.Write(buf[:1]) 701 | debugln("WRITE 5\t", 1, wroteBytes) 702 | } 703 | if doSleep && writeRng.Intn(10) == 0 { 704 | time.Sleep(time.Duration(writeRng.Intn(maxSleep))) 705 | } 706 | } 707 | if err := rb.Flush(); err != nil { 708 | t.Fatalf("flush failed: %v", err) 709 | } 710 | rb.CloseWriter() 711 | } 712 | wg.Wait() 713 | if !errors.Is(readErr, io.EOF) { 714 | t.Fatalf("expect io.EOF but got %v", readErr) 715 | } 716 | if readBytes != wroteBytes { 717 | a, b := readBuf.Bytes(), wroteBuf.Bytes() 718 | if debug && !bytes.Equal(a, b) { 719 | common := len(a) 720 | for i := range a { 721 | if a[i] != b[i] { 722 | t.Errorf("%x != %x", a[i], b[i]) 723 | common = i 724 | break 725 | } 726 | } 727 | a, b = a[common:], b[common:] 728 | if len(a) > 64 { 729 | a = a[:64] 730 | } 731 | if len(b) > 64 { 732 | b = b[:64] 733 | } 734 | t.Errorf("after %d common bytes, difference \nread: %x\nwrote:%x", common, a, b) 735 | } 736 | t.Fatalf("expect read %d bytes but got %d", wroteBytes, readBytes) 737 | } 738 | if readHash.Sum32() != wroteHash.Sum32() { 739 | t.Fatalf("expect read hash 0x%08x but got 0x%08x", readHash.Sum32(), wroteHash.Sum32()) 740 | } 741 | } 742 | 743 | func TestRingBuffer_ReadFromBig(t *testing.T) { 744 | // Typical runtime is ~5-10s. 745 | defer timeout(60 * time.Second)() 746 | const debug = false 747 | 748 | var readBytes int 749 | var wroteBytes int 750 | readHash := crc32.NewIEEE() 751 | wroteHash := crc32.NewIEEE() 752 | var readBuf bytes.Buffer 753 | var wroteBuf bytes.Buffer 754 | read := io.Writer(readHash) 755 | wrote := io.Writer(wroteHash) 756 | read = io.MultiWriter(read, &readBuf) 757 | wrote = io.MultiWriter(wrote, &wroteBuf) 758 | debugln := func(args ...interface{}) { 759 | if debug { 760 | fmt.Println(args...) 761 | } 762 | } 763 | // Inject random reader/writer sleeps. 764 | const maxSleep = int(1 * time.Millisecond) 765 | doSleep := !testing.Short() 766 | rb := New(4 << 10).SetBlocking(true) 767 | 768 | // Reader 769 | var readErr error 770 | var wg sync.WaitGroup 771 | wg.Add(1) 772 | go func() { 773 | defer wg.Done() 774 | defer rb.CloseWithError(readErr) 775 | readRng := rand.New(rand.NewSource(1)) 776 | buf := make([]byte, 64<<10) 777 | for { 778 | // Read 779 | n, err := rb.Read(buf[:readRng.Intn(len(buf))]) 780 | readBytes += n 781 | read.Write(buf[:n]) 782 | if err != nil { 783 | readErr = err 784 | break 785 | } 786 | debugln("READ 1\t", n, readBytes) 787 | 788 | // ReadByte 789 | b, err := rb.ReadByte() 790 | if err != nil { 791 | readErr = err 792 | break 793 | } 794 | readBytes++ 795 | read.Write([]byte{b}) 796 | debugln("READ 2\t", 1, readBytes) 797 | 798 | // TryRead 799 | n, err = rb.TryRead(buf[:readRng.Intn(len(buf))]) 800 | readBytes += n 801 | read.Write(buf[:n]) 802 | if err != nil && err != ErrAcquireLock && err != ErrIsEmpty { 803 | readErr = err 804 | break 805 | } 806 | debugln("READ 3\t", n, readBytes) 807 | if doSleep && readRng.Intn(20) == 0 { 808 | time.Sleep(time.Duration(readRng.Intn(maxSleep))) 809 | } 810 | } 811 | }() 812 | 813 | // Writer 814 | { 815 | writeRng := rand.New(rand.NewSource(2)) 816 | buf := make([]byte, 100<<10) 817 | for i := 0; i < 500; i++ { 818 | writeRng.Read(buf) 819 | // Write 820 | wroteBytes += len(buf) 821 | wrote.Write(buf) 822 | } 823 | debugln("ReadFrom with", wroteBytes, wroteBuf.Len()) 824 | n, err := rb.ReadFrom(bytes.NewReader(wroteBuf.Bytes())) 825 | debugln("ReadFrom returned", n, err) 826 | if n != int64(wroteBytes) { 827 | t.Fatalf("expected %d bytes but got %d", wroteBytes, n) 828 | } 829 | if err != nil { 830 | t.Fatalf("ReadFrom failed: %v", err) 831 | } 832 | debugln("ReadFrom with", wroteBytes, wroteBuf.Len()) 833 | if err := rb.Flush(); err != nil { 834 | t.Fatalf("flush failed: %v", err) 835 | } 836 | rb.CloseWriter() 837 | } 838 | wg.Wait() 839 | if !errors.Is(readErr, io.EOF) { 840 | t.Fatalf("expect io.EOF but got %v", readErr) 841 | } 842 | if readBytes != wroteBytes { 843 | a, b := readBuf.Bytes(), wroteBuf.Bytes() 844 | if debug && !bytes.Equal(a, b) { 845 | common := len(a) 846 | for i := range a { 847 | if a[i] != b[i] { 848 | t.Errorf("%x != %x", a[i], b[i]) 849 | common = i 850 | break 851 | } 852 | } 853 | a, b = a[common:], b[common:] 854 | if len(a) > 64 { 855 | a = a[:64] 856 | } 857 | if len(b) > 64 { 858 | b = b[:64] 859 | } 860 | t.Errorf("after %d common bytes, difference \nread: %x\nwrote:%x", common, a, b) 861 | } 862 | t.Fatalf("expect read %d bytes but got %d", wroteBytes, readBytes) 863 | } 864 | if readHash.Sum32() != wroteHash.Sum32() { 865 | t.Fatalf("expect read hash 0x%08x but got 0x%08x", readHash.Sum32(), wroteHash.Sum32()) 866 | } 867 | } 868 | 869 | type serveStream struct { 870 | b []byte 871 | rng *rand.Rand 872 | } 873 | 874 | func (s *serveStream) Read(p []byte) (n int, err error) { 875 | if len(s.b) == 0 { 876 | return 0, io.EOF 877 | } 878 | n = s.rng.Intn(len(p) + 1) 879 | if n > len(s.b) { 880 | n = len(s.b) 881 | } 882 | copy(p, s.b[:n]) 883 | s.b = s.b[n:] 884 | if len(s.b) == 0 { 885 | return n, io.EOF 886 | } 887 | return n, nil 888 | } 889 | 890 | func TestRingBuffer_Copy(t *testing.T) { 891 | // Typical runtime is ~1-2s. 892 | defer timeout(60 * time.Second)() 893 | 894 | for i := int64(0); i < 100; i++ { 895 | if testing.Short() && i > 5 { 896 | break 897 | } 898 | var wroteBytes int 899 | readHash := crc32.NewIEEE() 900 | wroteHash := crc32.NewIEEE() 901 | var readBuf bytes.Buffer 902 | var wroteBuf bytes.Buffer 903 | read := io.Writer(readHash) 904 | wrote := io.Writer(wroteHash) 905 | read = io.MultiWriter(read, &readBuf) 906 | wrote = io.MultiWriter(wrote, &wroteBuf) 907 | 908 | rb := New(4 << 10).SetBlocking(true) 909 | 910 | // Writer 911 | writeRng := rand.New(rand.NewSource(2 + i)) 912 | buf := make([]byte, 100<<10) 913 | writeRng.Read(buf) 914 | for i := 0; i < writeRng.Intn(1000); i++ { 915 | // Write 916 | wroteBytes += len(buf) 917 | wrote.Write(buf) 918 | } 919 | in := &serveStream{ 920 | b: wroteBuf.Bytes(), 921 | rng: rand.New(rand.NewSource(0xc0cac01a + i)), 922 | } 923 | 924 | copied, err := rb.Copy(read, in) 925 | if err != nil { 926 | t.Fatal(err) 927 | } 928 | if copied != int64(wroteBytes) { 929 | t.Fatalf("copied %d bytes, expected %d", copied, wroteBytes) 930 | } 931 | readBytes := readBuf.Len() 932 | 933 | if readBytes != wroteBytes || readHash.Sum32() != wroteHash.Sum32() { 934 | a, b := readBuf.Bytes(), wroteBuf.Bytes() 935 | if !bytes.Equal(a, b) { 936 | common := len(a) 937 | for i := range a { 938 | if a[i] != b[i] { 939 | t.Errorf("%x != %x", a[i], b[i]) 940 | common = i 941 | break 942 | } 943 | } 944 | a, b = a[common:], b[common:] 945 | if len(a) > 64 { 946 | a = a[:64] 947 | } 948 | if len(b) > 64 { 949 | b = b[:64] 950 | } 951 | t.Errorf("after %d common bytes, difference \nread: %x\nwrote:%x", common, a, b) 952 | } 953 | t.Fatalf("expect read %d bytes but got %d", wroteBytes, readBytes) 954 | } 955 | if readHash.Sum32() != wroteHash.Sum32() { 956 | t.Fatalf("expect read hash 0x%08x but got 0x%08x", readHash.Sum32(), wroteHash.Sum32()) 957 | } 958 | } 959 | } 960 | 961 | type errormock struct { 962 | rerr error 963 | read int 964 | rleft int 965 | werr error 966 | written int 967 | wleft int 968 | } 969 | 970 | var _ io.Reader = &errormock{} 971 | var _ io.Writer = &errormock{} 972 | 973 | func (e *errormock) Read(p []byte) (n int, err error) { 974 | switch { 975 | case e.rleft <= 0: 976 | err = e.rerr 977 | 978 | case len(p) > e.rleft: 979 | n = e.rleft 980 | 981 | default: 982 | n = len(p) 983 | } 984 | 985 | e.rleft -= n 986 | e.read += n 987 | 988 | return 989 | } 990 | 991 | func (e *errormock) Write(p []byte) (n int, err error) { 992 | switch { 993 | case e.wleft <= 0: 994 | err = e.werr 995 | 996 | case len(p) > e.wleft: 997 | n = e.wleft 998 | err = e.werr 999 | 1000 | default: 1001 | n = len(p) 1002 | } 1003 | 1004 | e.wleft -= n 1005 | e.written += n 1006 | 1007 | return 1008 | } 1009 | 1010 | func TestRingBuffer_ReadFrom_Error(t *testing.T) { 1011 | const bufsize = 4 << 10 1012 | 1013 | cases := []int{0, 1, bufsize, bufsize - 1, bufsize + 1} 1014 | 1015 | for _, c := range cases { 1016 | t.Run(fmt.Sprintf("limit=%d", c), func(t *testing.T) { 1017 | rb := New(bufsize).SetBlocking(true) 1018 | tester := &errormock{rerr: io.ErrUnexpectedEOF, rleft: c, wleft: 10 << 10} 1019 | 1020 | // drain the buffer 1021 | go func() { 1022 | _, _ = io.Copy(io.Discard, rb) 1023 | }() 1024 | 1025 | copied, err := rb.ReadFrom(tester) 1026 | 1027 | if err != io.ErrUnexpectedEOF { 1028 | t.Errorf("expect io.ErrUnexpectedEOF but got %v", err) 1029 | } 1030 | 1031 | if copied != int64(tester.read) { 1032 | t.Errorf("expect %d bytes copied but got %d", c, copied) 1033 | } 1034 | }) 1035 | } 1036 | } 1037 | 1038 | func TestRingBuffer_WriteTo_Error(t *testing.T) { 1039 | const bufsize = 4 << 10 1040 | 1041 | cases := []int{0, 1, bufsize, bufsize - 1, bufsize + 1} 1042 | 1043 | for _, c := range cases { 1044 | t.Run(fmt.Sprintf("limit=%d", c), func(t *testing.T) { 1045 | rb := New(bufsize).SetBlocking(true) 1046 | tester := &errormock{werr: io.ErrClosedPipe, wleft: c} 1047 | 1048 | // fill the buffer with enough data 1049 | go func() { 1050 | _, _ = io.Copy(rb, bytes.NewReader(make([]byte, bufsize*2))) 1051 | }() 1052 | 1053 | copied, err := rb.WriteTo(tester) 1054 | 1055 | if err != io.ErrClosedPipe { 1056 | t.Errorf("expect io.ErrClosedPipe but got %v", err) 1057 | } 1058 | 1059 | if copied != int64(tester.written) { 1060 | t.Errorf("expect %d bytes copied but got %d", c, copied) 1061 | } 1062 | }) 1063 | } 1064 | } 1065 | 1066 | func TestRingBuffer_Copy_ReadError(t *testing.T) { 1067 | const bufsize = 4 << 10 1068 | 1069 | cases := []int{0, 1, bufsize, bufsize - 1, bufsize + 1} 1070 | 1071 | for _, c := range cases { 1072 | t.Run(fmt.Sprintf("limit=%d", c), func(t *testing.T) { 1073 | rb := New(bufsize) 1074 | tester := &errormock{rerr: io.ErrUnexpectedEOF, rleft: c, wleft: 10 << 10} 1075 | 1076 | copied, err := rb.Copy(tester, tester) 1077 | 1078 | if err != io.ErrUnexpectedEOF { 1079 | t.Errorf("expect io.ErrUnexpectedEOF but got %v", err) 1080 | } 1081 | 1082 | if copied != int64(tester.written) { 1083 | t.Errorf("expect %d bytes copied but got %d", c, copied) 1084 | } 1085 | }) 1086 | } 1087 | } 1088 | 1089 | func TestRingBuffer_Copy_WriteError(t *testing.T) { 1090 | const bufsize = 4 << 10 1091 | 1092 | cases := []int{0, 1, bufsize, bufsize - 1, bufsize + 1} 1093 | 1094 | for _, c := range cases { 1095 | t.Run(fmt.Sprintf("limit=%d", c), func(t *testing.T) { 1096 | rb := New(bufsize) 1097 | tester := &errormock{rleft: 10 << 10, werr: io.ErrClosedPipe, wleft: c} 1098 | 1099 | copied, err := rb.Copy(tester, tester) 1100 | 1101 | if err != io.ErrClosedPipe { 1102 | t.Errorf("expect io.ErrUnexpectedEOF but got %v", err) 1103 | } 1104 | 1105 | if copied != int64(tester.written) { 1106 | t.Errorf("expect %d bytes copied but got %d", c, copied) 1107 | } 1108 | }) 1109 | } 1110 | } 1111 | 1112 | func TestRingBuffer_ByteInterface(t *testing.T) { 1113 | defer timeout(5 * time.Second)() 1114 | rb := New(2) 1115 | 1116 | // write one 1117 | err := rb.WriteByte('a') 1118 | if err != nil { 1119 | t.Fatalf("WriteByte failed: %v", err) 1120 | } 1121 | if rb.Length() != 1 { 1122 | t.Fatalf("expect len 1 byte but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 1123 | } 1124 | if rb.Free() != 1 { 1125 | t.Fatalf("expect free 1 byte but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 1126 | } 1127 | if !bytes.Equal(rb.Bytes(nil), []byte{'a'}) { 1128 | t.Fatalf("expect a but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 1129 | } 1130 | // check empty or full 1131 | if rb.IsEmpty() { 1132 | t.Fatalf("expect IsEmpty is false but got true") 1133 | } 1134 | if rb.IsFull() { 1135 | t.Fatalf("expect IsFull is false but got true") 1136 | } 1137 | 1138 | // write to, isFull 1139 | err = rb.WriteByte('b') 1140 | if err != nil { 1141 | t.Fatalf("WriteByte failed: %v", err) 1142 | } 1143 | if rb.Length() != 2 { 1144 | t.Fatalf("expect len 2 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 1145 | } 1146 | if rb.Free() != 0 { 1147 | t.Fatalf("expect free 0 byte but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 1148 | } 1149 | if !bytes.Equal(rb.Bytes(nil), []byte{'a', 'b'}) { 1150 | t.Fatalf("expect a but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 1151 | } 1152 | // check empty or full 1153 | if rb.IsEmpty() { 1154 | t.Fatalf("expect IsEmpty is false but got true") 1155 | } 1156 | if !rb.IsFull() { 1157 | t.Fatalf("expect IsFull is true but got false") 1158 | } 1159 | 1160 | // write 1161 | err = rb.WriteByte('c') 1162 | if err == nil { 1163 | t.Fatalf("expect ErrIsFull but got nil") 1164 | } 1165 | if rb.Length() != 2 { 1166 | t.Fatalf("expect len 2 bytes but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 1167 | } 1168 | if rb.Free() != 0 { 1169 | t.Fatalf("expect free 0 byte but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 1170 | } 1171 | if !bytes.Equal(rb.Bytes(nil), []byte{'a', 'b'}) { 1172 | t.Fatalf("expect a but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 1173 | } 1174 | // check empty or full 1175 | if rb.IsEmpty() { 1176 | t.Fatalf("expect IsEmpty is false but got true") 1177 | } 1178 | if !rb.IsFull() { 1179 | t.Fatalf("expect IsFull is true but got false") 1180 | } 1181 | 1182 | // read one 1183 | b, err := rb.ReadByte() 1184 | if err != nil { 1185 | t.Fatalf("ReadByte failed: %v", err) 1186 | } 1187 | if b != 'a' { 1188 | t.Fatalf("expect a but got %c. r.w=%d, r.r=%d", b, rb.w, rb.r) 1189 | } 1190 | if rb.Length() != 1 { 1191 | t.Fatalf("expect len 1 byte but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 1192 | } 1193 | if rb.Free() != 1 { 1194 | t.Fatalf("expect free 1 byte but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 1195 | } 1196 | if !bytes.Equal(rb.Bytes(nil), []byte{'b'}) { 1197 | t.Fatalf("expect a but got %s. r.w=%d, r.r=%d", rb.Bytes(nil), rb.w, rb.r) 1198 | } 1199 | // check empty or full 1200 | if rb.IsEmpty() { 1201 | t.Fatalf("expect IsEmpty is false but got true") 1202 | } 1203 | if rb.IsFull() { 1204 | t.Fatalf("expect IsFull is false but got true") 1205 | } 1206 | 1207 | // read two, empty 1208 | b, err = rb.ReadByte() 1209 | if err != nil { 1210 | t.Fatalf("ReadByte failed: %v", err) 1211 | } 1212 | if b != 'b' { 1213 | t.Fatalf("expect b but got %c. r.w=%d, r.r=%d", b, rb.w, rb.r) 1214 | } 1215 | if rb.Length() != 0 { 1216 | t.Fatalf("expect len 0 byte but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 1217 | } 1218 | if rb.Free() != 2 { 1219 | t.Fatalf("expect free 2 byte but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 1220 | } 1221 | // check empty or full 1222 | if !rb.IsEmpty() { 1223 | t.Fatalf("expect IsEmpty is true but got false") 1224 | } 1225 | if rb.IsFull() { 1226 | t.Fatalf("expect IsFull is false but got true") 1227 | } 1228 | 1229 | // read three, error 1230 | _, err = rb.ReadByte() 1231 | if err == nil { 1232 | t.Fatalf("expect ErrIsEmpty but got nil") 1233 | } 1234 | if rb.Length() != 0 { 1235 | t.Fatalf("expect len 0 byte but got %d. r.w=%d, r.r=%d", rb.Length(), rb.w, rb.r) 1236 | } 1237 | if rb.Free() != 2 { 1238 | t.Fatalf("expect free 2 byte but got %d. r.w=%d, r.r=%d", rb.Free(), rb.w, rb.r) 1239 | } 1240 | // check empty or full 1241 | if !rb.IsEmpty() { 1242 | t.Fatalf("expect IsEmpty is true but got false") 1243 | } 1244 | if rb.IsFull() { 1245 | t.Fatalf("expect IsFull is false but got true") 1246 | } 1247 | } 1248 | 1249 | func TestRingBufferCloseError(t *testing.T) { 1250 | type testError1 struct{ error } 1251 | type testError2 struct{ error } 1252 | 1253 | rb := New(100) 1254 | rb.CloseWithError(testError1{}) 1255 | if _, err := rb.Write(nil); err != (testError1{}) { 1256 | t.Errorf("Write error: got %T, want testError1", err) 1257 | } 1258 | if _, err := rb.Write([]byte{1}); err != (testError1{}) { 1259 | t.Errorf("Write error: got %T, want testError1", err) 1260 | } 1261 | if err := rb.WriteByte(0); err != (testError1{}) { 1262 | t.Errorf("Write error: got %T, want testError1", err) 1263 | } 1264 | if _, err := rb.TryWrite(nil); err != (testError1{}) { 1265 | t.Errorf("Write error: got %T, want testError1", err) 1266 | } 1267 | if _, err := rb.TryWrite([]byte{1}); err != (testError1{}) { 1268 | t.Errorf("Write error: got %T, want testError1", err) 1269 | } 1270 | if err := rb.TryWriteByte(0); err != (testError1{}) { 1271 | t.Errorf("Write error: got %T, want testError1", err) 1272 | } 1273 | if err := rb.Flush(); err != (testError1{}) { 1274 | t.Errorf("Write error: got %T, want testError1", err) 1275 | } 1276 | 1277 | rb.CloseWithError(testError2{}) 1278 | if _, err := rb.Write(nil); err != (testError1{}) { 1279 | t.Errorf("Write error: got %T, want testError1", err) 1280 | } 1281 | 1282 | rb.Reset() 1283 | rb.CloseWithError(testError1{}) 1284 | if _, err := rb.Read(nil); err != (testError1{}) { 1285 | t.Errorf("Read error: got %T, want testError1", err) 1286 | } 1287 | if _, err := rb.Read([]byte{0}); err != (testError1{}) { 1288 | t.Errorf("Read error: got %T, want testError1", err) 1289 | } 1290 | if _, err := rb.ReadByte(); err != (testError1{}) { 1291 | t.Errorf("Read error: got %T, want testError1", err) 1292 | } 1293 | if _, err := rb.TryRead(nil); err != (testError1{}) { 1294 | t.Errorf("Read error: got %T, want testError1", err) 1295 | } 1296 | if _, err := rb.TryRead([]byte{0}); err != (testError1{}) { 1297 | t.Errorf("Read error: got %T, want testError1", err) 1298 | } 1299 | rb.CloseWithError(testError2{}) 1300 | if _, err := rb.Read(nil); err != (testError1{}) { 1301 | t.Errorf("Read error: got %T, want testError1", err) 1302 | } 1303 | if _, err := rb.Read([]byte{0}); err != (testError1{}) { 1304 | t.Errorf("Read error: got %T, want testError1", err) 1305 | } 1306 | if _, err := rb.ReadByte(); err != (testError1{}) { 1307 | t.Errorf("Read error: got %T, want testError1", err) 1308 | } 1309 | if _, err := rb.TryRead(nil); err != (testError1{}) { 1310 | t.Errorf("Read error: got %T, want testError1", err) 1311 | } 1312 | if _, err := rb.TryRead([]byte{0}); err != (testError1{}) { 1313 | t.Errorf("Read error: got %T, want testError1", err) 1314 | } 1315 | } 1316 | 1317 | func TestRingBufferCloseErrorUnblocks(t *testing.T) { 1318 | const sz = 100 1319 | rb := New(sz).SetBlocking(true) 1320 | 1321 | testCancel := func(fn func()) { 1322 | t.Helper() 1323 | defer timeout(5 * time.Second)() 1324 | rb.Reset() 1325 | done := make(chan struct{}) 1326 | go func() { 1327 | defer close(done) 1328 | time.Sleep(10 * time.Millisecond) 1329 | fn() 1330 | }() 1331 | rb.CloseWithError(errors.New("test error")) 1332 | <-done 1333 | 1334 | rb.Reset() 1335 | done = make(chan struct{}) 1336 | go func() { 1337 | defer close(done) 1338 | fn() 1339 | }() 1340 | time.Sleep(10 * time.Millisecond) 1341 | rb.CloseWithError(errors.New("test error")) 1342 | <-done 1343 | } 1344 | testCancel(func() { 1345 | rb.Write([]byte{sz + 5: 1}) 1346 | }) 1347 | testCancel(func() { 1348 | rb.Write(make([]byte, sz)) 1349 | rb.WriteByte(0) 1350 | }) 1351 | testCancel(func() { 1352 | rb.Read([]byte{10: 1}) 1353 | }) 1354 | testCancel(func() { 1355 | rb.ReadByte() 1356 | }) 1357 | testCancel(func() { 1358 | rb.Write(make([]byte, sz)) 1359 | rb.Flush() 1360 | }) 1361 | } 1362 | 1363 | func TestWriteAfterWriterClose(t *testing.T) { 1364 | rb := New(100).SetBlocking(true) 1365 | 1366 | done := make(chan error) 1367 | go func() { 1368 | defer close(done) 1369 | _, err := rb.Write([]byte("hello")) 1370 | if err != nil { 1371 | t.Errorf("got error: %q; expected none", err) 1372 | } 1373 | rb.CloseWriter() 1374 | _, err = rb.Write([]byte("world")) 1375 | done <- err 1376 | err = rb.WriteByte(0) 1377 | done <- err 1378 | _, err = rb.TryWrite([]byte("world")) 1379 | done <- err 1380 | err = rb.TryWriteByte(0) 1381 | done <- err 1382 | }() 1383 | 1384 | buf := make([]byte, 100) 1385 | n, err := io.ReadFull(rb, buf) 1386 | if err != nil && err != io.ErrUnexpectedEOF { 1387 | t.Fatalf("got: %q; want: %q", err, io.ErrUnexpectedEOF) 1388 | } 1389 | for writeErr := range done { 1390 | if writeErr != ErrWriteOnClosed { 1391 | t.Errorf("got: %q; want: %q", writeErr, ErrWriteOnClosed) 1392 | } else { 1393 | t.Log("ok") 1394 | } 1395 | } 1396 | result := string(buf[0:n]) 1397 | if result != "hello" { 1398 | t.Errorf("got: %q; want: %q", result, "hello") 1399 | } 1400 | } 1401 | 1402 | func TestWithDeadline(t *testing.T) { 1403 | rb := New(100).SetBlocking(true) 1404 | tests := []struct { 1405 | // test function that will fill buffer and block either on read or write. 1406 | t func(chan<- error) 1407 | // Set one, based on if we expect to block read or write 1408 | r, w bool 1409 | }{ 1410 | // Read tests. 1411 | 0: {t: func(c chan<- error) { 1412 | _, err := rb.Read(make([]byte, 1000)) 1413 | c <- err 1414 | }, r: true}, 1415 | 1: {t: func(c chan<- error) { 1416 | _, err := rb.ReadByte() 1417 | c <- err 1418 | }, r: true}, 1419 | 2: {t: func(c chan<- error) { 1420 | _, err := rb.WriteTo(io.Discard) 1421 | c <- err 1422 | }, r: true}, 1423 | // Write tests 1424 | 3: {t: func(c chan<- error) { 1425 | // fill it 1426 | _, err := rb.Write(make([]byte, 100)) 1427 | if err != nil { 1428 | panic(err) 1429 | } 1430 | _, err = rb.Write(make([]byte, 1000)) 1431 | c <- err 1432 | }, w: true}, 1433 | 4: {t: func(c chan<- error) { 1434 | // fill it 1435 | _, err := rb.Write(make([]byte, 100)) 1436 | if err != nil { 1437 | panic(err) 1438 | } 1439 | err = rb.WriteByte(42) 1440 | c <- err 1441 | }, w: true}, 1442 | 5: {t: func(c chan<- error) { 1443 | // fill it 1444 | _, err := rb.Write(make([]byte, 100)) 1445 | if err != nil { 1446 | panic(err) 1447 | } 1448 | _, err = rb.WriteString("hello world!") 1449 | c <- err 1450 | }, w: true}, 1451 | 6: {t: func(c chan<- error) { 1452 | // fill it 1453 | _, err := rb.Write(make([]byte, 100)) 1454 | if err != nil { 1455 | panic(err) 1456 | } 1457 | _, err = rb.ReadFrom(bytes.NewBuffer([]byte("hello world!"))) 1458 | c <- err 1459 | }, w: true}, 1460 | } 1461 | 1462 | for i := range tests { 1463 | t.Run(fmt.Sprint("both-", i), func(t *testing.T) { 1464 | rb.WithTimeout(50 * time.Millisecond) 1465 | rb.Reset() 1466 | timedOut := make(chan error) 1467 | started := time.Now() 1468 | go tests[i].t(timedOut) 1469 | select { 1470 | case <-time.After(10 * time.Second): 1471 | t.Fatalf("deadline exceeded by 200x") 1472 | case err := <-timedOut: 1473 | if !errors.Is(err, context.DeadlineExceeded) { 1474 | t.Fatal("unexpected error:", err) 1475 | } 1476 | if d := time.Since(started); d < 40*time.Millisecond { 1477 | t.Errorf("benchmark terminated before timeout: %v", d) 1478 | } 1479 | } 1480 | }) 1481 | t.Run(fmt.Sprint("read-", i), func(t *testing.T) { 1482 | rb.Reset() 1483 | rb.WithTimeout(0).WithReadTimeout(50 * time.Millisecond) 1484 | timedOut := make(chan error) 1485 | started := time.Now() 1486 | go tests[i].t(timedOut) 1487 | select { 1488 | case <-time.After(100 * time.Millisecond): 1489 | if tests[i].r { 1490 | t.Fatalf("deadline exceeded by 200x") 1491 | } 1492 | rb.CloseWithError(errors.New("test error")) 1493 | <-timedOut 1494 | case err := <-timedOut: 1495 | if !errors.Is(err, context.DeadlineExceeded) { 1496 | t.Fatal("unexpected error:", err) 1497 | } 1498 | if tests[i].w { 1499 | t.Errorf("terminated on write") 1500 | } 1501 | if d := time.Since(started); d < 40*time.Millisecond { 1502 | t.Errorf("benchmark terminated before timeout: %v", d) 1503 | } 1504 | } 1505 | }) 1506 | t.Run(fmt.Sprint("write-", i), func(t *testing.T) { 1507 | rb.Reset() 1508 | rb.WithTimeout(0).WithWriteTimeout(50 * time.Millisecond) 1509 | timedOut := make(chan error) 1510 | started := time.Now() 1511 | go tests[i].t(timedOut) 1512 | select { 1513 | case <-time.After(100 * time.Millisecond): 1514 | if tests[i].w { 1515 | t.Fatalf("deadline exceeded on write") 1516 | } 1517 | rb.CloseWithError(errors.New("test error")) 1518 | <-timedOut 1519 | case err := <-timedOut: 1520 | if !errors.Is(err, context.DeadlineExceeded) { 1521 | t.Fatal("unexpected error:", err) 1522 | } 1523 | if tests[i].r { 1524 | t.Errorf("terminated on read") 1525 | } 1526 | if d := time.Since(started); d < 40*time.Millisecond { 1527 | t.Errorf("benchmark terminated before timeout: %v", d) 1528 | } 1529 | } 1530 | }) 1531 | 1532 | t.Run(fmt.Sprint("allocs-test-", i), func(t *testing.T) { 1533 | rb.Reset() 1534 | rb.WithTimeout(50 * time.Millisecond) 1535 | timedOut := make(chan error) 1536 | a := testing.AllocsPerRun(5, func() { 1537 | rb.Reset() 1538 | go tests[i].t(timedOut) 1539 | <-timedOut 1540 | }) 1541 | t.Logf("Average Allocs: %.1f", a) 1542 | }) 1543 | } 1544 | } 1545 | 1546 | func timeout(after time.Duration) (cancel func()) { 1547 | c := time.After(after) 1548 | cc := make(chan struct{}) 1549 | go func() { 1550 | select { 1551 | case <-cc: 1552 | return 1553 | case <-c: 1554 | buf := make([]byte, 1<<20) 1555 | stacklen := runtime.Stack(buf, true) 1556 | fmt.Printf("=== Timeout, assuming deadlock ===\n*** goroutine dump...\n%s\n*** end\n", string(buf[:stacklen])) 1557 | os.Exit(2) 1558 | } 1559 | }() 1560 | return func() { 1561 | close(cc) 1562 | } 1563 | } 1564 | 1565 | func TestRingBuffer_Peek(t *testing.T) { 1566 | rb := New(10) 1567 | data := []byte("hello") 1568 | rb.Write(data) 1569 | 1570 | buf := make([]byte, len(data)) 1571 | n, err := rb.Peek(buf) 1572 | if err != nil { 1573 | t.Fatalf("unexpected error: %v", err) 1574 | } 1575 | if n != len(data) { 1576 | t.Fatalf("expected %d bytes, got %d", len(data), n) 1577 | } 1578 | if string(buf) != string(data) { 1579 | t.Fatalf("expected %s, got %s", string(data), string(buf)) 1580 | } 1581 | } 1582 | --------------------------------------------------------------------------------