github.com/scottcagno/storage@v1.8.0/pkg/_junk/_binary/reader.go (about)

     1  package binary
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  	"os"
     7  )
     8  
     9  // Reader provides a read-only file descriptor
    10  type Reader struct {
    11  	path string   // path of the file that is currently open
    12  	fd   *os.File // underlying file to read from
    13  	open bool     // is the file open
    14  }
    15  
    16  // OpenReader returns a *reader for the file at the provided path
    17  func OpenReader(path string) (*Reader, error) {
    18  	// open file at specified path
    19  	fd, err := os.OpenFile(path, os.O_RDONLY, 0666)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  	// return new reader
    24  	return &Reader{
    25  		path: path,
    26  		fd:   fd,
    27  		open: true,
    28  	}, nil
    29  }
    30  
    31  // ReadFrom checks the given path and if it matches, simply returns
    32  // the same reader, but if it is different it opens a new one recycling
    33  // the same file descriptor. this allows you to read from multiple files
    34  // fairly quickly and pain free.
    35  func (r *Reader) ReadFrom(path string) (*Reader, error) {
    36  	// if there is already a file opened
    37  	if r.open {
    38  		// and if that file has the same path, simply return r
    39  		if r.path == path {
    40  			return r, nil
    41  		}
    42  		// otherwise, a file is still opened at a different
    43  		// location, so we must close it before we continue
    44  		err := r.Close()
    45  		if err != nil {
    46  			return nil, err
    47  		}
    48  	}
    49  	// open a file at a new path (if we're here then the file is closed)
    50  	fd, err := os.OpenFile(path, os.O_RDONLY, 0666)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	r.path = path
    55  	r.fd = fd
    56  	r.open = true
    57  	return r, nil
    58  }
    59  
    60  func DecodeEntry(r io.Reader) (*Entry, error) {
    61  	// make buffer
    62  	buf := make([]byte, 26)
    63  	// read entry id
    64  	_, err := r.Read(buf[0:10])
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	// read entry key length
    69  	_, err = r.Read(buf[10:18])
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	// read entry value length
    74  	_, err = r.Read(buf[18:26])
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	// decode id
    79  	id, _ := binary.Varint(buf[0:10])
    80  	// decode key length
    81  	klen := binary.LittleEndian.Uint64(buf[10:18])
    82  	// decode value length
    83  	vlen := binary.LittleEndian.Uint64(buf[18:26])
    84  	// make entry to read data into
    85  	e := &Entry{
    86  		Id:    id,
    87  		Key:   make([]byte, klen),
    88  		Value: make([]byte, vlen),
    89  	}
    90  	// read key from data into entry key
    91  	_, err = r.Read(e.Key)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	// read value key from data into entry value
    96  	_, err = r.Read(e.Value)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	// return entry
   101  	return e, nil
   102  }
   103  
   104  func DecodeEntryAt(r io.ReaderAt, offset int64) (*Entry, error) {
   105  	// make buffer
   106  	buf := make([]byte, 26)
   107  	// read entry id
   108  	n, err := r.ReadAt(buf[0:10], offset)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	// update offset
   113  	offset += int64(n)
   114  	// read entry key length
   115  	n, err = r.ReadAt(buf[10:18], offset)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	// update offset
   120  	offset += int64(n)
   121  	// read entry value length
   122  	n, err = r.ReadAt(buf[18:26], offset)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	// update offset for reading key data a bit below
   127  	offset += int64(n)
   128  	// decode id
   129  	id, _ := binary.Varint(buf[0:10])
   130  	// decode key length
   131  	klen := binary.LittleEndian.Uint64(buf[10:18])
   132  	// decode value length
   133  	vlen := binary.LittleEndian.Uint64(buf[18:26])
   134  	// make entry to read data into
   135  	e := &Entry{
   136  		Id:    id,
   137  		Key:   make([]byte, klen),
   138  		Value: make([]byte, vlen),
   139  	}
   140  	// read key from data into entry key
   141  	n, err = r.ReadAt(e.Key, offset)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	// update offset
   146  	offset += int64(n)
   147  	// read value key from data into entry value
   148  	n, err = r.ReadAt(e.Value, offset)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	// update offset
   153  	offset += int64(n)
   154  	// return entry
   155  	return e, nil
   156  }
   157  
   158  // ReadEntryIndex reads the next encoded entry index, sequentially
   159  func (r *Reader) ReadEntryIndex() (*EntryIndex, error) {
   160  	// check to make sure file is open
   161  	if !r.open {
   162  		return nil, ErrFileClosed
   163  	}
   164  	// call decode entry
   165  	return DecodeEntryIndex(r.fd)
   166  }
   167  
   168  // ReadEntryIndexAt reads the encoded entry index at the offset provided
   169  func (r *Reader) ReadEntryIndexAt(offset int64) (*EntryIndex, error) {
   170  	// check to make sure file is open
   171  	if !r.open {
   172  		return nil, ErrFileClosed
   173  	}
   174  	// call decode entry at
   175  	return DecodeEntryIndexAt(r.fd, offset)
   176  }
   177  
   178  // ReadEntry reads the next encoded entry, sequentially
   179  func (r *Reader) ReadEntry() (*Entry, error) {
   180  	// check to make sure file is open
   181  	if !r.open {
   182  		return nil, ErrFileClosed
   183  	}
   184  	// call decode entry
   185  	return DecodeEntry(r.fd)
   186  }
   187  
   188  // ReadEntryAt reads the encoded entry at the offset provided
   189  func (r *Reader) ReadEntryAt(offset int64) (*Entry, error) {
   190  	// check to make sure file is open
   191  	if !r.open {
   192  		return nil, ErrFileClosed
   193  	}
   194  	// call decode entry at
   195  	return DecodeEntryAt(r.fd, offset)
   196  }
   197  
   198  // Offset returns the *Reader's current file pointer offset
   199  func (r *Reader) Offset() (int64, error) {
   200  	// check to make sure file is open
   201  	if !r.open {
   202  		return -1, ErrFileClosed
   203  	}
   204  	// return current offset
   205  	return r.fd.Seek(0, io.SeekCurrent)
   206  }
   207  
   208  // Seek exposes io.Seeker
   209  func (r *Reader) Seek(offset int64, whence int) (int64, error) {
   210  	// check to make sure file is open
   211  	if !r.open {
   212  		return -1, ErrFileClosed
   213  	}
   214  	// seek to offset according to whence
   215  	return r.fd.Seek(offset, whence)
   216  }
   217  
   218  // Close simply closes the *Reader
   219  func (r *Reader) Close() error {
   220  	// check to make sure file is not already closed
   221  	if !r.open {
   222  		return ErrFileClosed
   223  	}
   224  	// close the reader
   225  	err := r.fd.Close()
   226  	if err != nil {
   227  		return err
   228  	}
   229  	r.open = false
   230  	r.path = ""
   231  	return nil
   232  }