github.com/scottcagno/storage@v1.8.0/pkg/lsmt/binary/reader.go (about)

     1  package binary
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  )
     7  
     8  // Reader provides a read-only file descriptor
     9  type Reader struct {
    10  	path string   // path of the file that is currently open
    11  	fd   *os.File // underlying file to read from
    12  	open bool     // is the file open
    13  }
    14  
    15  // OpenReader returns a *reader for the file at the provided path
    16  func OpenReader(path string) (*Reader, error) {
    17  	// open file at specified path
    18  	fd, err := os.OpenFile(path, os.O_RDONLY, 0666)
    19  	if err != nil {
    20  		return nil, err
    21  	}
    22  	// return new reader
    23  	return &Reader{
    24  		path: path,
    25  		fd:   fd,
    26  		open: true,
    27  	}, nil
    28  }
    29  
    30  // ReadFrom checks the given path and if it matches, simply returns
    31  // the same reader, but if it is different it opens a new one recycling
    32  // the same file descriptor. this allows you to read from multiple files
    33  // fairly quickly and pain free.
    34  func (r *Reader) ReadFrom(path string) (*Reader, error) {
    35  	// if there is already a file opened
    36  	if r.open {
    37  		// and if that file has the same path, simply return r
    38  		if r.path == path {
    39  			return r, nil
    40  		}
    41  		// otherwise, a file is still opened at a different
    42  		// location, so we must close it before we continue
    43  		err := r.Close()
    44  		if err != nil {
    45  			return nil, err
    46  		}
    47  	}
    48  	// open a file at a new path (if we're here then the file is closed)
    49  	fd, err := os.OpenFile(path, os.O_RDONLY, 0666)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	r.path = path
    54  	r.fd = fd
    55  	r.open = true
    56  	return r, nil
    57  }
    58  
    59  // ReadIndex reads the next encoded index, sequentially
    60  func (r *Reader) ReadIndex() (*Index, error) {
    61  	// check to make sure file is open
    62  	if !r.open {
    63  		return nil, ErrFileClosed
    64  	}
    65  	// call decode entry
    66  	return DecodeIndex(r.fd)
    67  }
    68  
    69  // ReadIndexAt reads the encoded entry index at the offset provided
    70  func (r *Reader) ReadIndexAt(offset int64) (*Index, error) {
    71  	// check to make sure file is open
    72  	if !r.open {
    73  		return nil, ErrFileClosed
    74  	}
    75  	// call decode entry at
    76  	return DecodeIndexAt(r.fd, offset)
    77  }
    78  
    79  // ReadEntry reads the next encoded entry, sequentially
    80  func (r *Reader) ReadEntry() (*Entry, error) {
    81  	// check to make sure file is open
    82  	if !r.open {
    83  		return nil, ErrFileClosed
    84  	}
    85  	// call decode entry
    86  	return DecodeEntry(r.fd)
    87  }
    88  
    89  // ReadEntryAt reads the encoded entry at the offset provided
    90  func (r *Reader) ReadEntryAt(offset int64) (*Entry, error) {
    91  	// check to make sure file is open
    92  	if !r.open {
    93  		return nil, ErrFileClosed
    94  	}
    95  	// call decode entry at
    96  	return DecodeEntryAt(r.fd, offset)
    97  }
    98  
    99  // Offset returns the *Reader's current file pointer offset
   100  func (r *Reader) Offset() (int64, error) {
   101  	// check to make sure file is open
   102  	if !r.open {
   103  		return -1, ErrFileClosed
   104  	}
   105  	// return current offset
   106  	return r.fd.Seek(0, io.SeekCurrent)
   107  }
   108  
   109  // Seek exposes io.Seeker
   110  func (r *Reader) Seek(offset int64, whence int) (int64, error) {
   111  	// check to make sure file is open
   112  	if !r.open {
   113  		return -1, ErrFileClosed
   114  	}
   115  	// seek to offset according to whence
   116  	return r.fd.Seek(offset, whence)
   117  }
   118  
   119  // Close simply closes the *Reader
   120  func (r *Reader) Close() error {
   121  	// check to make sure file is not already closed
   122  	if !r.open {
   123  		return ErrFileClosed
   124  	}
   125  	// close the reader
   126  	err := r.fd.Close()
   127  	if err != nil {
   128  		return err
   129  	}
   130  	r.open = false
   131  	r.path = ""
   132  	return nil
   133  }