Index: src/0dev.org/ioutil/ioutil.go ================================================================== --- src/0dev.org/ioutil/ioutil.go +++ src/0dev.org/ioutil/ioutil.go @@ -1,7 +1,11 @@ // Package ioutil contains various constructs for io operations package ioutil + +import ( + "io" +) // An function alias type that implements io.Writer type WriterFunc func([]byte) (int, error) // Delegates the call to the WriterFunc while implementing io.Writer @@ -14,5 +18,51 @@ // Delegates the call to the WriterFunc while implementing io.Reader func (r ReaderFunc) Read(b []byte) (int, error) { return r(b) } + +// Returns a reader that will delegate calls to Read(...) while ensuring +// that the output buffer will never be smaller than the required size +func MinReader(reader io.Reader, size int) io.Reader { + var buffer []byte = make([]byte, 0, size) + + return ReaderFunc(func(output []byte) (readCount int, e error) { + var ( + bufferLength int = len(buffer) + err error + ) + + start: + // Reply with the buffered data if there is any + if bufferLength > 0 { + readCount = copy(output, buffer) + + if readCount < bufferLength { + // Advance the data in the buffer + buffer = buffer[:copy(buffer, buffer[:readCount])] + } else { + // Clear the buffer + buffer = buffer[:0] + } + + // Stage any error for returning + e, err = err, nil + + return readCount, e + } + + // Delegate if the buffer is empty and the destination buffer is large enough + if len(output) >= size { + return reader.Read(output) + } + + // Extend the buffer up to the desired size and perform a Read + buffer = buffer[:size] + readCount, err = reader.Read(buffer) + + // Size the buffer down to the read data size and restart + buffer = buffer[:readCount] + bufferLength = len(buffer) + goto start + }) +} Index: src/0dev.org/ioutil/ioutil_test.go ================================================================== --- src/0dev.org/ioutil/ioutil_test.go +++ src/0dev.org/ioutil/ioutil_test.go @@ -1,10 +1,11 @@ package ioutil import ( diff "0dev.org/diff" "bytes" + "io" "testing" ) func TestWriter(t *testing.T) { var ( @@ -43,5 +44,51 @@ func(i, j int) bool { return input[i] == output[j] }}) if len(delta.Added) > 0 || len(delta.Removed) > 0 { t.Error("Differences detected ", delta) } } + +func TestMinReader(t *testing.T) { + var ( + input []byte = []byte{0, 1, 2, 3, 4, 5, 6, 7} + output []byte = make([]byte, 16) + + reader *bytes.Reader = bytes.NewReader(input) + min io.Reader = MinReader(reader, 4) + ) + + // Expecting a read count of 2 + count, err := min.Read(output[:2]) + if count != 2 { + t.Error("Invalid read count from MinReader", count) + } + if err != nil { + t.Error("Unexpected error from MinReader", err) + } + + // Expecting a read count of 2 as it should have 2 bytes in its buffer + count, err = min.Read(output[:3]) + if count != 2 { + t.Error("Invalid read count from MinReader", count) + } + if err != nil { + t.Error("Unexpected error from MinReader", err) + } + + // Expecting a read count of 4 as the buffer should be empty + count, err = min.Read(output[:4]) + if count != 4 { + t.Error("Invalid read count from MinReader", count) + } + if err != nil { + t.Error("Unexpected error from MinReader", err) + } + + // Expecting a read count of 0 with an EOF as the buffer should be empty + count, err = min.Read(output[:4]) + if count != 0 { + t.Error("Invalid read count from MinReader", count) + } + if err != io.EOF { + t.Error("Unexpected error from MinReader", err) + } +}