Overview
| Comment: | Implemented ioutil/SizedWriter. CC at 100%. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
c28a763d5e932739911cbb29f9e01b1c |
| User & Date: | spaskalev on 2014-12-24 19:07:41.349 |
| Other Links: | manifest | tags |
Context
|
2014-12-24
| ||
| 21:40 | Fixed SizedWriter behavior so that it follows io.Writer's Write(...) contract. Added more tests for 100% CC on the ioutil package. Predictor's compressor now uses SizedWriter and no longer has to do any internal buffering. check-in: e1778aba98 user: spaskalev tags: trunk | |
| 19:07 | Implemented ioutil/SizedWriter. CC at 100%. check-in: c28a763d5e user: spaskalev tags: trunk | |
| 08:24 | Added an explicit copyright notice in the code. check-in: ffd1ab7b0c user: spaskalev tags: trunk | |
Changes
Modified src/0dev.org/ioutil/ioutil.go
from [98f8caa018]
to [9da21d39e0].
|
| | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
// 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.
func (w WriterFunc) Write(b []byte) (int, error) {
return w(b)
}
// An function alias type that implements io.Reader.
type ReaderFunc func([]byte) (int, error)
// Delegates the call to the WriterFunc while implementing io.Reader.
func (r ReaderFunc) Read(b []byte) (int, error) {
return r(b)
}
// Returns a writer that delegates calls to Write(...) while ensuring
// that it is never called with less bytes than the specified amount.
//
// Calls with fewer bytes are buffered while a call with a nil slice
// causes the buffer to be flushed to the underlying writer.
func SizedWriter(writer io.Writer, size int) io.Writer {
var buffer []byte = make([]byte, 0, size)
var write WriterFunc
write = func(input []byte) (int, error) {
var (
count int
err error
)
// Flush the buffer when called with no bytes to write
if input == nil {
// Call the writer with whatever we have in store..
count, err = writer.Write(buffer)
// Advance the buffer
buffer = buffer[:copy(buffer, buffer[count:])]
return 0, err
}
// Delegate to the writer if the size is right
if len(buffer) == 0 && len(input) >= size {
return writer.Write(input)
}
// Append data to the buffer
count = copy(buffer[len(buffer):size], input)
buffer = buffer[:len(buffer)+count]
// Return if we don't have enough bytes to write
if len(buffer) < size {
return len(input), nil
}
// Flush the buffer as it is filled
_, err = write(nil)
if err != nil {
return count, err
}
// Handle the rest of the input
return write(input[count:])
}
return write
}
// Returns a reader that delegates calls to Read(...) while ensuring
// that the output buffer is never smaller than the required size
// and is downsized to a multiple of the required size if larger.
func SizedReader(reader io.Reader, size int) io.Reader {
var buffer []byte = make([]byte, 0, size)
return ReaderFunc(func(output []byte) (int, error) {
var (
count int
err error
)
start:
// Reply with the buffered data if there is any
if len(buffer) > 0 {
count = copy(output, buffer)
// Advance the data in the buffer
buffer = buffer[:copy(buffer, buffer[count:])]
// Return count and error if we have read the whole buffer
if len(buffer) == 0 {
return count, err
}
// Do not propagate an error until the buffer is exhausted
return count, nil
}
// Delegate if the buffer is empty and the destination buffer is large enough
if len(output) >= size {
return reader.Read(output[:(len(output)/size)*size])
}
// Perform a read into the buffer
count, err = reader.Read(buffer[:size])
// Size the buffer down to the read data size
// and restart if we have successfully read some bytes
buffer = buffer[:count]
if len(buffer) > 0 {
goto start
}
// Returning on err/misbehaving noop reader
return 0, err
})
|
| ︙ | ︙ |
Modified src/0dev.org/ioutil/ioutil_test.go
from [f071d3739a]
to [ac59af05f3].
1 2 3 4 5 6 7 8 9 | package ioutil import ( diff "0dev.org/diff" "bytes" "io" "testing" ) | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
package ioutil
import (
diff "0dev.org/diff"
"bytes"
"errors"
"io"
"testing"
)
func TestWriterFunc(t *testing.T) {
var (
input []byte = []byte{0, 1, 2, 3, 4, 5, 6, 7}
output []byte
reader *bytes.Reader = bytes.NewReader(input)
buffer bytes.Buffer
)
reader.WriteTo(WriterFunc(buffer.Write))
output = buffer.Bytes()
// Diff the result against the initial input
delta := diff.Diff(diff.D{len(input), len(output),
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 TestReaderFunc(t *testing.T) {
var (
input []byte = []byte{0, 1, 2, 3, 4, 5, 6, 7}
output []byte
reader *bytes.Reader = bytes.NewReader(input)
buffer bytes.Buffer
)
buffer.ReadFrom(ReaderFunc(reader.Read))
output = buffer.Bytes()
// Diff the result against the initial input
delta := diff.Diff(diff.D{len(input), len(output),
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 TestSizedWriter(t *testing.T) {
var (
buffer bytes.Buffer
writer io.Writer = SizedWriter(&buffer, 4)
)
count, err := writer.Write([]byte("12"))
if count != 2 {
t.Error("Unexpected write count from SizedWriter", count)
}
if err != nil {
t.Error("Unexpected error from SizedWriter", err)
}
count, err = writer.Write([]byte("3456"))
if count != 2 {
t.Error("Unexpected write count from SizedWriter", count)
}
if err != nil {
t.Error("Unexpected error from SizedWriter", err)
}
if buffer.String() != "1234" {
t.Error("Unexpected value in wrapped writer", buffer.String())
}
// Flush the buffer
count, err = writer.Write(nil)
if count != 0 {
t.Error("Unexpected write count from SizedWriter", count)
}
if err != nil {
t.Error("Unexpected error from SizedWriter", err)
}
if buffer.String() != "123456" {
t.Error("Unexpected value in wrapped writer", buffer.String())
}
count, err = writer.Write([]byte("7890"))
if count != 4 {
t.Error("Unexpected write count from SizedWriter", count)
}
if err != nil {
t.Error("Unexpected error from SizedWriter", err)
}
if buffer.String() != "1234567890" {
t.Error("Unexpected value in wrapped writer", buffer.String())
}
}
func TestSizedWriterError(t *testing.T) {
var (
errorWriter io.Writer = WriterFunc(func([]byte) (int, error) {
return 1, errors.New("Invalid write")
})
writer io.Writer = SizedWriter(errorWriter, 2)
)
count, err := writer.Write([]byte("1"))
if count != 1 {
t.Error("Unexpected write count from SizedWriter", count)
}
if err != nil {
t.Error("Unexpected error from SizedWriter", err)
}
count, err = writer.Write([]byte("2"))
if count != 1 {
t.Error("Unexpected write count from SizedWriter", count)
}
if err == nil {
t.Error("Unexpected lack of error from SizedWriter")
}
}
func TestSizedReader(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 = SizedReader(reader, 4)
)
// Expecting a read count of 2
count, err := min.Read(output[:2])
if count != 2 {
t.Error("Invalid read count from SizedReader", count)
}
if err != nil {
t.Error("Unexpected error from SizedReader", 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 SizedReader", count)
}
if err != nil {
t.Error("Unexpected error from SizedReader", 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 SizedReader", count)
}
if err != nil {
t.Error("Unexpected error from SizedReader", err)
}
// Expecting a read count of 0 with an EOF as the buffer should be empty
count, err = min.Read(output[:1])
if count != 0 {
t.Error("Invalid read count from SizedReader", count)
}
if err != io.EOF {
t.Error("Unexpected error from SizedReader", err)
}
}
|