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

     1  package lsmtree
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  )
     7  
     8  // readIndex reads and decodes the provided entry index from r
     9  func readIndex(r io.Reader) (*Index, error) {
    10  	// make buffer
    11  	buf := make([]byte, 18)
    12  	// read index header
    13  	_, err := r.Read(buf)
    14  	if err != nil {
    15  		return nil, err
    16  	}
    17  	// decode key length
    18  	klen := binary.LittleEndian.Uint64(buf[0:8])
    19  	// decode data offset
    20  	off, _ := binary.Varint(buf[8:18])
    21  	// make entry index
    22  	i := &Index{
    23  		Key:    make([]byte, klen),
    24  		Offset: off,
    25  	}
    26  	// read key from data into entry key
    27  	_, err = r.Read(i.Key)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  	// return index
    32  	return i, nil
    33  }
    34  
    35  // readIndexAt decodes the index at the provided offset using the provided reader
    36  func readIndexAt(r io.ReaderAt, offset int64) (*Index, error) {
    37  	// make buffer
    38  	buf := make([]byte, 18)
    39  	// read index header
    40  	n, err := r.ReadAt(buf, offset)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	// update offset
    45  	offset += int64(n)
    46  	// decode key length
    47  	klen := binary.LittleEndian.Uint64(buf[0:8])
    48  	// decode data offset
    49  	off, _ := binary.Varint(buf[8:18])
    50  	// make entry index
    51  	e := &Index{
    52  		Key:    make([]byte, klen),
    53  		Offset: off,
    54  	}
    55  	// read key from data into entry key
    56  	n, err = r.ReadAt(e.Key, offset)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	// update offset
    61  	offset += int64(n)
    62  	// return entry
    63  	return e, nil
    64  }
    65  
    66  // writeIndex encodes and writes the provided entry index to w
    67  func writeIndex(w io.WriteSeeker, i *Index) (int64, error) {
    68  	// error check
    69  	if i == nil {
    70  		return -1, ErrNilIndex
    71  	}
    72  	// get the file pointer offset for the entry
    73  	offset, err := w.Seek(0, io.SeekCurrent)
    74  	if err != nil {
    75  		return -1, err
    76  	}
    77  	// make buffer
    78  	buf := make([]byte, 18)
    79  	// encode and write index key length
    80  	binary.LittleEndian.PutUint64(buf[0:8], uint64(len(i.Key)))
    81  	_, err = w.Write(buf[0:8])
    82  	if err != nil {
    83  		return -1, err
    84  	}
    85  	// encode and write index data offset
    86  	binary.PutVarint(buf[8:18], i.Offset)
    87  	_, err = w.Write(buf[8:18])
    88  	if err != nil {
    89  		return -1, err
    90  	}
    91  	// write index key
    92  	_, err = w.Write(i.Key)
    93  	if err != nil {
    94  		return -1, err
    95  	}
    96  	return offset, nil
    97  }
    98  
    99  func readEntryHeader(r io.Reader, hdr *EntryHeader) (int, error) {
   100  	// make header buffer to read data into
   101  	buf := make([]byte, 16)
   102  	// read the header from the underlying reader into the buffer
   103  	n, err := r.Read(buf)
   104  	if err != nil {
   105  		return n, err
   106  	}
   107  	// decode key length
   108  	hdr.klen = binary.LittleEndian.Uint32(buf[0:4])
   109  	// decode value length
   110  	hdr.vlen = binary.LittleEndian.Uint32(buf[4:8])
   111  	// decode crc32 value
   112  	hdr.crc = binary.LittleEndian.Uint32(buf[8:12])
   113  	// skip the last 4 bytes (reserved for future use)
   114  	//
   115  	return n, nil
   116  }
   117  
   118  // readEntry reads the entry from the provided io.Reader
   119  // and returns the entry or nil and an error
   120  func readEntry(r io.Reader) (*Entry, error) {
   121  	// make entry header
   122  	hdr := new(EntryHeader)
   123  	// reader entry header from r into EntryHeader
   124  	_, err := readEntryHeader(r, hdr)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	// make entry to read key and value into
   129  	e := &Entry{
   130  		Key:   make([]byte, hdr.klen),
   131  		Value: make([]byte, hdr.vlen),
   132  		CRC:   hdr.crc,
   133  	}
   134  	// read key from data into entry key
   135  	_, err = r.Read(e.Key)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	// read value key from data into entry value
   140  	_, err = r.Read(e.Value)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	// make sure the crc checksum is valid
   145  	crc := checksum(append(e.Key, e.Value...))
   146  	if e.CRC != crc {
   147  		return nil, ErrBadChecksum
   148  	}
   149  	// return entry
   150  	return e, nil
   151  }
   152  
   153  func readEntryHeaderAt(r io.ReaderAt, offset int64, hdr *EntryHeader) (int, error) {
   154  	// make header buffer to read data into
   155  	buf := make([]byte, 16)
   156  	// read the header from the underlying reader into the buffer
   157  	n, err := r.ReadAt(buf, offset)
   158  	if err != nil {
   159  		return n, err
   160  	}
   161  	// decode key length
   162  	hdr.klen = binary.LittleEndian.Uint32(buf[0:4])
   163  	// decode value length
   164  	hdr.vlen = binary.LittleEndian.Uint32(buf[4:8])
   165  	// decode crc32 value
   166  	hdr.crc = binary.LittleEndian.Uint32(buf[8:12])
   167  	// skip the last 4 bytes (reserved for future use)
   168  	//
   169  	return n, nil
   170  }
   171  
   172  // readEntryAt reads the entry from the provided io.ReaderAt
   173  // and returns the entry or nil and an error
   174  func readEntryAt(r io.ReaderAt, offset int64) (*Entry, error) {
   175  	// make entry header
   176  	hdr := new(EntryHeader)
   177  	// reader entry header from r into EntryHeader
   178  	n, err := readEntryHeaderAt(r, offset, hdr)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	// update offset
   183  	offset += int64(n)
   184  	// make entry to read key and value into
   185  	e := &Entry{
   186  		Key:   make([]byte, hdr.klen),
   187  		Value: make([]byte, hdr.vlen),
   188  		CRC:   hdr.crc,
   189  	}
   190  	// read key from data into entry key
   191  	n, err = r.ReadAt(e.Key, offset)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	// update offset
   196  	offset += int64(n)
   197  	// read value key from data into entry value
   198  	n, err = r.ReadAt(e.Value, offset)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	// update offset
   203  	offset += int64(n)
   204  	// make sure the crc checksum is valid
   205  	crc := checksum(append(e.Key, e.Value...))
   206  	if e.CRC != crc {
   207  		return nil, ErrBadChecksum
   208  	}
   209  	// return entry
   210  	return e, nil
   211  }
   212  
   213  func writeEntryHeader(w io.Writer, hdr *EntryHeader) (int, error) {
   214  	// make header buffer to write data into
   215  	buf := make([]byte, 16)
   216  	// encode key length into header
   217  	binary.LittleEndian.PutUint32(buf[0:4], hdr.klen)
   218  	// encode value length into header
   219  	binary.LittleEndian.PutUint32(buf[4:8], hdr.vlen)
   220  	// encode crc32 value into header
   221  	binary.LittleEndian.PutUint32(buf[8:12], hdr.crc)
   222  	// skip the last 4 bytes (reserved for future use)
   223  	//
   224  	// write the header to the underlying writer
   225  	return w.Write(buf)
   226  }
   227  
   228  // writeEntry writes the provided entry to the provided io.Writer
   229  func writeEntry(w io.WriteSeeker, e *Entry) (int64, error) {
   230  	// error check
   231  	if e == nil {
   232  		return -1, ErrNilEntry
   233  	}
   234  	// get the file pointer offset for the entry
   235  	offset, err := w.Seek(0, io.SeekCurrent)
   236  	if err != nil {
   237  		return -1, err
   238  	}
   239  	// make entry header
   240  	hdr := &EntryHeader{
   241  		klen: uint32(len(e.Key)),
   242  		vlen: uint32(len(e.Value)),
   243  		crc:  e.CRC,
   244  	}
   245  	// write entry header
   246  	_, err = writeEntryHeader(w, hdr)
   247  	if err != nil {
   248  		return -1, err
   249  	}
   250  	// write entry key
   251  	_, err = w.Write(e.Key)
   252  	if err != nil {
   253  		return -1, err
   254  	}
   255  	// write entry value
   256  	_, err = w.Write(e.Value)
   257  	if err != nil {
   258  		return -1, err
   259  	}
   260  	// return offset
   261  	return offset, nil
   262  }