Skip to content

Commit

Permalink
add: add cmd lpush, rpush, lrange
Browse files Browse the repository at this point in the history
  • Loading branch information
xgzlucario committed Jun 8, 2024
1 parent 6f78f00 commit 14efecf
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 69 deletions.
71 changes: 71 additions & 0 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ import (
"github.com/xgzlucario/rotom/structx"
)

var (
// cmdTable is the list of all available commands.
cmdTable []*Command = []*Command{
{"ping", pingCommand, 0, false},
{"set", setCommand, 2, true},
{"get", getCommand, 1, false},
{"hset", hsetCommand, 3, true},
{"hget", hgetCommand, 2, false},
{"hdel", hdelCommand, 2, true},
{"hgetall", hgetallCommand, 1, false},
{"rpush", rpushCommand, 2, true},
{"lpush", lpushCommand, 2, true},
{"lrange", lrangeCommand, 3, false},
}
)

func pingCommand(_ []Value) Value {
return Value{typ: STRING, raw: []byte("PONG")}
}
Expand Down Expand Up @@ -105,10 +121,65 @@ func hgetallCommand(args []Value) Value {
return newArrayValue(res)
}

func pushInternal(args []Value, isDirectLeft bool) Value {
key := args[0].ToString()

ls, err := fetchList(key, true)
if err != nil {
return newErrValue(err)
}
if isDirectLeft {
for _, arg := range args[1:] {
ls.LPush(arg.ToString())
}
} else {
for _, arg := range args[1:] {
ls.RPush(arg.ToString())
}
}
return newIntegerValue(ls.Size())
}

func lpushCommand(args []Value) Value {
return pushInternal(args, true)
}

func rpushCommand(args []Value) Value {
return pushInternal(args, false)
}

func lrangeCommand(args []Value) Value {
key := args[0].ToString()
start, err := args[1].ToInt()
if err != nil {
return newErrValue(err)
}
end, err := args[2].ToInt()
if err != nil {
return newErrValue(err)
}

ls, err := fetchList(key)
if err != nil {
return newErrValue(err)
}

var res []Value
ls.Range(start, end, func(data []byte) (stop bool) {
res = append(res, newBulkValue(data))
return false
})
return newArrayValue(res)
}

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

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

