Skip to content

Commit

Permalink
adapt hset command
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshi-099 committed Jun 5, 2024
1 parent c38c305 commit d26f4f7
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 72 deletions.
4 changes: 3 additions & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ var (

ErrUnknownType = errors.New("ERR unknown value type")

ErrParseInteger = errors.New("ERR value is not an integer or out of range")

ErrCRLFNotFound = errors.New("ERR CRLF not found in line")
)

func ErrWrongArgs(cmd string) error {
func ErrWrongNumberArgs(cmd string) error {
return fmt.Errorf("ERR wrong number of arguments for '%s' command", cmd)
}
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ require (
github.com/sakeven/RbTree v0.0.0-20240321014605-9899538dc980
github.com/stretchr/testify v1.9.0
github.com/tidwall/mmap v0.3.0
github.com/xgzlucario/GigaCache v0.0.0-20240531152919-576765cef731
github.com/xgzlucario/GigaCache v0.0.0-20240605031700-e88a04a9dd84
github.com/xgzlucario/quicklist v0.0.0-20240530174658-6f1a884f579b
golang.org/x/sys v0.20.0
golang.org/x/sys v0.21.0
)

require (
Expand All @@ -23,6 +23,6 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/mmap v0.3.0 h1:XXt1YsiXCF5/UAu3pLbu6g7iulJ9jsbs6vt7UpiV0sY=
github.com/tidwall/mmap v0.3.0/go.mod h1:2/dNzF5zA+te/JVHfrqNLcRkb8LjdH3c80vYHFQEZRk=
github.com/xgzlucario/GigaCache v0.0.0-20240531152919-576765cef731 h1:frRQxMZFCPWfoiWau4bPcYmNDGNVPLqM9nqnsp6Uakg=
github.com/xgzlucario/GigaCache v0.0.0-20240531152919-576765cef731/go.mod h1:sPwGPAuvd9WdiONTmusXGNocqcY5L/J7+st1upAMlX8=
github.com/xgzlucario/GigaCache v0.0.0-20240605031700-e88a04a9dd84 h1:YZQ7pvAASgoW0FsOF4pXkzgdWJQSS7j4JsOaU8Oc724=
github.com/xgzlucario/GigaCache v0.0.0-20240605031700-e88a04a9dd84/go.mod h1:sPwGPAuvd9WdiONTmusXGNocqcY5L/J7+st1upAMlX8=
github.com/xgzlucario/quicklist v0.0.0-20240530174658-6f1a884f579b h1:C/+nN/kFJ6yrmEhIu+5Ra2jx/W8w+Ayu8pTiZfuU5Xc=
github.com/xgzlucario/quicklist v0.0.0-20240530174658-6f1a884f579b/go.mod h1:1ZgyZNk91XIllYdOPpwP+9L2RCw6QGSy6alTYF+Z0iU=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
92 changes: 46 additions & 46 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"strconv"
"time"

cache "github.com/xgzlucario/GigaCache"
"github.com/xgzlucario/rotom/structx"
)

Expand All @@ -16,62 +15,78 @@ func pingCommand(_ []Value) Value {
func setCommand(args []Value) Value {
key := args[0].bulk
value := args[1].bulk
var ttl int64
exargs := args[2:]
var duration time.Duration

for i, arg := range args[2:] {
for i, arg := range exargs {
switch b2s(arg.bulk) {
case "NX":
case "PX":
case "EX":
if len(args) > i+3 {
seconds, _ := strconv.Atoi(b2s(args[i+3].bulk))
ttl = cache.GetNanoSec() + int64(seconds)*int64(time.Second)
case "NX", "nx":
case "PX", "px":
case "EX", "ex":
if len(exargs) > i+1 {
seconds, err := strconv.Atoi(b2s(exargs[i+1].bulk))
if err != nil {
return newErrValue(ErrParseInteger)
}
duration = time.Duration(seconds)
} else {
return newErrValue(ErrWrongArgs("set"))
return newErrValue(ErrWrongNumberArgs("set"))
}
}
}
db.strs.SetTx(b2s(key), value, ttl)
db.strs.SetEx(b2s(key), value, duration)
return ValueOK
}

func getCommand(args []Value) Value {
key := args[0].bulk
key := b2s(args[0].bulk)

value, _, ok := db.strs.Get(b2s(key))
value, _, ok := db.strs.Get(key)
if ok {
return newBulkValue(value)
}
// check extra maps
_, ok = db.extras[b2s(key)]
_, ok = db.extras[key]
if ok {
return newErrValue(ErrWrongType)
}
return ValueNull
}

func hsetCommand(args []Value) Value {
hash := b2s(args[0].bulk)
key := b2s(args[1].bulk)
value := args[2].bulk
hash := args[0].bulk

// check arguments number
exargs := args[1:]
if len(exargs) == 0 || len(exargs)%2 == 1 {
return newErrValue(ErrWrongNumberArgs("hset"))
}

m, err := fetchMap(hash, true)
hmap, err := fetchMap(hash, true)
if err != nil {
return newErrValue(err)
}
m.Set(key, value)
return ValueOK

var newFields int
for i := 0; i < len(exargs); i += 2 {
key := exargs[i].bulk
value := exargs[i+1].bulk
if hmap.Set(b2s(key), value) {
newFields++
}
}
return newIntegerValue(newFields)
}

func hgetCommand(args []Value) Value {
hash := args[0].bulk
key := args[1].bulk

m, err := fetchMap(b2s(hash))
hmap, err := fetchMap(hash)
if err != nil {
return newErrValue(ErrWrongType)
}
value, _, ok := m.Get(b2s(key))
value, _, ok := hmap.Get(b2s(key))
if !ok {
return ValueNull
}
Expand All @@ -82,13 +97,13 @@ func hdelCommand(args []Value) Value {
hash := args[0].bulk
keys := args[1:]

m, err := fetchMap(b2s(hash))
hmap, err := fetchMap(hash)
if err != nil {
return newErrValue(err)
}
var success int
for _, v := range keys {
if m.Remove(b2s(v.bulk)) {
if hmap.Remove(b2s(v.bulk)) {
success++
}
}
Expand All @@ -98,41 +113,25 @@ func hdelCommand(args []Value) Value {
func hgetallCommand(args []Value) Value {
hash := args[0].bulk

m, err := fetchMap(b2s(hash))
hmap, err := fetchMap(hash)
if err != nil {
return newErrValue(err)
}

res := make([]Value, 0, 8)
m.Scan(func(key, value []byte) {
hmap.Scan(func(key, value []byte) {
res = append(res, Value{typ: BULK, bulk: key})
res = append(res, Value{typ: BULK, bulk: value})
})
return newArrayValue(res)
}

func fetchMap(key string, setnx ...bool) (Map, error) {
func fetchMap(key []byte, setnx ...bool) (Map, error) {
return fetch(key, func() Map { return structx.NewMap() }, setnx...)
}

// func fetchSet(key string, setnx ...bool) (Set, error) {
// return fetch(key, func() Set { return structx.NewSet() }, setnx...)
// }

// func fetchList(key string, setnx ...bool) (List, error) {
// return fetch(key, func() List { return structx.NewList() }, setnx...)
// }

// func fetchBitMap(key string, setnx ...bool) (BitMap, error) {
// return fetch(key, func() BitMap { return structx.NewBitmap() }, setnx...)
// }

// func fetchZSet(key string, setnx ...bool) (ZSet, error) {
// return fetch(key, func() ZSet { return structx.NewZSet() }, setnx...)
// }

func fetch[T any](key string, new func() T, setnx ...bool) (v T, err error) {
item, ok := db.extras[key]
func fetch[T any](key []byte, new func() T, setnx ...bool) (v T, err error) {
item, ok := db.extras[b2s(key)]
if ok {
v, ok := item.(T)
if ok {
Expand All @@ -143,7 +142,8 @@ func fetch[T any](key string, new func() T, setnx ...bool) (v T, err error) {

v = new()
if len(setnx) > 0 && setnx[0] {
db.extras[key] = v
// here NEED to use copy of key
db.extras[string(key)] = v
}
return v, nil
}
32 changes: 30 additions & 2 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ func TestHandler(t *testing.T) {
assert := assert.New(t)

go startup()
time.Sleep(time.Second / 2)
time.Sleep(time.Second / 3)

// wait for client starup
rdb := redis.NewClient(&redis.Options{
Addr: ":20082",
})
time.Sleep(time.Second / 2)
time.Sleep(time.Second / 3)

t.Run("ping", func(t *testing.T) {
res, _ := rdb.Ping(ctx).Result()
Expand All @@ -45,5 +45,33 @@ func TestHandler(t *testing.T) {

res, _ = rdb.Get(ctx, "foo").Result()
assert.Equal(res, "bar")

// expire
rdb.Set(ctx, "foo", "bar1", time.Second).Result()
time.Sleep(time.Second * 2)

res, _ = rdb.Get(ctx, "foo").Result()
assert.Equal(res, "")
})

t.Run("hset", func(t *testing.T) {
res, _ := rdb.HSet(ctx, "map", "k1", "v1").Result()
assert.Equal(res, int64(1))

res, _ = rdb.HSet(ctx, "map", "k2", "v2", "k3", "v3").Result()
assert.Equal(res, int64(2))

res, _ = rdb.HSet(ctx, "map", map[string]any{"k4": "v4", "k5": "v5"}).Result()
assert.Equal(res, int64(2))

res, _ = rdb.HSet(ctx, "map", map[string]any{"k4": "v4", "k5": "v5"}).Result()
assert.Equal(res, int64(0))

resm, _ := rdb.HGetAll(ctx, "map").Result()
assert.Equal(resm, map[string]string{"k1": "v1", "k2": "v2", "k3": "v3", "k4": "v4", "k5": "v5"})

// error
_, err := rdb.HSet(ctx, "map").Result()
assert.Equal(err.Error(), ErrWrongNumberArgs("hset").Error())
})
}
28 changes: 22 additions & 6 deletions resp.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,19 +188,31 @@ func (v Value) Marshal() []byte {
}

func (v Value) marshalInteger() []byte {
str := strconv.FormatInt(v.num, 10)
return append([]byte{INTEGER}, append([]byte(str), '\r', '\n')...)
w := bytes.NewBuffer(nil)
w.WriteByte(INTEGER)
w.WriteString(strconv.FormatInt(v.num, 10))
w.Write(CRLF)
return w.Bytes()
}

// marshalString marshals a string value into RESP format.
func (v Value) marshalString() []byte {
return append([]byte{STRING}, append([]byte(v.str), '\r', '\n')...)
w := bytes.NewBuffer(nil)
w.WriteByte(STRING)
w.WriteString(v.str)
w.Write(CRLF)
return w.Bytes()
}

// marshalBulk marshals a bulk string into RESP format.
func (v Value) marshalBulk() []byte {
bulkHeader := append([]byte{BULK}, append([]byte(strconv.Itoa(len(v.bulk))), '\r', '\n')...)
return append(bulkHeader, append(v.bulk, '\r', '\n')...)
w := bytes.NewBuffer(nil)
w.WriteByte(BULK)
w.WriteString(strconv.Itoa(len(v.bulk)))
w.Write(CRLF)
w.Write(v.bulk)
w.Write(CRLF)
return w.Bytes()
}

// marshalArray marshals an array of values into RESP format.
Expand All @@ -214,7 +226,11 @@ func (v Value) marshalArray() []byte {

// marshallError marshals an error message into RESP format.
func (v Value) marshallError() []byte {
return append([]byte{ERROR}, append([]byte(v.str), '\r', '\n')...)
w := bytes.NewBuffer(nil)
w.WriteByte(ERROR)
w.WriteString(v.str)
w.Write(CRLF)
return w.Bytes()
}

// marshallNull marshals a null value into RESP bulk string format.
Expand Down
2 changes: 1 addition & 1 deletion rotom.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func lookupCommand(command []byte) (*Command, error) {

func (cmd *Command) processCommand(args []Value) Value {
if len(args) < cmd.arity {
return newErrValue(ErrWrongArgs(cmd.name))
return newErrValue(ErrWrongNumberArgs(cmd.name))
}
return cmd.handler(args)
}
Expand Down
13 changes: 6 additions & 7 deletions structx/map.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package structx

import (
"slices"

cache "github.com/xgzlucario/GigaCache"
)

Expand All @@ -13,7 +11,9 @@ type Map struct {
func defaultOptions() cache.Options {
options := cache.DefaultOptions
options.ConcurrencySafe = false
options.ShardCount = 4
// the hash keys is no need expire
options.DisableEvict = true
options.ShardCount = 1
options.IndexSize = 8
options.BufferSize = 32
return options
Expand All @@ -27,8 +27,8 @@ func (m *Map) Get(key string) ([]byte, int64, bool) {
return m.m.Get(key)
}

func (m *Map) Set(key string, val []byte) {
m.m.Set(key, val)
func (m *Map) Set(key string, val []byte) (newField bool) {
return m.m.Set(key, val)
}

func (m *Map) Remove(key string) bool {
Expand All @@ -37,8 +37,7 @@ func (m *Map) Remove(key string) bool {

func (m *Map) Scan(fn func(key, value []byte)) {
m.m.Scan(func(key, val []byte, _ int64) (next bool) {
// return copy
fn(slices.Clone(key), slices.Clone(val))
fn(key, val)
return true
})
}
Expand Down

0 comments on commit d26f4f7

Please sign in to comment.