Adding the challenge 0103
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
package db0103
|
||||
|
||||
type KV struct {
|
||||
log Log
|
||||
mem map[string][]byte
|
||||
}
|
||||
|
||||
func (kv *KV) Open() error
|
||||
|
||||
func (kv *KV) Close() error { return kv.log.Close() }
|
||||
|
||||
func (kv *KV) Get(key []byte) (val []byte, ok bool, err error) {
|
||||
val, ok = kv.mem[string(key)]
|
||||
return
|
||||
}
|
||||
|
||||
func (kv *KV) Set(key []byte, val []byte) (updated bool, err error)
|
||||
|
||||
func (kv *KV) Del(key []byte) (deleted bool, err error)
|
||||
|
||||
// QzBQWVJJOUhU https://trialofcode.org/
|
||||
@@ -0,0 +1,51 @@
|
||||
package db0103
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
key []byte
|
||||
val []byte
|
||||
deleted bool
|
||||
}
|
||||
|
||||
func (ent *Entry) Encode() []byte {
|
||||
data := make([]byte, 4+4+1+len(ent.key)+len(ent.val))
|
||||
binary.LittleEndian.PutUint32(data[0:4], uint32(len(ent.key)))
|
||||
copy(data[9:], ent.key)
|
||||
if ent.deleted {
|
||||
data[8] = 1
|
||||
} else {
|
||||
binary.LittleEndian.PutUint32(data[4:8], uint32(len(ent.val)))
|
||||
copy(data[9+len(ent.key):], ent.val)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (ent *Entry) Decode(r io.Reader) error {
|
||||
var header [9]byte
|
||||
if _, err := io.ReadFull(r, header[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
klen := int(binary.LittleEndian.Uint32(header[0:4]))
|
||||
vlen := int(binary.LittleEndian.Uint32(header[4:8]))
|
||||
deleted := header[8]
|
||||
|
||||
data := make([]byte, klen+vlen)
|
||||
if _, err := io.ReadFull(r, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ent.key = data[:klen]
|
||||
if deleted != 0 {
|
||||
ent.deleted = true
|
||||
} else {
|
||||
ent.deleted = false
|
||||
ent.val = data[klen:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// QzBQWVJJOUhU https://trialofcode.org/
|
||||
@@ -0,0 +1,75 @@
|
||||
package db0103
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestKVBasic(t *testing.T) {
|
||||
kv := KV{}
|
||||
kv.log.FileName = ".test_db"
|
||||
defer os.Remove(kv.log.FileName)
|
||||
|
||||
os.Remove(kv.log.FileName)
|
||||
err := kv.Open()
|
||||
assert.Nil(t, err)
|
||||
defer kv.Close()
|
||||
|
||||
updated, err := kv.Set([]byte("k1"), []byte("v1"))
|
||||
assert.True(t, updated && err == nil)
|
||||
|
||||
val, ok, err := kv.Get([]byte("k1"))
|
||||
assert.True(t, string(val) == "v1" && ok && err == nil)
|
||||
|
||||
_, ok, err = kv.Get([]byte("xxx"))
|
||||
assert.True(t, !ok && err == nil)
|
||||
|
||||
updated, err = kv.Del([]byte("xxx"))
|
||||
assert.True(t, !updated && err == nil)
|
||||
|
||||
updated, err = kv.Del([]byte("k1"))
|
||||
assert.True(t, updated && err == nil)
|
||||
|
||||
_, ok, err = kv.Get([]byte("xxx"))
|
||||
assert.True(t, !ok && err == nil)
|
||||
|
||||
updated, err = kv.Set([]byte("k2"), []byte("v2"))
|
||||
assert.True(t, updated && err == nil)
|
||||
|
||||
// reopen
|
||||
kv.Close()
|
||||
err = kv.Open()
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, ok, err = kv.Get([]byte("k1"))
|
||||
assert.True(t, !ok && err == nil)
|
||||
val, ok, err = kv.Get([]byte("k2"))
|
||||
assert.True(t, string(val) == "v2" && ok && err == nil)
|
||||
}
|
||||
|
||||
func TestEntryEncode(t *testing.T) {
|
||||
ent := Entry{key: []byte("k1"), val: []byte("xxx")}
|
||||
data := []byte{2, 0, 0, 0, 3, 0, 0, 0, 0, 'k', '1', 'x', 'x', 'x'}
|
||||
|
||||
assert.Equal(t, data, ent.Encode())
|
||||
|
||||
decoded := Entry{}
|
||||
err := decoded.Decode(bytes.NewBuffer(data))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ent, decoded)
|
||||
|
||||
ent = Entry{key: []byte("k1"), deleted: true}
|
||||
data = []byte{2, 0, 0, 0, 0, 0, 0, 0, 1, 'k', '1'}
|
||||
|
||||
assert.Equal(t, data, ent.Encode())
|
||||
|
||||
decoded = Entry{}
|
||||
err = decoded.Decode(bytes.NewBuffer(data))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ent, decoded)
|
||||
}
|
||||
|
||||
// QzBQWVJJOUhU https://trialofcode.org/
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package db0103
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Log struct {
|
||||
FileName string
|
||||
fp *os.File
|
||||
}
|
||||
|
||||
func (log *Log) Open() (err error) {
|
||||
log.fp, err = os.OpenFile(log.FileName, os.O_RDWR|os.O_CREATE, 0o644)
|
||||
return err
|
||||
}
|
||||
|
||||
func (log *Log) Close() error {
|
||||
return log.fp.Close()
|
||||
}
|
||||
|
||||
func (log *Log) Write(ent *Entry) error {
|
||||
_, err := log.fp.Write(ent.Encode())
|
||||
return err
|
||||
}
|
||||
|
||||
func (log *Log) Read(ent *Entry) (eof bool, err error) {
|
||||
err = ent.Decode(log.fp)
|
||||
if err == io.EOF {
|
||||
return true, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// QzBQWVJJOUhU https://trialofcode.org/
|
||||
Reference in New Issue
Block a user