diff --git a/src/lib/io/bytebuffer.go b/src/lib/io/bytebuffer.go index 9c78e8566f4..011166e9c0f 100644 --- a/src/lib/io/bytebuffer.go +++ b/src/lib/io/bytebuffer.go @@ -11,11 +11,8 @@ import ( "os"; ) - -// TODO(r): Do better memory management. - func bytecopy(dst []byte, doff int, src []byte, soff int, count int) { - for i := 0; i < count; i++ { + for ; count > 0; count-- { dst[doff] = src[soff]; doff++; soff++; @@ -26,79 +23,68 @@ func bytecopy(dst []byte, doff int, src []byte, soff int, count int) { // connected to a buffer of bytes. // The zero value for ByteBuffer is an empty buffer ready to use. type ByteBuffer struct { - buf []byte; - off int; // Read from here - len int; // Write to here - cap int; + buf []byte; // contents are the bytes buf[off : len(buf)] + off int; // read at &buf[off], write at &buf[len(buf)] +} + +// Data returns the contents of the unread portion of the buffer; +// len(b.Data()) == b.Len(). +func (b *ByteBuffer) Data() []byte { + return b.buf[b.off : len(b.buf)] +} + +// Len returns the number of bytes of the unread portion of the buffer; +// b.Len() == len(b.Data()). +func (b *ByteBuffer) Len() int { + return len(b.buf) - b.off } // Reset resets the buffer so it has no content. func (b *ByteBuffer) Reset() { - b.off = 0; - b.len = 0; + b.off = len(b.buf) } // Write appends the contents of p to the buffer. The return -// value is the length of p; err is always nil. +// value n is the length of p; err is always nil. func (b *ByteBuffer) Write(p []byte) (n int, err os.Error) { - plen := len(p); - if len(b.buf) == 0 { - b.cap = plen + 1024; - b.buf = make([]byte, b.cap); - b.len = 0; + m := b.Len(); + n = len(p); + + if len(b.buf) + n > cap(b.buf) { + // not enough space at end + buf := b.buf; + if m + n > cap(b.buf) { + // not enough space anywhere + buf = make([]byte, 2*cap(b.buf) + n) + } + bytecopy(buf, 0, b.buf, b.off, m); + b.buf = buf; + b.off = 0 } - if b.len + plen > b.cap { - b.cap = 2*(b.cap + plen); - nb := make([]byte, b.cap); - bytecopy(nb, 0, b.buf, 0, b.len); - b.buf = nb; - } - bytecopy(b.buf, b.len, p, 0, plen); - b.len += plen; - return plen, nil; + + b.buf = b.buf[0 : b.off + m + n]; + bytecopy(b.buf, b.off + m, p, 0, n); + return n, nil } // Read reads the next len(p) bytes from the buffer or until the buffer -// is drained. The return value is the number of bytes read; err is always nil. +// is drained. The return value n is the number of bytes read; err is always nil. func (b *ByteBuffer) Read(p []byte) (n int, err os.Error) { - plen := len(p); - if len(b.buf) == 0 { - return 0, nil - } - if b.off == b.len { // empty buffer - b.Reset(); - return 0, nil - } - if plen > b.len - b.off { - plen = b.len - b.off - } - bytecopy(p, 0, b.buf, b.off, plen); - b.off += plen; - return plen, nil; -} + m := b.Len(); + n = len(p); -// Len returns the length of the underlying buffer. -func (b *ByteBuffer) Len() int { - return b.len -} + if n > m { + // more bytes requested than available + n = m + } -// Off returns the location within the buffer of the next byte to be read. -func (b *ByteBuffer) Off() int { - return b.off -} - -// Data returns the contents of the unread portion of the buffer. -func (b *ByteBuffer) Data() []byte { - return b.buf[b.off:b.len] + bytecopy(p, 0, b.buf, b.off, n); + b.off += n; + return n, nil } // NewByteBufferFromArray creates and initializes a new ByteBuffer // with buf as its initial contents. func NewByteBufferFromArray(buf []byte) *ByteBuffer { - b := new(ByteBuffer); - b.buf = buf; - b.off = 0; - b.len = len(buf); - b.cap = len(buf); - return b; + return &ByteBuffer{buf, 0}; } diff --git a/src/lib/io/bytebuffer_test.go b/src/lib/io/bytebuffer_test.go new file mode 100644 index 00000000000..6a4f3c9b907 --- /dev/null +++ b/src/lib/io/bytebuffer_test.go @@ -0,0 +1,146 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package io + +import ( + "io"; + "rand"; + "testing"; +) + + +const N = 10000; // make this bigger for a larger (and slower) test +var data []byte; // test data for write tests + + +func init() { + data = make([]byte, N); + for i := 0; i < len(data); i++ { + data[i] = 'a' + byte(i % 26) + } +} + + +// Verify that contents of buf match the string s. +func check(t *testing.T, testname string, buf *ByteBuffer, s string) { + if buf.Len() != len(buf.Data()) { + t.Errorf("%s: buf.Len() == %d, len(buf.Data()) == %d\n", testname, buf.Len(), len(buf.Data())) + } + + if buf.Len() != len(s) { + t.Errorf("%s: buf.Len() == %d, len(s) == %d\n", testname, buf.Len(), len(s)) + } + + if string(buf.Data()) != s { + t.Errorf("%s: string(buf.Data()) == %q, s == %q\n", testname, string(buf.Data()), s) + } +} + + +// Fill buf through n writes of fub. +// The initial contents of buf corresponds to the string s; +// the result is the final contents of buf returned as a string. +func fill(t *testing.T, testname string, buf *ByteBuffer, s string, n int, fub []byte) string { + check(t, testname + " (fill 1)", buf, s); + for ; n > 0; n-- { + m, err := buf.Write(fub); + if m != len(fub) { + t.Errorf(testname + " (fill 2): m == %d, expected %d\n", m, len(fub)); + } + if err != nil { + t.Errorf(testname + " (fill 3): err should always be nil, found err == %s\n", err); + } + s += string(fub); + check(t, testname + " (fill 4)", buf, s); + } + return s; +} + + +// Empty buf through repeated reads into fub. +// The initial contents of buf corresponds to the string s. +func empty(t *testing.T, testname string, buf *ByteBuffer, s string, fub []byte) { + check(t, testname + " (empty 1)", buf, s); + + for { + n, err := buf.Read(fub); + if n == 0 { + break; + } + if err != nil { + t.Errorf(testname + " (empty 2): err should always be nil, found err == %s\n", err); + } + s = s[n : len(s)]; + check(t, testname + " (empty 3)", buf, s); + } + + check(t, testname + " (empty 4)", buf, ""); +} + + +func TestBasicOperations(t *testing.T) { + var buf ByteBuffer; + + for i := 0; i < 5; i++ { + check(t, "TestBasicOperations (1)", &buf, ""); + + buf.Reset(); + check(t, "TestBasicOperations (2)", &buf, ""); + + n, err := buf.Write(data[0 : 1]); + if n != 1 { + t.Errorf("wrote 1 byte, but n == %d\n", n); + } + if err != nil { + t.Errorf("err should always be nil, but err == %s\n", err); + } + check(t, "TestBasicOperations (3)", &buf, "a"); + + n, err = buf.Write(data[1 : 26]); + if n != 25 { + t.Errorf("wrote 25 bytes, but n == %d\n", n); + } + check(t, "TestBasicOperations (4)", &buf, string(data[0 : 26])); + + empty(t, "TestBasicOperations (5)", &buf, string(data[0 : 26]), make([]byte, 5)); + empty(t, "TestBasicOperations (6)", &buf, "", make([]byte, 100)); + } +} + + +func TestLargeWrites(t *testing.T) { + var buf ByteBuffer; + for i := 3; i < 30; i += 3 { + s := fill(t, "TestLargeWrites (1)", &buf, "", 5, data); + empty(t, "TestLargeWrites (2)", &buf, s, make([]byte, len(data)/i)); + } + check(t, "TestLargeWrites (3)", &buf, ""); +} + + +func TestLargeReads(t *testing.T) { + var buf ByteBuffer; + for i := 3; i < 30; i += 3 { + s := fill(t, "TestLargeReads (1)", &buf, "", 5, data[0 : len(data)/i]); + empty(t, "TestLargeReads (2)", &buf, s, make([]byte, len(data))); + } + check(t, "TestLargeReads (3)", &buf, ""); +} + + +func TestMixedReadsAndWrites(t *testing.T) { + var buf ByteBuffer; + s := ""; + for i := 0; i < 50; i++ { + wlen := rand.Intn(len(data)); + s = fill(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, data[0 : wlen]); + + rlen := rand.Intn(len(data)); + fub := make([]byte, rlen); + n, err := buf.Read(fub); + s = s[n : len(s)]; + } + empty(t, "TestMixedReadsAndWrites (2)", &buf, s, make([]byte, buf.Len())); +}