diff --git a/.gitignore b/.gitignore index e3a4ef449c0b605b3a9b4b70e3ed4b5057ebb367..928ab7bddae22ed94b8bf4c98af9620320a0f30f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ vendor/ # Ignore vim .swp buffers for open files .*.swp .*.swo +# Ignore ekv test files +.ekv_testdir_* \ No newline at end of file diff --git a/interface_test.go b/interface_test.go index 362aac08c6920a9edbcacdd24b76956b7bb537e3..6c72eac1faef29e702ed47fa83c17fc14950854a 100644 --- a/interface_test.go +++ b/interface_test.go @@ -14,7 +14,7 @@ import ( // Tests happy path of Exists() with Memstore. func TestExists_Memstore(t *testing.T) { - f := make(Memstore) + f := MakeMemstore() i := &MarshalableString{S: "Hi"} err := f.Set("key2", i) if err != nil { diff --git a/memstore.go b/memstore.go index f949a5408935dfd69a0c30c476dc403b18c57565..622f3691ff6c4050d4feac91c686013007138a57 100644 --- a/memstore.go +++ b/memstore.go @@ -10,6 +10,7 @@ package ekv import ( "encoding/json" "github.com/pkg/errors" + "sync" ) const ( @@ -17,47 +18,73 @@ const ( setInterfaceErr = "SetInterface error" ) -// Memstore is an unencrypted memory based map that implements the KV interface -type Memstore map[string][]byte +// Memstore is an unencrypted memory-based map that implements the KeyValue +// interface. +type Memstore struct { + store map[string][]byte + mux sync.RWMutex +} + +// MakeMemstore returns a new Memstore with a newly initialised a new map. +func MakeMemstore() *Memstore { + return &Memstore{store: make(map[string][]byte)} +} + +// Set stores the value if there's no serialization error. +func (m *Memstore) Set(key string, objectToStore Marshaler) error { + m.mux.Lock() + defer m.mux.Unlock() -// Set stores the value if there's no serialization error -func (m Memstore) Set(key string, objectToStore Marshaler) error { ser := objectToStore.Marshal() - m[key] = ser + m.store[key] = ser return nil } -// Get returns the value -func (m Memstore) Get(key string, loadIntoThisObject Unmarshaler) error { - data, ok := m[key] +// Get returns the value. +func (m *Memstore) Get(key string, loadIntoThisObject Unmarshaler) error { + m.mux.RLock() + defer m.mux.RUnlock() + + data, ok := m.store[key] if !ok { return errors.New(objectNotFoundErr) } return loadIntoThisObject.Unmarshal(data) } -// Get returns the value -func (m Memstore) Delete(key string) error { - delete(m, key) +// Delete removes the value from the store. +func (m *Memstore) Delete(key string) error { + m.mux.Lock() + defer m.mux.Unlock() + + delete(m.store, key) return nil } -// SetInterface sets the value using a json encoder -func (m Memstore) SetInterface(key string, objectToStore interface{}) error { +// SetInterface sets the value using a JSON encoder. +func (m *Memstore) SetInterface(key string, objectToStore interface{}) error { + m.mux.Lock() + defer m.mux.Unlock() + data, err := json.Marshal(objectToStore) if err != nil { return errors.Wrap(err, setInterfaceErr) } - m[key] = data + + m.store[key] = data return nil } -// GetInterface gets the value using a json encoder -func (m Memstore) GetInterface(key string, objectToLoad interface{}) error { - data, ok := m[key] +// GetInterface gets the value using a JSON encoder. +func (m *Memstore) GetInterface(key string, objectToLoad interface{}) error { + m.mux.Lock() + defer m.mux.Unlock() + + data, ok := m.store[key] if !ok { return errors.New(objectNotFoundErr) } + err := json.Unmarshal(data, objectToLoad) if err != nil { return errors.WithStack(err) diff --git a/memstore_test.go b/memstore_test.go index ef294f2f5b3aea38b94e5ada18112d207f68aea2..1b54af5b8b059da9b8fff06fd7313073468c8cb5 100644 --- a/memstore_test.go +++ b/memstore_test.go @@ -12,9 +12,9 @@ import ( "testing" ) -// TestFilestore_Smoke runs a basic read/write on the current directory +// TestMemstore_Smoke runs a basic read/write on the current directory. func TestMemstore_Smoke(t *testing.T) { - f := make(Memstore) + f := MakeMemstore() i := &MarshalableString{ S: "Hi", } @@ -46,9 +46,9 @@ func TestMemstore_Smoke(t *testing.T) { } } -// TestFilestore_Broken tries to marshal with a broken object +// TestMemstore_Broken tries to marshal with a broken object. func TestMemstore_Broken(t *testing.T) { - f := make(Memstore) + f := MakeMemstore() i := &BrokenMarshalable{ S: "Hi", @@ -65,11 +65,11 @@ func TestMemstore_Broken(t *testing.T) { } } -// TestFilestore_Multiset makes sure we can continuously set the object and get +// TestMemstore_Multiset makes sure we can continuously set the object and get // the right result each time (exercises the internal monotonic counter -// functionality) +// functionality). func TestMemstore_Multiset(t *testing.T) { - f := make(Memstore) + f := MakeMemstore() for x := 0; x < 20; x++ { expStr := fmt.Sprintf("Hi, %d!", x)