func fetch[T any](key string, new func() T, setnx ...bool) (v T, err error) {
item, ok := db.extras[key]
if ok {
Expand Down
24 changes: 22 additions & 2 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestHandler(t *testing.T) {
assert.Equal(res, "PONG")
})

t.Run("set", func(t *testing.T) {
t.Run("key", func(t *testing.T) {
res, _ := rdb.Set(ctx, "foo", "bar", 0).Result()
assert.Equal(res, "OK")

Expand All @@ -54,7 +54,7 @@ func TestHandler(t *testing.T) {
assert.Equal(res, "")
})

t.Run("hset", func(t *testing.T) {
t.Run("hash", func(t *testing.T) {
// hset
res, _ := rdb.HSet(ctx, "map", "k1", "v1").Result()
assert.Equal(res, int64(1))
Expand All @@ -81,11 +81,31 @@ func TestHandler(t *testing.T) {
resm, _ := rdb.HGetAll(ctx, "map").Result()
assert.Equal(resm, map[string]string{"k1": "v1", "k2": "v2", "k3": "v3", "k4": "v4", "k5": "v5"})

// hdel
{
res, _ := rdb.HDel(ctx, "map", "k1", "k2", "k3", "k99").Result()
assert.Equal(res, int64(3))
}

// error
_, err := rdb.HSet(ctx, "map").Result()
assert.Equal(err.Error(), ErrWrongNumberArgs("hset").Error())

_, err = rdb.HSet(ctx, "map", "k1", "v1", "k2").Result()
assert.Equal(err.Error(), ErrWrongNumberArgs("hset").Error())
})

t.Run("list", func(t *testing.T) {
// lpush
n, _ := rdb.LPush(ctx, "list", "a", "b", "c").Result()
assert.Equal(n, int64(3))

// rpush
n, _ = rdb.RPush(ctx, "list", "d", "e", "f").Result()
assert.Equal(n, int64(6))

// lrange
res, _ := rdb.LRange(ctx, "list", 0, -1).Result()
assert.Equal(res, []string{"c", "b", "a", "d", "e", "f"})
})
}
71 changes: 40 additions & 31 deletions resp.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var (
// Value represents the different types of RESP (Redis Serialization Protocol) values.
type Value struct {
typ byte // Type of value ('string', 'error', 'integer', 'bulk', 'array', 'null')
raw []byte // Used for string, error, integer ad bulk strings
raw []byte // Used for string, error, integer and bulk strings
array []Value // Used for arrays of nested values
}

Expand Down Expand Up @@ -161,9 +161,17 @@ func (r *Resp) readBulk() (Value, error) {
return value, nil
}

func (v Value) ToString() string { return string(v.raw) }
func (v Value) ToString() string {
return string(v.raw)
}

func (v Value) ToInt() (int, error) {
return strconv.Atoi(string(v.raw))
}

func (v Value) ToBytes() []byte { return v.raw }
func (v Value) ToBytes() []byte {
return v.raw
}

// Marshal converts a Value object into its corresponding RESP bytes.
func (v Value) Marshal() []byte {
Expand All @@ -186,52 +194,53 @@ func (v Value) Marshal() []byte {
}

func (v Value) marshalInteger() []byte {
w := bytes.NewBuffer(nil)
w.WriteByte(INTEGER)
w.Write(v.raw)
w.Write(CRLF)
return w.Bytes()
buf := make([]byte, 0, 1+len(v.raw)+2)
buf = append(buf, INTEGER)
buf = append(buf, v.raw...)
buf = append(buf, CRLF...)
return buf
}

// marshalString marshals a string value into RESP format.
func (v Value) marshalString() []byte {
w := bytes.NewBuffer(nil)
w.WriteByte(STRING)
w.Write(v.raw)
w.Write(CRLF)
return w.Bytes()
buf := make([]byte, 0, 1+len(v.raw)+2)
buf = append(buf, STRING)
buf = append(buf, v.raw...)
buf = append(buf, CRLF...)
return buf
}

// marshalBulk marshals a bulk string into RESP format.
func (v Value) marshalBulk() []byte {
w := bytes.NewBuffer(nil)
w.WriteByte(BULK)
w.WriteString(strconv.Itoa(len(v.raw)))
w.Write(CRLF)
w.Write(v.raw)
w.Write(CRLF)
return w.Bytes()
format := strconv.Itoa(len(v.raw))
buf := make([]byte, 0, 1+len(format)+2+len(v.raw)+2)
buf = append(buf, BULK)
buf = append(buf, format...)
buf = append(buf, CRLF...)
buf = append(buf, v.raw...)
buf = append(buf, CRLF...)
return buf
}

// marshalArray marshals an array of values into RESP format.
func (v Value) marshalArray() []byte {
w := bytes.NewBuffer(nil)
w.WriteByte(ARRAY)
w.WriteString(strconv.Itoa(len(v.array)))
w.Write(CRLF)
buf := make([]byte, 0, 16)
buf = append(buf, ARRAY)
buf = append(buf, strconv.Itoa(len(v.array))...)
buf = append(buf, CRLF...)
for _, val := range v.array {
w.Write(val.Marshal())
buf = append(buf, val.Marshal()...)
}
return w.Bytes()
return buf
}

// marshallError marshals an error message into RESP format.
func (v Value) marshallError() []byte {
w := bytes.NewBuffer(nil)
w.WriteByte(ERROR)
w.Write(v.raw)
w.Write(CRLF)
return w.Bytes()
buf := make([]byte, 0, 1+len(v.raw)+2)
buf = append(buf, ERROR)
buf = append(buf, v.raw...)
buf = append(buf, CRLF...)
return buf
}

// marshallNull marshals a null value into RESP bulk string format.
Expand Down
21 changes: 0 additions & 21 deletions resp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,24 +102,3 @@ func TestValue(t *testing.T) {
assert.Equal(string(data), ErrUnknownType.Error())
})
}

func BenchmarkRESP(b *testing.B) {
b.Run("str-value", func(b *testing.B) {
value := ValueOK
for i := 0; i < b.N; i++ {
value.Marshal()
}
})
b.Run("bulk-value", func(b *testing.B) {
value := newBulkValue([]byte("hello"))
for i := 0; i < b.N; i++ {
value.Marshal()
}
})
b.Run("integer-value", func(b *testing.B) {
value := newIntegerValue(100)
for i := 0; i < b.N; i++ {
value.Marshal()
}
})
}
16 changes: 1 addition & 15 deletions rotom.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,8 @@ type Command struct {
}

var (
// db is the main database object.
db DB

// server is the main server object.
db DB
server Server

// cmdTable is the list of all available commands.
cmdTable []*Command = []*Command{
{"ping", pingCommand, 0, false},
{"set", setCommand, 2, true},
{"get", getCommand, 1, false},
{"hset", hsetCommand, 3, true},
{"hget", hgetCommand, 2, false},
{"hdel", hdelCommand, 2, true},
{"hgetall", hgetallCommand, 1, false},
}
)

func lookupCommand(command string) *Command {
Expand Down

0 comments on commit 14efecf

Please sign in to comment.