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 }