164 lines
3.9 KiB
Go
164 lines
3.9 KiB
Go
|
// Copyright (c) 2015-2016 The btcsuite developers
|
||
|
// Use of this source code is governed by an ISC
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
// This file is part of the ffldb package rather than the ffldb_test package as
|
||
|
// it is part of the whitebox testing.
|
||
|
|
||
|
package ffldb
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
// Errors used for the mock file.
|
||
|
var (
|
||
|
// errMockFileClosed is used to indicate a mock file is closed.
|
||
|
errMockFileClosed = errors.New("file closed")
|
||
|
|
||
|
// errInvalidOffset is used to indicate an offset that is out of range
|
||
|
// for the file was provided.
|
||
|
errInvalidOffset = errors.New("invalid offset")
|
||
|
|
||
|
// errSyncFail is used to indicate simulated sync failure.
|
||
|
errSyncFail = errors.New("simulated sync failure")
|
||
|
)
|
||
|
|
||
|
// mockFile implements the filer interface and used in order to force failures
|
||
|
// the database code related to reading and writing from the flat block files.
|
||
|
// A maxSize of -1 is unlimited.
|
||
|
type mockFile struct {
|
||
|
sync.RWMutex
|
||
|
maxSize int64
|
||
|
data []byte
|
||
|
forceSyncErr bool
|
||
|
closed bool
|
||
|
}
|
||
|
|
||
|
// Close closes the mock file without releasing any data associated with it.
|
||
|
// This allows it to be "reopened" without losing the data.
|
||
|
//
|
||
|
// This is part of the filer implementation.
|
||
|
func (f *mockFile) Close() error {
|
||
|
f.Lock()
|
||
|
defer f.Unlock()
|
||
|
|
||
|
if f.closed {
|
||
|
return errMockFileClosed
|
||
|
}
|
||
|
f.closed = true
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ReadAt reads len(b) bytes from the mock file starting at byte offset off. It
|
||
|
// returns the number of bytes read and the error, if any. ReadAt always
|
||
|
// returns a non-nil error when n < len(b). At end of file, that error is
|
||
|
// io.EOF.
|
||
|
//
|
||
|
// This is part of the filer implementation.
|
||
|
func (f *mockFile) ReadAt(b []byte, off int64) (int, error) {
|
||
|
f.RLock()
|
||
|
defer f.RUnlock()
|
||
|
|
||
|
if f.closed {
|
||
|
return 0, errMockFileClosed
|
||
|
}
|
||
|
maxSize := int64(len(f.data))
|
||
|
if f.maxSize > -1 && maxSize > f.maxSize {
|
||
|
maxSize = f.maxSize
|
||
|
}
|
||
|
if off < 0 || off > maxSize {
|
||
|
return 0, errInvalidOffset
|
||
|
}
|
||
|
|
||
|
// Limit to the max size field, if set.
|
||
|
numToRead := int64(len(b))
|
||
|
endOffset := off + numToRead
|
||
|
if endOffset > maxSize {
|
||
|
numToRead = maxSize - off
|
||
|
}
|
||
|
|
||
|
copy(b, f.data[off:off+numToRead])
|
||
|
if numToRead < int64(len(b)) {
|
||
|
return int(numToRead), io.EOF
|
||
|
}
|
||
|
return int(numToRead), nil
|
||
|
}
|
||
|
|
||
|
// Truncate changes the size of the mock file.
|
||
|
//
|
||
|
// This is part of the filer implementation.
|
||
|
func (f *mockFile) Truncate(size int64) error {
|
||
|
f.Lock()
|
||
|
defer f.Unlock()
|
||
|
|
||
|
if f.closed {
|
||
|
return errMockFileClosed
|
||
|
}
|
||
|
maxSize := int64(len(f.data))
|
||
|
if f.maxSize > -1 && maxSize > f.maxSize {
|
||
|
maxSize = f.maxSize
|
||
|
}
|
||
|
if size > maxSize {
|
||
|
return errInvalidOffset
|
||
|
}
|
||
|
|
||
|
f.data = f.data[:size]
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Write writes len(b) bytes to the mock file. It returns the number of bytes
|
||
|
// written and an error, if any. Write returns a non-nil error any time
|
||
|
// n != len(b).
|
||
|
//
|
||
|
// This is part of the filer implementation.
|
||
|
func (f *mockFile) WriteAt(b []byte, off int64) (int, error) {
|
||
|
f.Lock()
|
||
|
defer f.Unlock()
|
||
|
|
||
|
if f.closed {
|
||
|
return 0, errMockFileClosed
|
||
|
}
|
||
|
maxSize := f.maxSize
|
||
|
if maxSize < 0 {
|
||
|
maxSize = 100 * 1024 // 100KiB
|
||
|
}
|
||
|
if off < 0 || off > maxSize {
|
||
|
return 0, errInvalidOffset
|
||
|
}
|
||
|
|
||
|
// Limit to the max size field, if set, and grow the slice if needed.
|
||
|
numToWrite := int64(len(b))
|
||
|
if off+numToWrite > maxSize {
|
||
|
numToWrite = maxSize - off
|
||
|
}
|
||
|
if off+numToWrite > int64(len(f.data)) {
|
||
|
newData := make([]byte, off+numToWrite)
|
||
|
copy(newData, f.data)
|
||
|
f.data = newData
|
||
|
}
|
||
|
|
||
|
copy(f.data[off:], b[:numToWrite])
|
||
|
if numToWrite < int64(len(b)) {
|
||
|
return int(numToWrite), io.EOF
|
||
|
}
|
||
|
return int(numToWrite), nil
|
||
|
}
|
||
|
|
||
|
// Sync doesn't do anything for mock files. However, it will return an error if
|
||
|
// the mock file's forceSyncErr flag is set.
|
||
|
//
|
||
|
// This is part of the filer implementation.
|
||
|
func (f *mockFile) Sync() error {
|
||
|
if f.forceSyncErr {
|
||
|
return errSyncFail
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Ensure the mockFile type implements the filer interface.
|
||
|
var _ filer = (*mockFile)(nil)
|