@@ -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 + }) +}