github.com/scottcagno/storage@v1.8.0/pkg/_junk/_lsmtree/sstable/index.go (about)

     1  package sstable
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"path/filepath"
     9  	"strconv"
    10  )
    11  
    12  func IndexFileNameFromIndex(index int64) string {
    13  	hexa := strconv.FormatInt(index, 16)
    14  	return fmt.Sprintf("%s%010s%s", filePrefix, hexa, indexFileSuffix)
    15  }
    16  
    17  func IndexFromIndexFileName(name string) (int64, error) {
    18  	hexa := name[len(filePrefix) : len(name)-len(indexFileSuffix)]
    19  	return strconv.ParseInt(hexa, 16, 32)
    20  }
    21  
    22  type sstIndexEntry struct {
    23  	key    string // key is a key
    24  	offset int64  // offset is the offset of this data entry
    25  }
    26  
    27  func (i *sstIndexEntry) String() string {
    28  	return fmt.Sprintf("sstIndexEntry.key=%q, sstIndexEntry.offset=%d", i.key, i.offset)
    29  }
    30  
    31  type indexEntryMeta struct {
    32  	path string         // filepath
    33  	data *sstIndexEntry // index entry
    34  }
    35  
    36  type indexEntryMetaSet []*indexEntryMeta
    37  
    38  // Len [implementing sort interface]
    39  func (ie indexEntryMetaSet) Len() int {
    40  	return len(ie)
    41  }
    42  
    43  // Less [implementing sort interface]
    44  func (ie indexEntryMetaSet) Less(i, j int) bool {
    45  	return ie[i].data.key < ie[j].data.key
    46  }
    47  
    48  // Swap [implementing sort interface]
    49  func (ie indexEntryMetaSet) Swap(i, j int) {
    50  	ie[i], ie[j] = ie[j], ie[i]
    51  }
    52  
    53  type SSIndex struct {
    54  	//lock  sync.RWMutex
    55  	path  string           // base is the base path of the index file
    56  	file  *os.File         // file is the index file, file descriptor
    57  	open  bool             // reports if the file is open or closed
    58  	first string           // first is the first key
    59  	last  string           // last is the last key
    60  	data  []*sstIndexEntry // data is the sstIndexEntry
    61  }
    62  
    63  func OpenSSIndex(base string, index int64) (*SSIndex, error) {
    64  	// make sure we are working with absolute paths
    65  	base, err := filepath.Abs(base)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	// sanitize any path separators
    70  	base = filepath.ToSlash(base)
    71  	// create new index file path
    72  	path := filepath.Join(base, IndexFileNameFromIndex(index))
    73  	// open (or create) index file
    74  	file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0666)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	// init and return SSIndex
    79  	ssi := &SSIndex{
    80  		path: path, // path is the full path of the index file
    81  		file: file, // file is the index file, file descriptor
    82  		open: true,
    83  	}
    84  	// load sst data index info
    85  	err = ssi.LoadSSIndexData()
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	return ssi, nil
    90  }
    91  
    92  func (ssi *SSIndex) LoadSSIndexData() error {
    93  	// check to make sure file exists
    94  	_, err := os.Stat(ssi.path)
    95  	if os.IsNotExist(err) {
    96  		return err
    97  	}
    98  	// open file to read header
    99  	fd, err := os.OpenFile(ssi.path, os.O_RDONLY, 0666)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	// make sure we close!
   104  	defer fd.Close()
   105  	// read and decode index entries
   106  	for {
   107  		// decode next index entry
   108  		ei, err := DecodeIndexEntry(fd)
   109  		if err != nil {
   110  			if err == io.EOF || err == io.ErrUnexpectedEOF {
   111  				break
   112  			}
   113  			return err
   114  		}
   115  		// add index entry to sst index
   116  		ssi.data = append(ssi.data, ei)
   117  	}
   118  	// update sst first and last and then return
   119  	if len(ssi.data) > 0 {
   120  		ssi.first = ssi.data[0].key
   121  		ssi.last = ssi.data[len(ssi.data)-1].key
   122  	}
   123  	return nil
   124  }
   125  
   126  func (ssi *SSIndex) errorCheckFileAndIndex() error {
   127  	// make sure file is not closed
   128  	if !ssi.open {
   129  		return ErrFileClosed
   130  	}
   131  	// make sure index is loaded
   132  	if ssi.data == nil {
   133  		err := ssi.LoadSSIndexData()
   134  		if err != nil {
   135  			return err
   136  		}
   137  	}
   138  	return nil
   139  }
   140  
   141  func (ssi *SSIndex) WriteIndexEntry(key string, offset int64) error {
   142  	// error check
   143  	err := ssi.errorCheckFileAndIndex()
   144  	if err != nil {
   145  		return err
   146  	}
   147  	// create new index
   148  	ie := &sstIndexEntry{key: key, offset: offset}
   149  	// write entry info to index file
   150  	_, err = EncodeIndexEntry(ssi.file, ie)
   151  	if err != nil {
   152  		return err
   153  	}
   154  	// add to index
   155  	ssi.data = append(ssi.data, ie)
   156  	// check last
   157  	if ssi.last != ssi.lastKey() {
   158  		ssi.last = ssi.lastKey()
   159  	}
   160  	return nil
   161  }
   162  
   163  func (ssi *SSIndex) ReadDataEntry(r io.ReaderAt, key string) (*sstDataEntry, error) {
   164  	// error check
   165  	err := ssi.errorCheckFileAndIndex()
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	// check index for entry offset
   170  	offset, err := ssi.GetEntryOffset(key)
   171  	if err != nil || offset == -1 {
   172  		return nil, err
   173  	}
   174  	// attempt to read and decode data entry using provided reader
   175  	de, err := DecodeDataEntryAt(r, offset)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	// return data entry
   180  	return de, nil
   181  }
   182  
   183  func (ssi *SSIndex) ReadDataEntryAt(r io.ReaderAt, offset int64) (*sstDataEntry, error) {
   184  	// error check
   185  	err := ssi.errorCheckFileAndIndex()
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	// attempt to read and decode data entry using provided reader at provided offset
   190  	de, err := DecodeDataEntryAt(r, offset)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	// return data entry
   195  	return de, nil
   196  }
   197  
   198  func (ssi *SSIndex) searchDataIndex(key string) int {
   199  	// declare for later
   200  	i, j := 0, len(ssi.data)
   201  	// otherwise, perform binary search
   202  	for i < j {
   203  		h := i + (j-i)/2
   204  		if key >= ssi.data[h].key {
   205  			i = h + 1
   206  		} else {
   207  			j = h
   208  		}
   209  	}
   210  	return i - 1
   211  }
   212  
   213  func (ssi *SSIndex) GetEntryOffset(key string) (int64, error) {
   214  	// if data index is not loaded, then load it
   215  	if ssi.data == nil || len(ssi.data) == 0 {
   216  		err := ssi.LoadSSIndexData()
   217  		if err != nil {
   218  			return -1, err
   219  		}
   220  	}
   221  
   222  	// try binary search
   223  	in := ssi.searchDataIndex(key)
   224  	ki := ssi.data[in]
   225  	log.Printf("get offset for key=%q, offset=%d\n", key, ki.offset)
   226  	// double check we have a match
   227  	if ki.key == key {
   228  		return ki.offset, nil
   229  	}
   230  	// otherwise, we return not found
   231  	return -1, ErrIndexEntryNotFound
   232  }
   233  
   234  func (ssi *SSIndex) Scan(iter func(key string, offset int64) bool) {
   235  	for i := range ssi.data {
   236  		ie := ssi.data[i]
   237  		if !iter(ie.key, ie.offset) {
   238  			continue
   239  		}
   240  	}
   241  }
   242  
   243  func (ssi *SSIndex) lastKey() string {
   244  	return ssi.data[len(ssi.data)-1].key
   245  }
   246  
   247  func (ssi *SSIndex) Len() int {
   248  	return len(ssi.data)
   249  }
   250  
   251  func (ssi *SSIndex) Close() error {
   252  	if !ssi.open {
   253  		return nil
   254  	}
   255  	err := ssi.file.Sync()
   256  	if err != nil {
   257  		return err
   258  	}
   259  	err = ssi.file.Close()
   260  	if err != nil {
   261  		return err
   262  	}
   263  	ssi.open = false
   264  	return nil
   265  }