github.com/MetalBlockchain/metalgo@v1.11.9/database/memdb/db.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package memdb
     5  
     6  import (
     7  	"context"
     8  	"slices"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/MetalBlockchain/metalgo/database"
    13  )
    14  
    15  const (
    16  	// Name is the name of this database for database switches
    17  	Name = "memdb"
    18  
    19  	// DefaultSize is the default initial size of the memory database
    20  	DefaultSize = 1024
    21  )
    22  
    23  var (
    24  	_ database.Database = (*Database)(nil)
    25  	_ database.Batch    = (*batch)(nil)
    26  	_ database.Iterator = (*iterator)(nil)
    27  )
    28  
    29  // Database is an ephemeral key-value store that implements the Database
    30  // interface.
    31  type Database struct {
    32  	lock sync.RWMutex
    33  	db   map[string][]byte
    34  }
    35  
    36  // New returns a map with the Database interface methods implemented.
    37  func New() *Database {
    38  	return NewWithSize(DefaultSize)
    39  }
    40  
    41  // NewWithSize returns a map pre-allocated to the provided size with the
    42  // Database interface methods implemented.
    43  func NewWithSize(size int) *Database {
    44  	return &Database{db: make(map[string][]byte, size)}
    45  }
    46  
    47  func (db *Database) Close() error {
    48  	db.lock.Lock()
    49  	defer db.lock.Unlock()
    50  
    51  	if db.db == nil {
    52  		return database.ErrClosed
    53  	}
    54  	db.db = nil
    55  	return nil
    56  }
    57  
    58  func (db *Database) isClosed() bool {
    59  	db.lock.RLock()
    60  	defer db.lock.RUnlock()
    61  
    62  	return db.db == nil
    63  }
    64  
    65  func (db *Database) Has(key []byte) (bool, error) {
    66  	db.lock.RLock()
    67  	defer db.lock.RUnlock()
    68  
    69  	if db.db == nil {
    70  		return false, database.ErrClosed
    71  	}
    72  	_, ok := db.db[string(key)]
    73  	return ok, nil
    74  }
    75  
    76  func (db *Database) Get(key []byte) ([]byte, error) {
    77  	db.lock.RLock()
    78  	defer db.lock.RUnlock()
    79  
    80  	if db.db == nil {
    81  		return nil, database.ErrClosed
    82  	}
    83  	if entry, ok := db.db[string(key)]; ok {
    84  		return slices.Clone(entry), nil
    85  	}
    86  	return nil, database.ErrNotFound
    87  }
    88  
    89  func (db *Database) Put(key []byte, value []byte) error {
    90  	db.lock.Lock()
    91  	defer db.lock.Unlock()
    92  
    93  	if db.db == nil {
    94  		return database.ErrClosed
    95  	}
    96  	db.db[string(key)] = slices.Clone(value)
    97  	return nil
    98  }
    99  
   100  func (db *Database) Delete(key []byte) error {
   101  	db.lock.Lock()
   102  	defer db.lock.Unlock()
   103  
   104  	if db.db == nil {
   105  		return database.ErrClosed
   106  	}
   107  	delete(db.db, string(key))
   108  	return nil
   109  }
   110  
   111  func (db *Database) NewBatch() database.Batch {
   112  	return &batch{db: db}
   113  }
   114  
   115  func (db *Database) NewIterator() database.Iterator {
   116  	return db.NewIteratorWithStartAndPrefix(nil, nil)
   117  }
   118  
   119  func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
   120  	return db.NewIteratorWithStartAndPrefix(start, nil)
   121  }
   122  
   123  func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
   124  	return db.NewIteratorWithStartAndPrefix(nil, prefix)
   125  }
   126  
   127  func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
   128  	db.lock.RLock()
   129  	defer db.lock.RUnlock()
   130  
   131  	if db.db == nil {
   132  		return &database.IteratorError{
   133  			Err: database.ErrClosed,
   134  		}
   135  	}
   136  
   137  	startString := string(start)
   138  	prefixString := string(prefix)
   139  	keys := make([]string, 0, len(db.db))
   140  	for key := range db.db {
   141  		if strings.HasPrefix(key, prefixString) && key >= startString {
   142  			keys = append(keys, key)
   143  		}
   144  	}
   145  	slices.Sort(keys) // Keys need to be in sorted order
   146  	values := make([][]byte, 0, len(keys))
   147  	for _, key := range keys {
   148  		values = append(values, db.db[key])
   149  	}
   150  	return &iterator{
   151  		db:     db,
   152  		keys:   keys,
   153  		values: values,
   154  	}
   155  }
   156  
   157  func (db *Database) Compact(_, _ []byte) error {
   158  	db.lock.RLock()
   159  	defer db.lock.RUnlock()
   160  
   161  	if db.db == nil {
   162  		return database.ErrClosed
   163  	}
   164  	return nil
   165  }
   166  
   167  func (db *Database) HealthCheck(context.Context) (interface{}, error) {
   168  	if db.isClosed() {
   169  		return nil, database.ErrClosed
   170  	}
   171  	return nil, nil
   172  }
   173  
   174  type batch struct {
   175  	database.BatchOps
   176  
   177  	db *Database
   178  }
   179  
   180  func (b *batch) Write() error {
   181  	b.db.lock.Lock()
   182  	defer b.db.lock.Unlock()
   183  
   184  	if b.db.db == nil {
   185  		return database.ErrClosed
   186  	}
   187  
   188  	for _, op := range b.Ops {
   189  		if op.Delete {
   190  			delete(b.db.db, string(op.Key))
   191  		} else {
   192  			b.db.db[string(op.Key)] = op.Value
   193  		}
   194  	}
   195  	return nil
   196  }
   197  
   198  func (b *batch) Inner() database.Batch {
   199  	return b
   200  }
   201  
   202  type iterator struct {
   203  	db          *Database
   204  	initialized bool
   205  	keys        []string
   206  	values      [][]byte
   207  	err         error
   208  }
   209  
   210  func (it *iterator) Next() bool {
   211  	// Short-circuit and set an error if the underlying database has been closed.
   212  	if it.db.isClosed() {
   213  		it.keys = nil
   214  		it.values = nil
   215  		it.err = database.ErrClosed
   216  		return false
   217  	}
   218  
   219  	// If the iterator was not yet initialized, do it now
   220  	if !it.initialized {
   221  		it.initialized = true
   222  		return len(it.keys) > 0
   223  	}
   224  	// Iterator already initialize, advance it
   225  	if len(it.keys) > 0 {
   226  		it.keys[0] = ""
   227  		it.keys = it.keys[1:]
   228  		it.values[0] = nil
   229  		it.values = it.values[1:]
   230  	}
   231  	return len(it.keys) > 0
   232  }
   233  
   234  func (it *iterator) Error() error {
   235  	return it.err
   236  }
   237  
   238  func (it *iterator) Key() []byte {
   239  	if len(it.keys) > 0 {
   240  		return []byte(it.keys[0])
   241  	}
   242  	return nil
   243  }
   244  
   245  func (it *iterator) Value() []byte {
   246  	if len(it.values) > 0 {
   247  		return it.values[0]
   248  	}
   249  	return nil
   250  }
   251  
   252  func (it *iterator) Release() {
   253  	it.keys = nil
   254  	it.values = nil
   255  }