github.com/btcsuite/btcd@v0.24.0/database/ffldb/mockfile_test.go (about)

     1  // Copyright (c) 2015-2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file is part of the ffldb package rather than the ffldb_test package as
     6  // it is part of the whitebox testing.
     7  
     8  package ffldb
     9  
    10  import (
    11  	"errors"
    12  	"io"
    13  	"sync"
    14  )
    15  
    16  // Errors used for the mock file.
    17  var (
    18  	// errMockFileClosed is used to indicate a mock file is closed.
    19  	errMockFileClosed = errors.New("file closed")
    20  
    21  	// errInvalidOffset is used to indicate an offset that is out of range
    22  	// for the file was provided.
    23  	errInvalidOffset = errors.New("invalid offset")
    24  
    25  	// errSyncFail is used to indicate simulated sync failure.
    26  	errSyncFail = errors.New("simulated sync failure")
    27  )
    28  
    29  // mockFile implements the filer interface and used in order to force failures
    30  // the database code related to reading and writing from the flat block files.
    31  // A maxSize of -1 is unlimited.
    32  type mockFile struct {
    33  	sync.RWMutex
    34  	maxSize      int64
    35  	data         []byte
    36  	forceSyncErr bool
    37  	closed       bool
    38  }
    39  
    40  // Close closes the mock file without releasing any data associated with it.
    41  // This allows it to be "reopened" without losing the data.
    42  //
    43  // This is part of the filer implementation.
    44  func (f *mockFile) Close() error {
    45  	f.Lock()
    46  	defer f.Unlock()
    47  
    48  	if f.closed {
    49  		return errMockFileClosed
    50  	}
    51  	f.closed = true
    52  	return nil
    53  }
    54  
    55  // ReadAt reads len(b) bytes from the mock file starting at byte offset off. It
    56  // returns the number of bytes read and the error, if any.  ReadAt always
    57  // returns a non-nil error when n < len(b). At end of file, that error is
    58  // io.EOF.
    59  //
    60  // This is part of the filer implementation.
    61  func (f *mockFile) ReadAt(b []byte, off int64) (int, error) {
    62  	f.RLock()
    63  	defer f.RUnlock()
    64  
    65  	if f.closed {
    66  		return 0, errMockFileClosed
    67  	}
    68  	maxSize := int64(len(f.data))
    69  	if f.maxSize > -1 && maxSize > f.maxSize {
    70  		maxSize = f.maxSize
    71  	}
    72  	if off < 0 || off > maxSize {
    73  		return 0, errInvalidOffset
    74  	}
    75  
    76  	// Limit to the max size field, if set.
    77  	numToRead := int64(len(b))
    78  	endOffset := off + numToRead
    79  	if endOffset > maxSize {
    80  		numToRead = maxSize - off
    81  	}
    82  
    83  	copy(b, f.data[off:off+numToRead])
    84  	if numToRead < int64(len(b)) {
    85  		return int(numToRead), io.EOF
    86  	}
    87  	return int(numToRead), nil
    88  }
    89  
    90  // Truncate changes the size of the mock file.
    91  //
    92  // This is part of the filer implementation.
    93  func (f *mockFile) Truncate(size int64) error {
    94  	f.Lock()
    95  	defer f.Unlock()
    96  
    97  	if f.closed {
    98  		return errMockFileClosed
    99  	}
   100  	maxSize := int64(len(f.data))
   101  	if f.maxSize > -1 && maxSize > f.maxSize {
   102  		maxSize = f.maxSize
   103  	}
   104  	if size > maxSize {
   105  		return errInvalidOffset
   106  	}
   107  
   108  	f.data = f.data[:size]
   109  	return nil
   110  }
   111  
   112  // Write writes len(b) bytes to the mock file. It returns the number of bytes
   113  // written and an error, if any.  Write returns a non-nil error any time
   114  // n != len(b).
   115  //
   116  // This is part of the filer implementation.
   117  func (f *mockFile) WriteAt(b []byte, off int64) (int, error) {
   118  	f.Lock()
   119  	defer f.Unlock()
   120  
   121  	if f.closed {
   122  		return 0, errMockFileClosed
   123  	}
   124  	maxSize := f.maxSize
   125  	if maxSize < 0 {
   126  		maxSize = 100 * 1024 // 100KiB
   127  	}
   128  	if off < 0 || off > maxSize {
   129  		return 0, errInvalidOffset
   130  	}
   131  
   132  	// Limit to the max size field, if set, and grow the slice if needed.
   133  	numToWrite := int64(len(b))
   134  	if off+numToWrite > maxSize {
   135  		numToWrite = maxSize - off
   136  	}
   137  	if off+numToWrite > int64(len(f.data)) {
   138  		newData := make([]byte, off+numToWrite)
   139  		copy(newData, f.data)
   140  		f.data = newData
   141  	}
   142  
   143  	copy(f.data[off:], b[:numToWrite])
   144  	if numToWrite < int64(len(b)) {
   145  		return int(numToWrite), io.EOF
   146  	}
   147  	return int(numToWrite), nil
   148  }
   149  
   150  // Sync doesn't do anything for mock files.  However, it will return an error if
   151  // the mock file's forceSyncErr flag is set.
   152  //
   153  // This is part of the filer implementation.
   154  func (f *mockFile) Sync() error {
   155  	if f.forceSyncErr {
   156  		return errSyncFail
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  // Ensure the mockFile type implements the filer interface.
   163  var _ filer = (*mockFile)(nil)