From e187711c6545487d4cac3701f0f506bb536234e2 Mon Sep 17 00:00:00 2001 From: ethersphere Date: Wed, 20 Jun 2018 14:06:27 +0200 Subject: swarm: network rewrite merge --- swarm/state/dbstore.go | 96 ++++++++++++++++++++++++++++++++++ swarm/state/dbstore_test.go | 122 +++++++++++++++++++++++++++++++++++++++++++ swarm/state/inmemorystore.go | 94 +++++++++++++++++++++++++++++++++ swarm/state/store.go | 26 +++++++++ 4 files changed, 338 insertions(+) create mode 100644 swarm/state/dbstore.go create mode 100644 swarm/state/dbstore_test.go create mode 100644 swarm/state/inmemorystore.go create mode 100644 swarm/state/store.go (limited to 'swarm/state') diff --git a/swarm/state/dbstore.go b/swarm/state/dbstore.go new file mode 100644 index 000000000..5e5c172b2 --- /dev/null +++ b/swarm/state/dbstore.go @@ -0,0 +1,96 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "encoding" + "encoding/json" + "errors" + + "github.com/syndtr/goleveldb/leveldb" +) + +// ErrNotFound is returned when no results are returned from the database +var ErrNotFound = errors.New("ErrorNotFound") + +// ErrInvalidArgument is returned when the argument type does not match the expected type +var ErrInvalidArgument = errors.New("ErrorInvalidArgument") + +// DBStore uses LevelDB to store values. +type DBStore struct { + db *leveldb.DB +} + +// NewDBStore creates a new instance of DBStore. +func NewDBStore(path string) (s *DBStore, err error) { + db, err := leveldb.OpenFile(path, nil) + if err != nil { + return nil, err + } + return &DBStore{ + db: db, + }, nil +} + +// Get retrieves a persisted value for a specific key. If there is no results +// ErrNotFound is returned. The provided parameter should be either a byte slice or +// a struct that implements the encoding.BinaryUnmarshaler interface +func (s *DBStore) Get(key string, i interface{}) (err error) { + has, err := s.db.Has([]byte(key), nil) + if err != nil || !has { + return ErrNotFound + } + + data, err := s.db.Get([]byte(key), nil) + if err == leveldb.ErrNotFound { + return ErrNotFound + } + + unmarshaler, ok := i.(encoding.BinaryUnmarshaler) + if !ok { + return json.Unmarshal(data, i) + } + return unmarshaler.UnmarshalBinary(data) +} + +// Put stores an object that implements Binary for a specific key. +func (s *DBStore) Put(key string, i interface{}) (err error) { + bytes := []byte{} + + marshaler, ok := i.(encoding.BinaryMarshaler) + if !ok { + if bytes, err = json.Marshal(i); err != nil { + return err + } + } else { + if bytes, err = marshaler.MarshalBinary(); err != nil { + return err + } + } + + return s.db.Put([]byte(key), bytes, nil) +} + +// Delete removes entries stored under a specific key. +func (s *DBStore) Delete(key string) (err error) { + return s.db.Delete([]byte(key), nil) +} + +// Close releases the resources used by the underlying LevelDB. +func (s *DBStore) Close() error { + return s.db.Close() +} diff --git a/swarm/state/dbstore_test.go b/swarm/state/dbstore_test.go new file mode 100644 index 000000000..6683e788f --- /dev/null +++ b/swarm/state/dbstore_test.go @@ -0,0 +1,122 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "bytes" + "errors" + "io/ioutil" + "os" + "strings" + "testing" +) + +var ErrInvalidArraySize = errors.New("invalid byte array size") +var ErrInvalidValuePersisted = errors.New("invalid value was persisted to the db") + +type SerializingType struct { + key string + value string +} + +func (st *SerializingType) MarshalBinary() (data []byte, err error) { + d := []byte(strings.Join([]string{st.key, st.value}, ";")) + + return d, nil +} + +func (st *SerializingType) UnmarshalBinary(data []byte) (err error) { + d := bytes.Split(data, []byte(";")) + l := len(d) + if l == 0 { + return ErrInvalidArraySize + } + if l == 2 { + keyLen := len(d[0]) + st.key = string(d[0][:keyLen]) + + valLen := len(d[1]) + st.value = string(d[1][:valLen]) + } + + return nil +} + +// TestDBStore tests basic functionality of DBStore. +func TestDBStore(t *testing.T) { + dir, err := ioutil.TempDir("", "db_store_test") + if err != nil { + panic(err) + } + defer os.RemoveAll(dir) + + store, err := NewDBStore(dir) + if err != nil { + t.Fatal(err) + } + + testStore(t, store) + + store.Close() + + persistedStore, err := NewDBStore(dir) + if err != nil { + t.Fatal(err) + } + defer persistedStore.Close() + + testPersistedStore(t, persistedStore) +} + +func testStore(t *testing.T, store Store) { + ser := &SerializingType{key: "key1", value: "value1"} + jsonify := []string{"a", "b", "c"} + + err := store.Put(ser.key, ser) + if err != nil { + t.Fatal(err) + } + + err = store.Put("key2", jsonify) + if err != nil { + t.Fatal(err) + } + +} + +func testPersistedStore(t *testing.T, store Store) { + ser := &SerializingType{} + + err := store.Get("key1", ser) + if err != nil { + t.Fatal(err) + } + + if ser.key != "key1" || ser.value != "value1" { + t.Fatal(ErrInvalidValuePersisted) + } + + as := []string{} + err = store.Get("key2", &as) + + if len(as) != 3 { + t.Fatalf("serialized array did not match expectation") + } + if as[0] != "a" || as[1] != "b" || as[2] != "c" { + t.Fatalf("elements serialized did not match expected values") + } +} diff --git a/swarm/state/inmemorystore.go b/swarm/state/inmemorystore.go new file mode 100644 index 000000000..1ca25404a --- /dev/null +++ b/swarm/state/inmemorystore.go @@ -0,0 +1,94 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "encoding" + "encoding/json" + "sync" +) + +// InmemoryStore is the reference implementation of Store interface that is supposed +// to be used in tests. +type InmemoryStore struct { + db map[string][]byte + mu sync.RWMutex +} + +// NewInmemoryStore returns a new instance of InmemoryStore. +func NewInmemoryStore() *InmemoryStore { + return &InmemoryStore{ + db: make(map[string][]byte), + } +} + +// Get retrieves a value stored for a specific key. If there is no value found, +// ErrNotFound is returned. +func (s *InmemoryStore) Get(key string, i interface{}) (err error) { + s.mu.RLock() + defer s.mu.RUnlock() + + bytes, ok := s.db[key] + if !ok { + return ErrNotFound + } + + unmarshaler, ok := i.(encoding.BinaryUnmarshaler) + if !ok { + return json.Unmarshal(bytes, i) + } + + return unmarshaler.UnmarshalBinary(bytes) +} + +// Put stores a value for a specific key. +func (s *InmemoryStore) Put(key string, i interface{}) (err error) { + s.mu.Lock() + defer s.mu.Unlock() + bytes := []byte{} + + marshaler, ok := i.(encoding.BinaryMarshaler) + if !ok { + if bytes, err = json.Marshal(i); err != nil { + return err + } + } else { + if bytes, err = marshaler.MarshalBinary(); err != nil { + return err + } + } + + s.db[key] = bytes + return nil +} + +// Delete removes value stored under a specific key. +func (s *InmemoryStore) Delete(key string) (err error) { + s.mu.Lock() + defer s.mu.Unlock() + + if _, ok := s.db[key]; !ok { + return ErrNotFound + } + delete(s.db, key) + return nil +} + +// Close does not do anything. +func (s *InmemoryStore) Close() error { + return nil +} diff --git a/swarm/state/store.go b/swarm/state/store.go new file mode 100644 index 000000000..fb7fe258f --- /dev/null +++ b/swarm/state/store.go @@ -0,0 +1,26 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +// Store defines methods required to get, set, delete values for different keys +// and close the underlying resources. +type Store interface { + Get(key string, i interface{}) (err error) + Put(key string, i interface{}) (err error) + Delete(key string) (err error) + Close() error +} -- cgit v1.2.3