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