object-cache: use golang bytes.Buffer and bytes.NewReader instead of custom implementation. (#2108)
parent
7bde27032d
commit
01cbacd803
@ -1,143 +0,0 @@ |
||||
/* |
||||
* Minio Cloud Storage, (C) 2016 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
package objcache |
||||
|
||||
import ( |
||||
"bytes" |
||||
"errors" |
||||
"io" |
||||
"time" |
||||
) |
||||
|
||||
// A Buffer is a variable-sized buffer of bytes with Read, Write and Seek methods.
|
||||
// The zero value for Buffer is an empty buffer ready to use.
|
||||
type Buffer struct { |
||||
buf []byte // contents are the bytes buf[off : len(buf)]
|
||||
off int // read at &buf[off], write at &buf[len(buf)]
|
||||
bootstrap [64]byte // memory to hold first slice; helps small buffers (Printf) avoid allocation.
|
||||
accessTime time.Time // accessTime holds value of the last access time of this buffer.
|
||||
} |
||||
|
||||
// NewBuffer creates and initializes a new Buffer using buf as its initial
|
||||
// contents. It is intended to prepare a Buffer to read existing data. It
|
||||
// can also be used to size the internal buffer for writing. To do that,
|
||||
// buf should have the desired capacity but a length of zero.
|
||||
//
|
||||
// In most cases, new(Buffer) (or just declaring a Buffer variable) is
|
||||
// sufficient to initialize a Buffer.
|
||||
func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } |
||||
|
||||
// Len returns the number of bytes of the unread portion of the buffer;
|
||||
// b.Len() == len(b.Bytes()).
|
||||
func (b *Buffer) Len() int { return len(b.buf) - b.off } |
||||
|
||||
// Size returns the original length of the underlying byte slice.
|
||||
// Size is the number of bytes available for reading via ReadAt.
|
||||
// The returned value is always the same and is not affected by calls
|
||||
// to any other method.
|
||||
func (b *Buffer) Size() int64 { return int64(len(b.buf)) } |
||||
|
||||
// makeSlice allocates a slice of size n. If the allocation fails, it panics
|
||||
// with ErrTooLarge.
|
||||
func makeSlice(n int) []byte { |
||||
// If the make fails, give a known error.
|
||||
defer func() { |
||||
if recover() != nil { |
||||
panic(bytes.ErrTooLarge) |
||||
} |
||||
}() |
||||
return make([]byte, n) |
||||
} |
||||
|
||||
// grow grows the buffer to guarantee space for n more bytes.
|
||||
// It returns the index where bytes should be written.
|
||||
// If the buffer can't grow it will panic with ErrTooLarge.
|
||||
func (b *Buffer) grow(n int) int { |
||||
m := b.Len() |
||||
// If buffer is empty, reset to recover space.
|
||||
if m == 0 && b.off != 0 { |
||||
// Reuse buffer space.
|
||||
b.buf = b.buf[0:0] |
||||
} |
||||
if len(b.buf)+n > cap(b.buf) { |
||||
var buf []byte |
||||
if b.buf == nil && n <= len(b.bootstrap) { |
||||
buf = b.bootstrap[0:] |
||||
} else if m+n <= cap(b.buf)/2 { |
||||
// We can slide things down instead of allocating a new
|
||||
// slice. We only need m+n <= cap(b.buf) to slide, but
|
||||
// we instead let capacity get twice as large so we
|
||||
// don't spend all our time copying.
|
||||
copy(b.buf[:], b.buf[b.off:]) |
||||
buf = b.buf[:m] |
||||
} else { |
||||
// not enough space anywhere
|
||||
buf = makeSlice(2*cap(b.buf) + n) |
||||
copy(buf, b.buf[b.off:]) |
||||
} |
||||
b.buf = buf |
||||
b.off = 0 |
||||
} |
||||
b.buf = b.buf[0 : b.off+m+n] |
||||
return b.off + m |
||||
} |
||||
|
||||
// Write appends the contents of p to the buffer, growing the buffer as
|
||||
// needed. The return value n is the length of p; err is always nil. If the
|
||||
// buffer becomes too large, Write will panic with ErrTooLarge.
|
||||
func (b *Buffer) Write(p []byte) (n int, err error) { |
||||
m := b.grow(len(p)) |
||||
return copy(b.buf[m:], p), nil |
||||
} |
||||
|
||||
// Read reads the next len(p) bytes from the buffer or until the buffer
|
||||
// is drained. The return value n is the number of bytes read. If the
|
||||
// buffer has no data to return, err is io.EOF (unless len(p) is zero);
|
||||
// otherwise it is nil.
|
||||
func (b *Buffer) Read(p []byte) (n int, err error) { |
||||
if len(p) == 0 { |
||||
return 0, nil |
||||
} |
||||
if b.off >= len(b.buf) { |
||||
return 0, io.EOF |
||||
} |
||||
n = copy(p, b.buf[b.off:]) |
||||
b.off += n |
||||
return |
||||
} |
||||
|
||||
// Seek implements the io.Seeker interface.
|
||||
func (b *Buffer) Seek(offset int64, whence int) (int64, error) { |
||||
var abs int64 |
||||
switch whence { |
||||
case 0: // Whence 0 sets the offset as new offset.
|
||||
abs = offset |
||||
case 1: // Whence 1 sets the current offset and offset as new offset.
|
||||
abs = int64(b.off) + offset |
||||
case 2: // Whence 2 sets the total size of the buffer and offset
|
||||
// as new offset, not supported yet. // FIXME.
|
||||
return 0, errors.New("cache.Buffer.Seek: whence os.SEEK_END is not supported") |
||||
default: |
||||
return 0, errors.New("cache.Buffer.Seek: invalid whence") |
||||
} |
||||
if abs < 0 { |
||||
return 0, errors.New("cache.Buffer.Seek: negative position") |
||||
} |
||||
b.off = int(abs) |
||||
return abs, nil |
||||
} |
@ -1,95 +0,0 @@ |
||||
/* |
||||
* Minio Cloud Storage, (C) 2016 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package objcache |
||||
|
||||
import ( |
||||
"io" |
||||
"io/ioutil" |
||||
"os" |
||||
"testing" |
||||
) |
||||
|
||||
// Tests different types of seekable operations on an allocated buffer.
|
||||
func TestBufferSeek(t *testing.T) { |
||||
r := NewBuffer([]byte("0123456789")) |
||||
tests := []struct { |
||||
off int64 |
||||
seek int |
||||
n int |
||||
want string |
||||
wantpos int64 |
||||
seekerr string |
||||
}{ |
||||
{seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"}, |
||||
{seek: os.SEEK_SET, off: 1, n: 1, want: "1"}, |
||||
{seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"}, |
||||
{seek: os.SEEK_SET, off: -1, seekerr: "cache.Buffer.Seek: negative position"}, |
||||
{seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33}, |
||||
{seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1}, |
||||
{seek: os.SEEK_SET, n: 5, want: "01234"}, |
||||
{seek: os.SEEK_CUR, n: 5, want: "56789"}, |
||||
{seek: os.SEEK_END, off: -1, seekerr: "cache.Buffer.Seek: whence os.SEEK_END is not supported"}, |
||||
} |
||||
|
||||
for i, tt := range tests { |
||||
pos, err := r.Seek(tt.off, tt.seek) |
||||
if err == nil && tt.seekerr != "" { |
||||
t.Errorf("%d. want seek error %q", i, tt.seekerr) |
||||
continue |
||||
} |
||||
if err != nil && err.Error() != tt.seekerr { |
||||
t.Errorf("%d. seek error = %q; want %q", i, err.Error(), tt.seekerr) |
||||
continue |
||||
} |
||||
if tt.wantpos != 0 && tt.wantpos != pos { |
||||
t.Errorf("%d. pos = %d, want %d", i, pos, tt.wantpos) |
||||
} |
||||
buf := make([]byte, tt.n) |
||||
n, err := r.Read(buf) |
||||
if err != nil { |
||||
t.Errorf("%d. read = %v", i, err) |
||||
continue |
||||
} |
||||
got := string(buf[:n]) |
||||
if got != tt.want { |
||||
t.Errorf("%d. got %q; want %q", i, got, tt.want) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Tests read operation after big seek.
|
||||
func TestReadAfterBigSeek(t *testing.T) { |
||||
r := NewBuffer([]byte("0123456789")) |
||||
if _, err := r.Seek(1<<31+5, os.SEEK_SET); err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF { |
||||
t.Errorf("Read = %d, %v; want 0, EOF", n, err) |
||||
} |
||||
} |
||||
|
||||
// tests that Len is affected by reads, but Size is not.
|
||||
func TestBufferLenSize(t *testing.T) { |
||||
r := NewBuffer([]byte("abc")) |
||||
io.CopyN(ioutil.Discard, r, 1) |
||||
if r.Len() != 2 { |
||||
t.Errorf("Len = %d; want 2", r.Len()) |
||||
} |
||||
if r.Size() != 3 { |
||||
t.Errorf("Size = %d; want 3", r.Size()) |
||||
} |
||||
} |
Loading…
Reference in new issue