diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..5fd31c1 --- /dev/null +++ b/errors.go @@ -0,0 +1,5 @@ +package zkv + +import "errors" + +var ErrNotExists = errors.New("not exists") diff --git a/go.mod b/go.mod index 313f293..392f6ec 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ module github.com/nxshock/zkv -go 1.17 +go 1.19 require ( - github.com/klauspost/compress v1.14.2 - github.com/stretchr/testify v1.7.0 + github.com/klauspost/compress v1.15.12 + github.com/stretchr/testify v1.8.1 ) require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9feb705..8609118 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,19 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= -github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= +github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/record.go b/record.go index 02a026e..eac0325 100644 --- a/record.go +++ b/record.go @@ -15,10 +15,9 @@ const ( ) type Record struct { - Type RecordType `json:"t"` - KeyHash []byte `json:"h"` - KeyBytes []byte `json:"k,omitempty"` // optional - ValueBytes []byte `json:"v"` + Type RecordType + KeyHash []byte + ValueBytes []byte } func newRecord(recordType RecordType, key, value interface{}) (*Record, error) { @@ -35,7 +34,6 @@ func newRecord(recordType RecordType, key, value interface{}) (*Record, error) { record := &Record{ Type: recordType, KeyHash: hashBytes(keyBytes), - KeyBytes: keyBytes, ValueBytes: valueBytes} return record, nil diff --git a/utils.go b/utils.go index 3e9c55c..10e0e15 100644 --- a/utils.go +++ b/utils.go @@ -5,7 +5,6 @@ import ( "crypto/sha256" "encoding/gob" "io" - "io/ioutil" ) func encode(value interface{}) ([]byte, error) { @@ -39,7 +38,7 @@ func skip(r io.Reader, count int64) (err error) { case io.Seeker: _, err = r.Seek(count, io.SeekCurrent) default: - _, err = io.CopyN(ioutil.Discard, r, count) + _, err = io.CopyN(io.Discard, r, count) } return err diff --git a/zkv.go b/zkv.go index 7d49c2a..d8b75fa 100644 --- a/zkv.go +++ b/zkv.go @@ -3,7 +3,6 @@ package zkv import ( "bytes" "encoding/base64" - "errors" "fmt" "io" "os" @@ -12,10 +11,6 @@ import ( "github.com/klauspost/compress/zstd" ) -type Options struct { - SaveKeys bool -} - type Database struct { dataOffset map[string]int64 file *os.File @@ -23,8 +18,6 @@ type Database struct { filePath string offset int64 - options Options - mu sync.Mutex } @@ -49,10 +42,6 @@ func (db *Database) Set(key, value interface{}) error { return err } - if !db.options.SaveKeys { - record.KeyBytes = nil - } - b, err := record.Marshal() if err != nil { return err @@ -81,7 +70,7 @@ func (db *Database) Get(key, value interface{}) error { offset, exists := db.dataOffset[string(hashToFind)] if !exists { - return errors.New("not exists") // TODO: заменить на константную ошибку + return ErrNotExists } readF, err := os.Open(db.filePath) @@ -106,7 +95,7 @@ func (db *Database) Get(key, value interface{}) error { return err } - if bytes.Compare(record.KeyHash, hashToFind) != 0 { + if !bytes.Equal(record.KeyHash, hashToFind) { return fmt.Errorf("wrong hash on this offset: expected %s, got %s", base64.StdEncoding.EncodeToString(hashToFind), base64.StdEncoding.EncodeToString(record.KeyHash)) // TODO: заменить на константную ошибку } @@ -168,3 +157,34 @@ func Open(filePath string) (*Database, error) { return database, nil } + +func (db *Database) Delete(key interface{}) error { + db.mu.Lock() + defer db.mu.Unlock() + + keyHash, err := hashInterface(key) + if err != nil { + return err + } + + record := &Record{ + Type: RecordTypeDelete, + KeyHash: keyHash, + } + + b, err := record.Marshal() + if err != nil { + return err + } + + delete(db.dataOffset, string(record.KeyHash)) + + _, err = db.compressor.Write(b) + if err != nil { + return err + } + + db.offset += int64(len(b)) + + return nil +} diff --git a/zkv_test.go b/zkv_test.go index cd5c58e..36f6aac 100644 --- a/zkv_test.go +++ b/zkv_test.go @@ -77,3 +77,44 @@ func TestSmallWrites(t *testing.T) { err = db.Close() assert.NoError(t, err) } + +func TestDeleteBasic(t *testing.T) { + const filePath = "TestDeleteBasic.zkv" + const recordCount = 100 + defer os.Remove(filePath) + + db, err := Open(filePath) + assert.NoError(t, err) + + for i := 1; i <= recordCount; i++ { + err = db.Set(i, i) + assert.NoError(t, err) + } + + assert.Len(t, db.dataOffset, recordCount) + + err = db.Delete(50) + assert.NoError(t, err) + + assert.Len(t, db.dataOffset, recordCount-1) + var value int + err = db.Get(50, &value) + assert.Equal(t, 0, value) + assert.ErrorIs(t, err, ErrNotExists) + + err = db.Close() + assert.NoError(t, err) + + // try to read + db, err = Open(filePath) + assert.NoError(t, err) + + assert.Len(t, db.dataOffset, recordCount-1) + value = 0 + err = db.Get(50, &value) + assert.Equal(t, 0, value) + assert.ErrorIs(t, err, ErrNotExists) + + err = db.Close() + assert.NoError(t, err) +}