github.com/scottcagno/storage@v1.8.0/pkg/lsmtree/_blockfile.go (about)

     1  package lsmtree
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"math"
     9  	"os"
    10  	"path/filepath"
    11  )
    12  
    13  const (
    14  	blockSize        = 4096
    15  	recordHeaderSize = 16
    16  	maxRecordSize    = blockSize * math.MaxUint16
    17  )
    18  
    19  const (
    20  	statusFree    = uint16(asciiUnitSeparator >> 0 << 10)
    21  	statusActive  = uint16(asciiUnitSeparator >> 1 << 10)
    22  	statusDeleted = uint16(asciiUnitSeparator >> 2 << 10)
    23  )
    24  
    25  var (
    26  	ErrBadOffsetAlignment = errors.New("bad offset; not correctly aligned")
    27  	ErrOffsetOutOfBounds  = errors.New("bad offset; out of bounds")
    28  	ErrDataTooLarge       = errors.New("data exceeds max record size")
    29  	ErrScannerSkip        = errors.New("skip to next place with scanner/iterator")
    30  )
    31  
    32  type blockFile struct {
    33  	path string   // path is the current filepath
    34  	open bool     // open reports true if the file is open
    35  	size int64    // size is the current size of the file
    36  	fp   *os.File // fp is the file pointer
    37  }
    38  
    39  func OpenBlockFile(name string) (*blockFile, error) {
    40  	return openBlockFile(name)
    41  }
    42  
    43  func openBlockFile(name string) (*blockFile, error) {
    44  	// sanitize base path
    45  	path, err := filepath.Abs(name)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	// sanitize any path separators
    50  	path = filepath.ToSlash(path)
    51  	// get dir
    52  	dir, _ := filepath.Split(path)
    53  	// create any directories if they are not there
    54  	err = os.MkdirAll(dir, os.ModeDir)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	// open file
    59  	f, err := os.OpenFile(path, os.O_CREATE, 0666)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	// get file size
    64  	fi, err := f.Stat()
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	// setup new blockFile
    69  	bf := &blockFile{
    70  		path: path,
    71  		open: true,
    72  		size: fi.Size(),
    73  		fp:   f,
    74  	}
    75  	// call init
    76  	err = bf.init()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	// return blockFile
    81  	return bf, nil
    82  }
    83  
    84  func (bf *blockFile) init() error {
    85  	return nil
    86  }
    87  
    88  func (bf *blockFile) Read() ([]byte, error) {
    89  	// error check
    90  	// allocate new record to read into
    91  	rd := new(recordData)
    92  	// read record
    93  	_, err := bf.readRecord(rd)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	// return record data
    98  	return rd.data, nil
    99  }
   100  
   101  func (bf *blockFile) ReadAt(off int64) ([]byte, error) {
   102  	// error check
   103  	// allocate new record to read into
   104  	rd := new(recordData)
   105  	// read record at
   106  	_, err := bf.readRecordAt(rd, off)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	// return record data
   111  	return rd.data, nil
   112  }
   113  
   114  func (bf *blockFile) Write(d []byte) (int, error) {
   115  	// error check
   116  
   117  	// make record
   118  	rd, err := makeRecord(d)
   119  	if err != nil {
   120  		return -1, err
   121  	}
   122  	// get record offset
   123  	off, err := currentOffset(bf.fp)
   124  	if err != nil {
   125  		return -1, err
   126  	}
   127  	// write record
   128  	_, err = bf.writeRecord(rd)
   129  	if err != nil {
   130  		return -1, err
   131  	}
   132  	// return offset of record
   133  	return int(off), nil
   134  }
   135  
   136  func (bf *blockFile) WriteAt(d []byte, off int64) (int, error) {
   137  	// error check
   138  	// make record
   139  	rd, err := makeRecord(d)
   140  	if err != nil {
   141  		return -1, err
   142  	}
   143  	// write record
   144  	_, err = bf.writeRecordAt(rd, off)
   145  	if err != nil {
   146  		return -1, err
   147  	}
   148  	// return bytes written
   149  	return int(off), nil
   150  }
   151  
   152  func (bf *blockFile) Seek(offset int64, whence int) (int64, error) {
   153  	off, err := bf.fp.Seek(offset, whence)
   154  	if err != nil {
   155  		return -1, err
   156  	}
   157  	return off, nil
   158  }
   159  
   160  type Record = recordData
   161  
   162  func (bf *blockFile) Scan(fn func(rd *Record) error) error {
   163  	for {
   164  		// allocate new record to read into
   165  		rd := new(recordData)
   166  		// read record
   167  		_, err := bf.readRecord(rd)
   168  		if err != nil {
   169  			if err == io.EOF || err == io.ErrUnexpectedEOF {
   170  				break
   171  			}
   172  			return err
   173  		}
   174  		err = fn(rd)
   175  		if err != nil {
   176  			if err == ErrScannerSkip {
   177  				continue
   178  			}
   179  			return err
   180  		}
   181  	}
   182  	return nil
   183  }
   184  
   185  func (bf *blockFile) Sync() error {
   186  	err := bf.fp.Sync()
   187  	if err != nil {
   188  		return err
   189  	}
   190  	return nil
   191  }
   192  
   193  func (bf *blockFile) Close() error {
   194  	err := bf.fp.Sync()
   195  	if err != nil {
   196  		return err
   197  	}
   198  	err = bf.fp.Close()
   199  	if err != nil {
   200  		return err
   201  	}
   202  	return nil
   203  }
   204  
   205  func (bf *blockFile) readRecord(rd *recordData) (int, error) {
   206  	// read record data
   207  	n, err := rd.readData(bf.fp)
   208  	if err != nil {
   209  		return -1, err
   210  	}
   211  	// skip to the next alignment offset
   212  	_, err = bf.fp.Seek(int64(rd.padding), io.SeekCurrent)
   213  	if err != nil {
   214  		return -1, err
   215  	}
   216  	return n, nil
   217  }
   218  
   219  func (bf *blockFile) readRecordAt(rd *recordData, offset int64) (int, error) {
   220  	// read record data
   221  	n, err := rd.readDataAt(bf.fp, offset)
   222  	if err != nil {
   223  		return -1, err
   224  	}
   225  	return n, nil
   226  }
   227  
   228  func (bf *blockFile) writeRecord(rd *recordData) (int, error) {
   229  	// write record data
   230  	n, err := rd.writeData(bf.fp)
   231  	if err != nil {
   232  		return -1, err
   233  	}
   234  	// skip to the next alignment offset
   235  	_, err = bf.fp.Seek(int64(rd.padding), io.SeekCurrent)
   236  	if err != nil {
   237  		return -1, err
   238  	}
   239  	return n, nil
   240  }
   241  
   242  func (bf *blockFile) writeRecordAt(rd *recordData, offset int64) (int, error) {
   243  	// write record data
   244  	n, err := rd.writeDataAt(bf.fp, offset)
   245  	if err != nil {
   246  		return -1, err
   247  	}
   248  	return n, nil
   249  }
   250  
   251  func align(size int64) int64 {
   252  	if size > 0 {
   253  		return ((size + 2) + blockSize - 1) &^ (blockSize - 1)
   254  	}
   255  	return blockSize
   256  }
   257  
   258  func getOffset(pos int, max int64) (int64, error) {
   259  	// calculate the
   260  	offset := int64(pos * blockSize)
   261  	// return error if offset is not block aligned
   262  	if offset%blockSize != 0 {
   263  		return -1, ErrBadOffsetAlignment
   264  	}
   265  	// return error if offset is larger than max
   266  	if offset > max {
   267  		return -1, ErrOffsetOutOfBounds
   268  	}
   269  	// otherwise, return offset
   270  	return offset, nil
   271  }
   272  
   273  func currentOffset(w io.Seeker) (int64, error) {
   274  	// get current offset (of the beginning of this record) to return
   275  	off, err := w.Seek(0, io.SeekCurrent)
   276  	if err != nil {
   277  		return -1, err
   278  	}
   279  	// return err if offset is not block aligned
   280  	if off%blockSize != 0 {
   281  		return -1, ErrBadOffsetAlignment
   282  	}
   283  	// return offset and a nil error
   284  	return off, nil
   285  }
   286  
   287  type recordHeader struct {
   288  	status  uint16 // max: 65535; status of the record
   289  	blocks  uint16 // max: 65535; blocks occupied by the record data
   290  	length  uint32 // max: 4294967295; length of the raw record data
   291  	padding uint32 // max: 4294967295; padding is the extra unused bytes in the block
   292  	magic   uint32 // max: 4294967295; magic currently unused
   293  }
   294  
   295  func (rh *recordHeader) readHeader(r io.Reader) (int, error) {
   296  	// make buffer for record header
   297  	buf := make([]byte, 16)
   298  	// read in entire record header
   299  	n, err := r.Read(buf)
   300  	if err != nil {
   301  		return n, err
   302  	}
   303  	// decode status
   304  	rh.status = binary.LittleEndian.Uint16(buf[0:2])
   305  	// decode blocks
   306  	rh.blocks = binary.LittleEndian.Uint16(buf[2:4])
   307  	// decode length
   308  	rh.length = binary.LittleEndian.Uint32(buf[4:8])
   309  	// decode padding
   310  	rh.padding = binary.LittleEndian.Uint32(buf[8:12])
   311  	// decode magic
   312  	rh.magic = binary.LittleEndian.Uint32(buf[12:16])
   313  	return n, nil
   314  }
   315  
   316  func (rh *recordHeader) readHeaderAt(r io.ReaderAt, offset int64) (int, error) {
   317  	// make buffer for record header
   318  	buf := make([]byte, 16)
   319  	// read in entire record header
   320  	n, err := r.ReadAt(buf, offset)
   321  	if err != nil {
   322  		return n, err
   323  	}
   324  	// decode status
   325  	rh.status = binary.LittleEndian.Uint16(buf[0:2])
   326  	// decode blocks
   327  	rh.blocks = binary.LittleEndian.Uint16(buf[2:4])
   328  	// decode length
   329  	rh.length = binary.LittleEndian.Uint32(buf[4:8])
   330  	// decode padding
   331  	rh.padding = binary.LittleEndian.Uint32(buf[8:12])
   332  	// decode extra2
   333  	rh.magic = binary.LittleEndian.Uint32(buf[12:16])
   334  	return n, nil
   335  }
   336  
   337  func (rh *recordHeader) writeHeader(w io.Writer) (int, error) {
   338  	// make buffer to encode record header into
   339  	buf := make([]byte, 16)
   340  	// encode status
   341  	binary.LittleEndian.PutUint16(buf[0:2], rh.status)
   342  	// encode blocks
   343  	binary.LittleEndian.PutUint16(buf[2:4], rh.blocks)
   344  	// encode length
   345  	binary.LittleEndian.PutUint32(buf[4:8], rh.length)
   346  	// encode padding
   347  	binary.LittleEndian.PutUint32(buf[8:12], rh.padding)
   348  	// encode magic
   349  	binary.LittleEndian.PutUint32(buf[12:16], rh.magic)
   350  	// write record header
   351  	return w.Write(buf)
   352  }
   353  
   354  func (rh *recordHeader) writeHeaderAt(w io.WriterAt, offset int64) (int, error) {
   355  	// make buffer to encode record header into
   356  	buf := make([]byte, 16)
   357  	// encode status
   358  	binary.LittleEndian.PutUint16(buf[0:2], rh.status)
   359  	// encode blocks
   360  	binary.LittleEndian.PutUint16(buf[2:4], rh.blocks)
   361  	// encode length
   362  	binary.LittleEndian.PutUint32(buf[4:8], rh.length)
   363  	// encode padding
   364  	binary.LittleEndian.PutUint32(buf[8:12], rh.padding)
   365  	// encode magic
   366  	binary.LittleEndian.PutUint32(buf[12:16], rh.magic)
   367  	// write record header at
   368  	return w.WriteAt(buf, offset)
   369  }
   370  
   371  type recordData struct {
   372  	*recordHeader        // record header
   373  	data          []byte // raw record data
   374  }
   375  
   376  func (rd *recordData) String() string {
   377  	s := fmt.Sprintf("record:\n")
   378  	s += fmt.Sprintf("\theader:\n")
   379  	s += fmt.Sprintf("\t\tstatus: %d\n", rd.recordHeader.status)
   380  	s += fmt.Sprintf("\t\tblocks: %d\n", rd.recordHeader.blocks)
   381  	s += fmt.Sprintf("\t\tlength: %d\n", rd.recordHeader.length)
   382  	s += fmt.Sprintf("\t\tpadding: %d\n", rd.recordHeader.padding)
   383  	s += fmt.Sprintf("\t\tmagic: %d\n", rd.recordHeader.magic)
   384  	s += fmt.Sprintf("\tdata: %s\n", rd.data)
   385  	return s
   386  }
   387  
   388  func makeRecord(d []byte) (*recordData, error) {
   389  	// calc "overhead"
   390  	overhead := recordHeaderSize + int64(len(d))
   391  	// get aligned size
   392  	size := align(overhead)
   393  	// error check
   394  	if size > maxRecordSize {
   395  		return nil, ErrDataTooLarge
   396  	}
   397  	// create record
   398  	rd := &recordData{
   399  		recordHeader: &recordHeader{
   400  			status:  statusActive,
   401  			blocks:  uint16(size / blockSize),
   402  			length:  uint32(len(d)),
   403  			padding: uint32(size - overhead),
   404  			magic:   uint32(0),
   405  		},
   406  		data: d,
   407  	}
   408  	// return record
   409  	return rd, nil
   410  }
   411  
   412  func (rd *recordData) readData(r io.Reader) (int, error) {
   413  	// create record header
   414  	rh := new(recordHeader)
   415  	// read the record header
   416  	n, err := rh.readHeader(r)
   417  	if err != nil {
   418  		return -1, err
   419  	}
   420  	// init count
   421  	count := n
   422  	// fill out the record and allocate space to read in data
   423  	rd.recordHeader = rh
   424  	rd.data = make([]byte, rh.length)
   425  	// read data into the record
   426  	n, err = r.Read(rd.data)
   427  	if err != nil {
   428  		return -1, err
   429  	}
   430  	// update count
   431  	count += n
   432  	return count, nil
   433  }
   434  
   435  func (rd *recordData) readDataAt(r io.ReaderAt, offset int64) (int, error) {
   436  	// create record header
   437  	rh := new(recordHeader)
   438  	// read the record header
   439  	n, err := rh.readHeaderAt(r, offset)
   440  	if err != nil {
   441  		return -1, err
   442  	}
   443  	// update offset for next read
   444  	offset += int64(n)
   445  	// fill out the record and allocate space to read in data
   446  	rd.recordHeader = rh
   447  	rd.data = make([]byte, rh.length)
   448  	// read data into the record
   449  	n, err = r.ReadAt(rd.data, offset)
   450  	if err != nil {
   451  		return -1, err
   452  	}
   453  	// update offset to return
   454  	offset += int64(n)
   455  	return int(offset), nil
   456  }
   457  
   458  func (rd *recordData) writeData(w io.Writer) (int, error) {
   459  	// capture bytes written
   460  	var wrote int
   461  	// write the record header
   462  	n, err := rd.recordHeader.writeHeader(w)
   463  	if err != nil {
   464  		return -1, err
   465  	}
   466  	// update wrote
   467  	wrote += n
   468  	// write the record data
   469  	n, err = w.Write(rd.data)
   470  	if err != nil {
   471  		return -1, err
   472  	}
   473  	// update written
   474  	wrote += n
   475  	// return bytes written
   476  	return wrote, nil
   477  }
   478  
   479  func (rd *recordData) writeDataAt(w io.WriterAt, offset int64) (int, error) {
   480  	// capture bytes written
   481  	var wrote int
   482  	// write the record header
   483  	n, err := rd.recordHeader.writeHeaderAt(w, offset)
   484  	if err != nil {
   485  		return -1, err
   486  	}
   487  	// update wrote and offset
   488  	wrote += n
   489  	offset += int64(n)
   490  	// write the record data
   491  	n, err = w.WriteAt(rd.data, offset)
   492  	if err != nil {
   493  		return -1, err
   494  	}
   495  	// update wrote
   496  	wrote += n
   497  	// return bytes written
   498  	return wrote, nil
   499  }