github.com/scottcagno/storage@v1.8.0/pkg/_junk/_lsmtree/encoding/binary/datafile.go (about)

     1  package binary
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  )
    10  
    11  var (
    12  	ErrFileClosed = errors.New("error: file is closed")
    13  	ErrBadEntry   = errors.New("error: bad entry")
    14  )
    15  
    16  // DataFile is is a syncronized binary reader and writer
    17  type DataFile struct {
    18  	sync.RWMutex
    19  	r *Reader
    20  	w *Writer
    21  }
    22  
    23  // OpenDataFile opens and returns a new datafile
    24  func OpenDataFile(path string) (*DataFile, error) {
    25  	// create and sanitize the path
    26  	path, err := Touch(path)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	// open a new reader
    31  	r, err := OpenReader(path)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	// open a new writer
    36  	w, err := OpenWriter(path)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	// init data file and return
    41  	return &DataFile{
    42  		r: r,
    43  		w: w,
    44  	}, nil
    45  }
    46  
    47  // WriteEntry writes an entry in an append-only fashion
    48  func (d *DataFile) WriteEntry(de *DataEntry) (int64, error) {
    49  	// lock
    50  	d.Lock()
    51  	defer d.Unlock()
    52  	// write entry (sequentially, append-only)
    53  	return d.w.WriteEntry(de)
    54  }
    55  
    56  // WriteEntryIndex writes an entry in an append-only fashion
    57  func (d *DataFile) WriteEntryIndex(ei *EntryIndex) (int64, error) {
    58  	// lock
    59  	d.Lock()
    60  	defer d.Unlock()
    61  	// write entry (sequentially, append-only)
    62  	return d.w.WriteEntryIndex(ei)
    63  }
    64  
    65  // ReadEntry attempts to read and return the next entry sequentially
    66  func (d *DataFile) ReadEntry() (*DataEntry, error) {
    67  	// read lock
    68  	d.RLock()
    69  	defer d.RUnlock()
    70  	// read next entry sequentially
    71  	return d.r.ReadEntry()
    72  }
    73  
    74  // ReadEntryIndex attempts to read and return the next entry sequentially
    75  func (d *DataFile) ReadEntryIndex() (*EntryIndex, error) {
    76  	// read lock
    77  	d.RLock()
    78  	defer d.RUnlock()
    79  	// read next entry sequentially
    80  	return d.r.ReadEntryIndex()
    81  }
    82  
    83  // ReadEntryAt attempts to read and return an entry at the specified offset
    84  func (d *DataFile) ReadEntryAt(offset int64) (*DataEntry, error) {
    85  	// lock
    86  	d.RLock()
    87  	defer d.RUnlock()
    88  	// read entry at specified offset
    89  	return d.r.ReadEntryAt(offset)
    90  }
    91  
    92  // ReadEntryIndexAt attempts to read and return an entry at the specified offset
    93  func (d *DataFile) ReadEntryIndexAt(offset int64) (*EntryIndex, error) {
    94  	// lock
    95  	d.RLock()
    96  	defer d.RUnlock()
    97  	// read entry at specified offset
    98  	return d.r.ReadEntryIndexAt(offset)
    99  }
   100  
   101  // Range iterates the entries as long as the provided boolean function is true
   102  func (d *DataFile) Range(iter func(de *DataEntry) bool) error {
   103  	// lock
   104  	d.Lock()
   105  	defer d.Unlock()
   106  	// grab the reader's offset, so we can set it back later
   107  	offset, err := d.r.Offset()
   108  	if err != nil {
   109  		return err
   110  	}
   111  	// go to the beginning of the file
   112  	_, err = d.r.fd.Seek(0, io.SeekStart)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	// start loop
   117  	for {
   118  		// read entry, and check for EOF
   119  		de, err := d.r.ReadEntry()
   120  		if err != nil {
   121  			if err == io.EOF || err == io.ErrUnexpectedEOF {
   122  				break
   123  			}
   124  			return err
   125  		}
   126  		// entry is good, lets pass it to our boolean function
   127  		if !iter(de) {
   128  			continue // if iter(e) returns false, continue to next entry
   129  		}
   130  	}
   131  	// we are done reading all the entries (hopefully), so
   132  	// we seek back to where we were at the start of this function
   133  	_, err = d.r.fd.Seek(offset, io.SeekStart)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	return nil
   138  }
   139  
   140  // Close closes the DataFile
   141  func (d *DataFile) Close() error {
   142  	// lock
   143  	d.Lock()
   144  	defer d.Unlock()
   145  	// close reader
   146  	err := d.r.Close()
   147  	if err != nil {
   148  		return err
   149  	}
   150  	// close writer
   151  	err = d.w.Close()
   152  	if err != nil {
   153  		return err
   154  	}
   155  	return nil
   156  }
   157  
   158  // Touch cleans and initializes the path, files and folders
   159  func Touch(path string) (string, error) {
   160  	// get absolute path
   161  	path, err := filepath.Abs(path)
   162  	if err != nil {
   163  		return "", err
   164  	}
   165  	// convert any slashes
   166  	path = filepath.ToSlash(path)
   167  	// check to see if the path exists
   168  	_, err = os.Stat(path)
   169  	if os.IsNotExist(err) {
   170  		// create dirs if they need creating
   171  		dir, file := filepath.Split(path)
   172  		err = os.MkdirAll(dir, os.ModeDir)
   173  		if err != nil {
   174  			return "", err
   175  		}
   176  		// create and files if the need creating
   177  		fd, err := os.Create(dir + file)
   178  		if err != nil {
   179  			return "", err
   180  		}
   181  		// close, because we are just touching them
   182  		err = fd.Close()
   183  		if err != nil {
   184  			return "", err
   185  		}
   186  	}
   187  	// return sanitized path (creating any files or folders)
   188  	return path, nil
   189  }
   190  
   191  // Offset is a helper function that returns the current
   192  // offset of the provided reader or writer
   193  func Offset(rw io.ReadWriteSeeker) (int64, error) {
   194  	return rw.Seek(0, io.SeekCurrent)
   195  }