github.com/scottcagno/storage@v1.8.0/cmd/index/mockdb.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"math"
     9  	"os"
    10  	"path/filepath"
    11  	"sort"
    12  	"sync"
    13  )
    14  
    15  const (
    16  	maxKeySize = math.MaxUint8
    17  	maxValSize = math.MaxUint16
    18  )
    19  
    20  var (
    21  	ErrIncomplete = errors.New("key or value is incomplete or missing")
    22  	ErrTooLarge   = errors.New(
    23  		fmt.Sprintf("key or value exceeded max size allowed (maxKey=%d, maxVal=%d)", maxKeySize, maxValSize))
    24  	ErrNotFound = errors.New("entry not found")
    25  
    26  	IterSkip = errors.New("skipping this item")
    27  	IterStop = errors.New("breaking out and stopping iteration")
    28  )
    29  
    30  type mockEntry struct {
    31  	k string
    32  	v string
    33  }
    34  
    35  func (me *mockEntry) check() error {
    36  	if me.k == "" || me.v == "" {
    37  		return ErrIncomplete
    38  	}
    39  	if len(me.k) > maxKeySize || len(me.v) > maxValSize {
    40  		return ErrTooLarge
    41  	}
    42  	return nil
    43  }
    44  
    45  func (me *mockEntry) String() string {
    46  	return fmt.Sprintf("{%q:%q}", me.k, me.v)
    47  }
    48  
    49  type mockIndex struct {
    50  	mdat map[string]int64 // mapped keys
    51  	odat []string         // ordered keys
    52  }
    53  
    54  func makeMockIndex() *mockIndex {
    55  	return &mockIndex{
    56  		mdat: make(map[string]int64),
    57  		odat: make([]string, 0),
    58  	}
    59  }
    60  
    61  func (mi *mockIndex) put(k string, off int64) {
    62  	// check if key is in map
    63  	if _, ok := mi.mdat[k]; !ok {
    64  		// add to ordered set if not in map
    65  		mi.odat = append(mi.odat, k)
    66  		if !sort.StringsAreSorted(mi.odat) {
    67  			sort.Strings(mi.odat)
    68  		}
    69  	}
    70  	// upsert into map
    71  	mi.mdat[k] = off
    72  }
    73  
    74  func (mi *mockIndex) get(k string) (int64, error) {
    75  	// check if key is in map
    76  	off, ok := mi.mdat[k]
    77  	if !ok {
    78  		// if not, return -1
    79  		return -1, ErrNotFound
    80  	}
    81  	return off, nil
    82  }
    83  
    84  func (mi *mockIndex) del(k string) {
    85  	// check if key is in map
    86  	if _, ok := mi.mdat[k]; !ok {
    87  		// if not, just return
    88  		return
    89  	}
    90  	// otherwise, delete from map
    91  	delete(mi.mdat, k)
    92  	// and delete from ordered set
    93  	deleteFromSlice(&mi.odat, k)
    94  }
    95  
    96  func (mi *mockIndex) scan(fn func(k string, off int64) error) {
    97  	for _, key := range mi.odat {
    98  		err := fn(key, mi.mdat[key])
    99  		if err != nil {
   100  			if err == IterSkip {
   101  				continue
   102  			}
   103  			break
   104  		}
   105  	}
   106  }
   107  
   108  // deleteFromSliceV1 expects a sorted slice!!! It will FAIL HARD if it doesn't get one
   109  func deleteFromSlice(a *[]string, s string) {
   110  	i := binarySearch(*a, s)
   111  	if (*a)[i] != s {
   112  		return // s was not found
   113  	}
   114  	*a = deleteAtI(*a, i)
   115  }
   116  
   117  // binarySearch expects a sorted slice!!! It will FAIL HARD if it doesn't get one
   118  func binarySearch(a []string, x string) int {
   119  	i, j := 0, len(a)
   120  	for i < j {
   121  		h := int(uint(i+j) >> 1)
   122  		if !(a[h] >= x) {
   123  			i = h + 1
   124  		} else {
   125  			j = h
   126  		}
   127  	}
   128  	return i
   129  }
   130  
   131  func deleteAtI(a []string, i int) []string {
   132  	if i < len(a)-1 {
   133  		copy(a[i:], a[i+1:])
   134  	}
   135  	a[len(a)-1] = ""
   136  	a = a[:len(a)-1]
   137  	return a
   138  }
   139  
   140  type mockDB struct {
   141  	lock  sync.RWMutex
   142  	f     *os.File
   143  	index *mockIndex
   144  }
   145  
   146  func OpenMockDB(base string) (*mockDB, error) {
   147  	dir, file := cleanPath(base)
   148  	f, err := openFile(filepath.Join(dir, file))
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	mdb := &mockDB{
   153  		f: f,
   154  	}
   155  	err = mdb.loadIndex()
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	return mdb, err
   160  }
   161  
   162  func (mdb *mockDB) loadIndex() error {
   163  	mdb.lock.Lock()
   164  	defer mdb.lock.Unlock()
   165  	if mdb.index == nil {
   166  		mdb.index = makeMockIndex()
   167  	}
   168  	for {
   169  		// get offset of entry
   170  		off, err := offset(mdb.f)
   171  		if err != nil {
   172  			return err
   173  		}
   174  		// read entry at offset
   175  		me, err := read(mdb.f)
   176  		if err != nil {
   177  			if err == io.EOF || err == io.ErrUnexpectedEOF {
   178  				break
   179  			}
   180  			return err
   181  		}
   182  		// read entry successfully, add to index
   183  		mdb.index.put(me.k, off)
   184  	}
   185  	return nil
   186  }
   187  
   188  func (mdb *mockDB) Scan(fn func(me *mockEntry) error) {
   189  	mdb.index.scan(func(k string, off int64) error {
   190  		me, err := readAt(mdb.f, off)
   191  		if err != nil {
   192  			return err
   193  		}
   194  		err = fn(me)
   195  		if err != nil {
   196  			return err
   197  		}
   198  		return nil
   199  	})
   200  }
   201  
   202  func (mdb *mockDB) Get(k string) (string, error) {
   203  	mdb.lock.Lock()
   204  	defer mdb.lock.Unlock()
   205  	off, err := mdb.index.get(k)
   206  	if err != nil {
   207  		return "", err
   208  	}
   209  	me, err := readAt(mdb.f, off)
   210  	if err != nil {
   211  		return "", err
   212  	}
   213  	return me.v, nil
   214  }
   215  
   216  func (mdb *mockDB) Put(k string, v string) error {
   217  	mdb.lock.Lock()
   218  	defer mdb.lock.Unlock()
   219  	off, err := write(mdb.f, &mockEntry{k, v})
   220  	if err != nil {
   221  		return err
   222  	}
   223  	mdb.index.put(k, off)
   224  	return nil
   225  }
   226  
   227  var tombstone = &mockEntry{"_TS", "_TS"}
   228  
   229  func (mdb *mockDB) Del(k string) error {
   230  	mdb.lock.Lock()
   231  	defer mdb.lock.Unlock()
   232  	_, err := write(mdb.f, tombstone)
   233  	if err != nil {
   234  		return err
   235  	}
   236  	mdb.index.del(k)
   237  	return nil
   238  }
   239  
   240  func (mdb *mockDB) PutBatch(b *Batch) error {
   241  	mdb.lock.Lock()
   242  	defer mdb.lock.Unlock()
   243  	sort.Stable(b)
   244  	for _, me := range b.entries {
   245  		off, err := write(mdb.f, me)
   246  		if err != nil {
   247  			return err
   248  		}
   249  		mdb.index.put(me.k, off)
   250  	}
   251  	return nil
   252  }
   253  
   254  func (mdb *mockDB) Sync() error {
   255  	mdb.lock.Lock()
   256  	defer mdb.lock.Unlock()
   257  	err := mdb.f.Sync()
   258  	if err != nil {
   259  		return err
   260  	}
   261  	return nil
   262  }
   263  
   264  func (mdb *mockDB) Close() error {
   265  	mdb.lock.Lock()
   266  	defer mdb.lock.Unlock()
   267  	err := mdb.f.Sync()
   268  	if err != nil {
   269  		return err
   270  	}
   271  	err = mdb.f.Close()
   272  	if err != nil {
   273  		return err
   274  	}
   275  	return nil
   276  }
   277  
   278  func (mdb *mockDB) Count() int {
   279  	return len(mdb.index.odat)
   280  }
   281  
   282  type Batch struct {
   283  	entries []*mockEntry
   284  }
   285  
   286  func NewBatch() *Batch {
   287  	return &Batch{
   288  		entries: make([]*mockEntry, 0),
   289  	}
   290  }
   291  
   292  func (b *Batch) Write(k string, v string) error {
   293  	me := &mockEntry{k: k, v: v}
   294  	err := me.check()
   295  	if err != nil {
   296  		return err
   297  	}
   298  	b.entries = append(b.entries, me)
   299  	return nil
   300  }
   301  
   302  func (b *Batch) Len() int {
   303  	return len(b.entries)
   304  }
   305  
   306  func (b *Batch) Less(i, j int) bool {
   307  	return b.entries[i].k < b.entries[j].k
   308  }
   309  
   310  func (b *Batch) Swap(i, j int) {
   311  	b.entries[i], b.entries[j] = b.entries[j], b.entries[i]
   312  }
   313  
   314  func getUint16(b []byte) uint16 {
   315  	_ = b[1] // early bounds check
   316  	return uint16(b[0]) | uint16(b[1])<<8
   317  }
   318  
   319  func putUint16(b []byte, v uint16) {
   320  	_ = b[1] // early bounds check
   321  	b[0] = byte(v)
   322  	b[1] = byte(v >> 8)
   323  }
   324  
   325  func read(r io.Reader) (*mockEntry, error) {
   326  	// make entry header
   327  	hdr := make([]byte, 3)
   328  	_, err := r.Read(hdr)
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  	// get key and value length
   333  	klen := hdr[0]
   334  	vlen := getUint16(hdr[1:])
   335  	// read key
   336  	k := make([]byte, klen)
   337  	_, err = r.Read(k)
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	// read value
   342  	v := make([]byte, vlen)
   343  	_, err = r.Read(v)
   344  	if err != nil {
   345  		return nil, err
   346  	}
   347  	// make and return entry
   348  	me := &mockEntry{
   349  		k: string(k),
   350  		v: string(v),
   351  	}
   352  	return me, nil
   353  }
   354  
   355  func readAt(r io.ReaderAt, off int64) (*mockEntry, error) {
   356  	// make entry header
   357  	hdr := make([]byte, 3)
   358  	n, err := r.ReadAt(hdr, off)
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  	// update offset
   363  	off += int64(n)
   364  	// get key and value length
   365  	klen := hdr[0]
   366  	vlen := getUint16(hdr[1:])
   367  	// read key
   368  	k := make([]byte, klen)
   369  	n, err = r.ReadAt(k, off)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  	// update offset
   374  	off += int64(n)
   375  	// read value
   376  	v := make([]byte, vlen)
   377  	_, err = r.ReadAt(v, off)
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  	// update offset
   382  	off += int64(n)
   383  	// make and return entry
   384  	me := &mockEntry{
   385  		k: string(k),
   386  		v: string(v),
   387  	}
   388  	return me, nil
   389  }
   390  
   391  func write(w io.WriteSeeker, me *mockEntry) (int64, error) {
   392  	// err check entry
   393  	err := me.check()
   394  	if err != nil {
   395  		return -1, err
   396  	}
   397  	// get the file pointer offset for the entry
   398  	off, err := offset(w)
   399  	if err != nil {
   400  		return -1, err
   401  	}
   402  	// make and encode entry header
   403  	hdr := make([]byte, 3)
   404  	hdr[0] = uint8(len(me.k))
   405  	putUint16(hdr[1:], uint16(len(me.v)))
   406  	// write entry header
   407  	_, err = w.Write(hdr)
   408  	if err != nil {
   409  		return -1, err
   410  	}
   411  	// write entry key and value
   412  	_, err = w.Write([]byte(me.k + me.v))
   413  	if err != nil {
   414  		return -1, err
   415  	}
   416  	// return offset
   417  	return off, nil
   418  }
   419  
   420  func offset(ws io.WriteSeeker) (int64, error) {
   421  	// get and return current offset
   422  	return ws.Seek(0, io.SeekCurrent)
   423  }
   424  
   425  func cleanPath(path string) (string, string) {
   426  	path, err := filepath.Abs(path)
   427  	if err != nil {
   428  		log.Panicf("cleaning path: %v\n", err)
   429  	}
   430  	return filepath.Split(filepath.ToSlash(path))
   431  }
   432  
   433  func openFile(path string) (*os.File, error) {
   434  	_, err := os.Stat(path)
   435  	if os.IsNotExist(err) {
   436  		dir, file := filepath.Split(path)
   437  		err = os.MkdirAll(dir, os.ModeDir)
   438  		if err != nil {
   439  			return nil, err
   440  		}
   441  		fd, err := os.Create(dir + file)
   442  		if err != nil {
   443  			return nil, err
   444  		}
   445  		err = fd.Close()
   446  		if err != nil {
   447  			return fd, err
   448  		}
   449  	}
   450  	fd, err := os.OpenFile(path, os.O_RDWR, 0666) // os.ModeSticky
   451  	if err != nil {
   452  		return nil, err
   453  	}
   454  	return fd, nil
   455  }