aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--handlers.go672
-rw-r--r--main.go33
-rw-r--r--pkg/commands/command-registry.go73
-rw-r--r--pkg/commands/dbsize.go15
-rw-r--r--pkg/commands/expire.go26
-rw-r--r--pkg/commands/flushall.go14
-rw-r--r--pkg/commands/hget.go25
-rw-r--r--pkg/commands/hgetall.go20
-rw-r--r--pkg/commands/hkeys.go24
-rw-r--r--pkg/commands/hset.go32
-rw-r--r--pkg/commands/hvalues.go24
-rw-r--r--pkg/commands/keys.go41
-rw-r--r--pkg/commands/lrem.go75
-rw-r--r--pkg/commands/ping.go16
-rw-r--r--pkg/commands/type.go24
-rwxr-xr-xredisClonebin0 -> 3632615 bytes
16 files changed, 409 insertions, 705 deletions
diff --git a/handlers.go b/handlers.go
deleted file mode 100644
index 1569771..0000000
--- a/handlers.go
+++ /dev/null
@@ -1,672 +0,0 @@
-package main
-
-import (
- "errors"
- "net"
- "path/filepath"
- "redisClone/pkg/core"
- "sort"
- "strconv"
- "time"
-)
-
-func handleGet(args []string)interface{} {
- if len(args) != 2 {
- err := errors.New("wrong number of arguments for 'GET'")
- return err
- }
-
- key := args[1]
-
- result := db.ExecuteRead(key, func(s *core.Shard) interface{}{
- item, exists := s.Data[key]
- if !exists {
- return nil
- }
-
- if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) {
- delete(s.Data, key)
- return nil
- }
-
- strValue, ok := item.Value.(string)
- if !ok {
- err := errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- return err
- }
- return strValue
- })
-
- return result
-}
-
-func handleExists(args []string)interface{} {
- if len(args) != 2 {
- err := errors.New("wrong number of arguments for 'EXISTS'")
- return err
- }
-
- key := args[1]
- result := db.ExecuteRead(key, func(s *core.Shard) interface{}{
- _, exists := s.Data[key]
- if exists {
- return 1
- } else {
- return 0
- }
- })
-
- return result
-}
-
-func handleSet(args []string)interface{} {
- if len(args) != 3 {
- err := errors.New("wrong number of arguments for 'SET'")
- return err
- }
-
- key := args[1]
- result := db.Execute(key, func(s *core.Shard) interface{}{
- item := core.Item{Value: args[2]}
- s.Data[key] = item
- return core.SimpleString("OK")
- })
-
- return result
-}
-
-func handleDel(args []string)interface{} {
-
- if len(args) < 2 {
- err := errors.New("wrong number of arguments for 'DEL'")
- return err
- }
- keys := args[1:]
- deletedKeys := db.ExecuteMulti(keys, func(shards []*core.Shard) interface{}{
- count := 0
- for _, key := range keys{
- shard := db.GetShard(key)
- _, exists := shard.Data[key]
- if exists {
- delete(shard.Data, key)
- count += 1
- }
- }
- return count
- })
-
- return deletedKeys
-}
-
-
-func handleIncr(args []string)interface{} {
-
- if len(args) != 2 {
- err := errors.New("wrong number of arguments for 'INCR'")
- return err
- }
-
- key := args[1]
- result := db.Execute(key, func(s *core.Shard)interface{}{
- item, exists := s.Data[key]
- if exists {
- strValue, ok := item.Value.(string)
- if !ok {
- err := errors.New("value is not an integer or out of range")
- return err
- }
-
- currentInt, err := strconv.Atoi(strValue)
- if err != nil {
- err := errors.New("value is not an integer or out of range")
- return err
- }
-
- newValueStr := strconv.Itoa(currentInt + 1)
- s.Data[key] = core.Item{Value: newValueStr}
- return currentInt + 1
- } else {
- s.Data[key] = core.Item{Value: "1"}
- return 1
- }
-
- })
-
- return result
-}
-
-func handleDecr(args []string)interface{} {
-
- if len(args) != 2 {
- err := errors.New("wrong number of arguments for 'DECR'")
- return err
- }
-
- key := args[1]
- result := db.Execute(key, func(s *core.Shard)interface{}{
- item, exists := s.Data[key]
- if exists {
- strValue, ok := item.Value.(string)
- if !ok {
- err := errors.New("value is not an integer or out of range")
- return err
- }
-
- currentInt, err := strconv.Atoi(strValue)
- if err != nil {
- err := errors.New("value is not an integer or out of range")
- return err
- }
-
- newValueStr := strconv.Itoa(currentInt + 1)
- s.Data[key] = core.Item{Value: newValueStr}
- return currentInt - 1
- } else {
- s.Data[key] = core.Item{Value: "1"}
- return -1
- }
-
- })
- return result
-}
-
-func handlePing(conn net.Conn, args []string) {
- conn.Write(core.SerializeRESP(core.SimpleString("OK")))
-}
-
-func handleFlushall(args []string)interface{} {
- if len(args) != 1 {
- err := errors.New("wrong number of arguments for 'FLUSHALL'")
- return err
- }
- result := db.ExecuteAll(func(shards []*core.Shard)interface{}{
- for _, shard := range shards{
- shard.Data = make(map[string]core.Item)
- }
- return core.SimpleString("OK")
- })
- return result
-}
-
-func handleRename(args []string)interface{}{
- if len(args) != 3 {
- err := errors.New("wrong number of arguments for 'RENAME'")
- return err
- }
- keys := args[1:]
-
- result := db.ExecuteMulti(keys,func(shards []*core.Shard)interface{}{
- item, exists := db.GetShard(keys[0]).Data[keys[0]]
- if !exists {
-
- return errors.New("no such key")
- }
-
- db.GetShard(keys[1]).Data[keys[1]] = item
- delete(db.GetShard(keys[0]).Data, keys[0])
-
- return core.SimpleString("OK")
- })
- return result
-}
-
-func handleRpush(args []string)interface{} {
- if len(args) < 3 {
- err := errors.New("wrong number of arguments for 'RPUSH'")
- return err
- }
- key := args[1]
- newItems := args[2:]
-
- result := db.Execute(key, func(s *core.Shard) interface{} {
- var list []string
- existingItem, exists := s.Data[key]
- if exists {
- var ok bool
- list, ok = existingItem.Value.([]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- } else {
- list = []string{}
- }
- list = append(list, newItems...)
- s.Data[key] = core.Item{Value: list}
- return len(list)
- })
- return result
-}
-
-func handleLpush(args []string)interface{} {
- if len(args) < 3 {
- err := errors.New("wrong number of arguments for 'LPUSH'")
- return err
- }
- key := args[1]
- newItems := args[2:]
-
- result := db.Execute(key, func(s *core.Shard) interface{} {
- var list []string
- existingItem, exists := s.Data[key]
- if exists {
- var ok bool
- list, ok = existingItem.Value.([]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- } else {
- list = []string{}
- }
- list = append(newItems, list...)
- s.Data[key] = core.Item{Value: list}
- return len(list)
- })
-
- return result
-}
-
-func handleLrange(args []string)interface{} {
- if len(args) != 4 {
- err := errors.New("wrong number of arguments for 'LRANGE'")
- return err
- }
- start, err1 := strconv.Atoi(args[2])
- stop, err2 := strconv.Atoi(args[3])
- if err1 != nil || err2 != nil {
- err := errors.New("value is not an integer or out of range")
- return err
- }
- key := args[1]
-
- result := db.ExecuteRead(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return []string{}
- }
- if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) {
- delete(s.Data, key)
- return nil
- }
- typedList, ok := item.Value.([]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- size := len(typedList)
- if start < 0 {
- start += size
- if start < 0 {
- start = 0
- }
- }
- if stop < 0 {
- stop += size
- if stop < 0 {
- stop = 0
- }
- }
- if stop >= size {
- stop = size - 1
- }
- if start > stop || start >= size {
- return []string{}
- }
- return typedList[start : stop+1]
- })
- return result
-}
-
-func handleLpop(args []string)interface{} {
- if len(args) != 2 {
- err := errors.New("wrong number of arguments for 'LPOP'")
- return err
- }
- key := args[1]
-
- result := db.Execute(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return nil
- }
- typedList, ok := item.Value.([]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- if len(typedList) == 0 {
- return nil
- }
- newItem := typedList[0]
- newList := typedList[1:]
- if len(newList) <= 0 {
- delete(s.Data, key)
- } else {
- s.Data[key] = core.Item{Value: newList}
- }
- return newItem
- })
- return result
-}
-
-func handleRpop(args []string)interface{} {
- if len(args) != 2 {
- err := errors.New("wrong number of arguments for 'RPOP'")
- return err
- }
- key := args[1]
-
- result := db.Execute(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return nil
- }
- typedList, ok := item.Value.([]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- if len(typedList) == 0 {
- return nil
- }
- newItem := typedList[len(typedList)-1]
- newList := typedList[:len(typedList)-1]
- if len(newList) <= 0 {
- delete(s.Data, key)
- } else {
- s.Data[key] = core.Item{Value: newList}
- }
- return newItem
- })
- return result
-}
-
-func handleExpire(args []string)interface{} {
- if len(args) != 3 {
- return errors.New("wrong number of arguments for 'EXPIRE'")
- }
- key := args[1]
- seconds, err := strconv.Atoi(args[2])
- if err != nil {
- return errors.New("value is not an integer")
- }
-
- result := db.Execute(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return 0
- }
- expiry := time.Now().Add(time.Duration(seconds) * time.Second)
- item.ExpiresAt = &expiry
- s.Data[key] = item
- return 1
- })
- return result
-}
-
-func handleHset(args []string)interface{}{
- if len(args) < 4 || (len(args)-2)%2 != 0 {
- err := errors.New("wrong number of arguments for 'HSET'")
- return err
- }
- key := args[1]
-
- result := db.Execute(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- var hash map[string]string
- if exists {
- var ok bool
- hash, ok = item.Value.(map[string]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- } else {
- hash = make(map[string]string)
- }
- count := 0
- for i := 2; i < len(args); i += 2 {
- hash[args[i]] = args[i+1]
- count++
- }
- s.Data[key] = core.Item{Value: hash}
- return count
- })
- return result
-}
-
-func handleHget(args []string)interface{} {
- if len(args) != 3 {
- return errors.New("wrong number of arguments for 'HGET'")
- }
- key := args[1]
- field := args[2]
-
- result := db.ExecuteRead(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return nil
- }
- hash, ok := item.Value.(map[string]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- val, found := hash[field]
- if !found {
- return nil
- }
- return val
- })
- return result
-}
-
-func handleHgetall(args []string)interface{} {
- if len(args) != 2 {
- return errors.New("wrong number of arguments for 'HGETALL'")
- }
- key := args[1]
-
- result := db.ExecuteRead(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return nil
- }
- hash, ok := item.Value.(map[string]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- return hash
- })
- return result
-}
-
-func handleHkeys(args []string)interface{} {
- if len(args) != 2 {
- return errors.New("wrong number of arguments for 'HKEYS'")
- }
- key := args[1]
-
- result := db.ExecuteRead(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return nil
- }
- hash, ok := item.Value.(map[string]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- keys := make([]string, 0, len(hash))
- for k := range hash {
- keys = append(keys, k)
- }
- return keys
- })
- return result
-}
-
-func handleHvalues(args []string)interface{} {
- if len(args) != 2 {
- return errors.New("wrong number of arguments for 'HVALUES'")
- }
- key := args[1]
-
- result := db.ExecuteRead(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return nil
- }
- hash, ok := item.Value.(map[string]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
- values := make([]string, 0, len(hash))
- for _, v := range hash {
- values = append(values, v)
- }
- return values
- })
- return result
-}
-
-func handleType(args []string)interface{} {
- if len(args) != 2 {
- return errors.New("wrong number of arguments for 'TYPE'")
- }
- key := args[1]
-
- result := db.ExecuteRead(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return core.SimpleString("none")
- }
- switch item.Value.(type) {
- case string:
- return core.SimpleString("string")
- case []string:
- return core.SimpleString("list")
- case map[string]string:
- return core.SimpleString("hash")
- default:
- return core.SimpleString("unknown")
- }
- })
- return result
-}
-
-func handleDbsize(args []string)interface{} {
- if len(args) != 1 {
- return errors.New("wrong number of arguments for 'DBSIZE'")
- }
- result := db.ExecuteReadAll(func(shards []*core.Shard) interface{} {
- count := 0
- for _, shard := range shards {
- count += len(shard.Data)
- }
- return count
- })
- return result
-}
-
-func handleKeys(args []string)interface{} {
- if len(args) < 2 || len(args) > 3 {
- return errors.New("wrong number of arguments for 'KEYS'")
- }
- pattern := args[1]
- limit := -1
- if len(args) == 3 {
- val, err := strconv.Atoi(args[2])
- if err != nil {
- return errors.New("value is not an integer")
- }
- limit = val
- }
-
- result := db.ExecuteReadAll(func(shards []*core.Shard) interface{} {
- var matches []string
- for _, shard := range shards {
- for key := range shard.Data {
- matched, err := filepath.Match(pattern, key)
- if err != nil {
- return errors.New("illegal glob pattern")
- }
- if matched {
- matches = append(matches, key)
- }
- }
- }
- sort.Strings(matches)
- if limit != -1 && len(matches) > limit {
- matches = matches[:limit]
- }
- return matches
- })
- return result
-}
-
-func handleLrem(args []string)interface{} {
- if len(args) != 4 {
- return errors.New("wrong number of arguments for 'LREM'")
- }
- key := args[1]
- count, err := strconv.Atoi(args[2])
- toBeRemoved := args[3]
- if err != nil {
- return errors.New("value is not an integer")
- }
-
- result := db.Execute(key, func(s *core.Shard) interface{} {
- item, exists := s.Data[key]
- if !exists {
- return 0
- }
- list, ok := item.Value.([]string)
- if !ok {
- return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
- }
-
- counter := 0
- absCount := count
- if count < 0 {
- absCount = -count
- }
-
- var newList []string
- if count == 0 {
- newList = []string{}
- for _, value := range list {
- if value == toBeRemoved {
- counter++
- } else {
- newList = append(newList, value)
- }
- }
- } else if count > 0 {
- newList = []string{}
- for _, value := range list {
- if value == toBeRemoved && counter < absCount {
- counter++
- } else {
- newList = append(newList, value)
- }
- }
- } else { // count < 0
- reversedNewList := []string{}
- for i := len(list) - 1; i >= 0; i-- {
- value := list[i]
- if value == toBeRemoved && counter < absCount {
- counter++
- } else {
- reversedNewList = append(reversedNewList, value)
- }
- }
- newList = make([]string, len(reversedNewList))
- for i, v := range reversedNewList {
- newList[len(reversedNewList)-1-i] = v
- }
- }
-
- if len(newList) == 0 {
- delete(s.Data, key)
- } else {
- item.Value = newList
- s.Data[key] = item
- }
- return counter
- })
- return result
-} \ No newline at end of file
diff --git a/main.go b/main.go
index 9c94e6c..9e48eb0 100644
--- a/main.go
+++ b/main.go
@@ -17,38 +17,6 @@ import (
var db core.RedisDB
var hub pubsub.Hub
-type baseHandlerFunc func([]string)interface{}
-type connectionHandlerFunc func(net.Conn, []string)
-
-var baseCommandRegistry = map[string]baseHandlerFunc{
- "GET": handleGet,
- "SET": handleSet,
- "DEL": handleDel,
- "EXISTS": handleExists,
- "INCR": handleIncr,
- "DECR": handleDecr,
- "RENAME": handleRename,
- "FLUSHALL": handleFlushall,
- "RPUSH": handleRpush,
- "LPUSH": handleLpush,
- "LRANGE": handleLrange,
- "LPOP": handleLpop,
- "RPOP": handleRpop,
- "EXPIRE": handleExpire,
- "HSET": handleHset,
- "HGET": handleHget,
- "HGETALL": handleHgetall,
- "HKEYS": handleHkeys,
- "HVALUES": handleHvalues,
- "TYPE": handleType,
- "DBSIZE": handleDbsize,
- "KEYS": handleKeys,
- "LREM": handleLrem,
-}
-var connectionCommandRegistry = map[string]connectionHandlerFunc{
- "PING": handlePing,
-}
-
func main() {
shards := make([]*core.Shard, core.NumShards)
@@ -59,6 +27,7 @@ func main() {
}
}
db = core.RedisDB{Shards: shards}
+ commands.SetDB(db)
hub = pubsub.Hub{Channels: make(map[string]map[net.Conn]struct{}),}
go func() {
diff --git a/pkg/commands/command-registry.go b/pkg/commands/command-registry.go
index baf7133..068fe7c 100644
--- a/pkg/commands/command-registry.go
+++ b/pkg/commands/command-registry.go
@@ -4,6 +4,13 @@ import (
"errors"
"redisClone/pkg/core"
)
+
+var dbInstance core.RedisDB
+
+func SetDB(db core.RedisDB) {
+ dbInstance = db
+}
+
func SingleKey(args []string) []string {
return []string{args[1]}
}
@@ -16,6 +23,10 @@ func NoKeys(args []string) []string {
return nil
}
+func RenameKeys(args []string) []string {
+ return []string{args[1], args[2]}
+}
+
type CommandDef struct{
MinArgs int
ExtractKeys func(args []string)[]string
@@ -72,7 +83,7 @@ var Registry = map[string]CommandDef{
},
"RENAME": {
MinArgs: 3,
- ExtractKeys: SingleKey,
+ ExtractKeys: RenameKeys,
Execute: Rename,
},
"RPUSH": {
@@ -100,4 +111,64 @@ var Registry = map[string]CommandDef{
ExtractKeys: SingleKey,
Execute: Lrange,
},
+ "FLUSHALL": {
+ MinArgs: 1,
+ ExtractKeys: NoKeys,
+ Execute: Flushall,
+ },
+ "EXPIRE": {
+ MinArgs: 3,
+ ExtractKeys: SingleKey,
+ Execute: Expire,
+ },
+ "HSET": {
+ MinArgs: 4,
+ ExtractKeys: SingleKey,
+ Execute: Hset,
+ },
+ "HGET": {
+ MinArgs: 3,
+ ExtractKeys: SingleKey,
+ Execute: Hget,
+ },
+ "HGETALL": {
+ MinArgs: 2,
+ ExtractKeys: SingleKey,
+ Execute: Hgetall,
+ },
+ "HKEYS": {
+ MinArgs: 2,
+ ExtractKeys: SingleKey,
+ Execute: Hkeys,
+ },
+ "HVALUES": {
+ MinArgs: 2,
+ ExtractKeys: SingleKey,
+ Execute: Hvalues,
+ },
+ "TYPE": {
+ MinArgs: 2,
+ ExtractKeys: SingleKey,
+ Execute: Type,
+ },
+ "DBSIZE": {
+ MinArgs: 1,
+ ExtractKeys: NoKeys,
+ Execute: Dbsize,
+ },
+ "KEYS": {
+ MinArgs: 2,
+ ExtractKeys: NoKeys,
+ Execute: Keys,
+ },
+ "LREM": {
+ MinArgs: 4,
+ ExtractKeys: SingleKey,
+ Execute: Lrem,
+ },
+ "PING": {
+ MinArgs: 1,
+ ExtractKeys: NoKeys,
+ Execute: Ping,
+ },
} \ No newline at end of file
diff --git a/pkg/commands/dbsize.go b/pkg/commands/dbsize.go
new file mode 100644
index 0000000..dbe39a4
--- /dev/null
+++ b/pkg/commands/dbsize.go
@@ -0,0 +1,15 @@
+package commands
+
+import (
+ "redisClone/pkg/core"
+)
+
+func Dbsize(args []string, getShard func(k string) *core.Shard) interface{} {
+ return dbInstance.ExecuteReadAll(func(shards []*core.Shard) interface{} {
+ count := 0
+ for _, shard := range shards {
+ count += len(shard.Data)
+ }
+ return count
+ })
+}
diff --git a/pkg/commands/expire.go b/pkg/commands/expire.go
new file mode 100644
index 0000000..4337e83
--- /dev/null
+++ b/pkg/commands/expire.go
@@ -0,0 +1,26 @@
+package commands
+
+import (
+ "errors"
+ "redisClone/pkg/core"
+ "strconv"
+ "time"
+)
+
+func Expire(args []string, getShard func(k string) *core.Shard) interface{} {
+ key := args[1]
+ seconds, err := strconv.Atoi(args[2])
+ if err != nil {
+ return errors.New("value is not an integer")
+ }
+
+ s := getShard(key)
+ item, exists := s.Data[key]
+ if !exists {
+ return 0
+ }
+ expiry := time.Now().Add(time.Duration(seconds) * time.Second)
+ item.ExpiresAt = &expiry
+ s.Data[key] = item
+ return 1
+}
diff --git a/pkg/commands/flushall.go b/pkg/commands/flushall.go
new file mode 100644
index 0000000..9f1316a
--- /dev/null
+++ b/pkg/commands/flushall.go
@@ -0,0 +1,14 @@
+package commands
+
+import (
+ "redisClone/pkg/core"
+)
+
+func Flushall(args []string, getShard func(k string) *core.Shard) interface{} {
+ return dbInstance.ExecuteAll(func(shards []*core.Shard) interface{} {
+ for _, shard := range shards {
+ shard.Data = make(map[string]core.Item)
+ }
+ return core.SimpleString("OK")
+ })
+}
diff --git a/pkg/commands/hget.go b/pkg/commands/hget.go
new file mode 100644
index 0000000..7d16808
--- /dev/null
+++ b/pkg/commands/hget.go
@@ -0,0 +1,25 @@
+package commands
+
+import (
+ "errors"
+ "redisClone/pkg/core"
+)
+
+func Hget(args []string, getShard func(k string) *core.Shard) interface{} {
+ key := args[1]
+ field := args[2]
+ s := getShard(key)
+ item, exists := s.Data[key]
+ if !exists {
+ return nil
+ }
+ hash, ok := item.Value.(map[string]string)
+ if !ok {
+ return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
+ }
+ val, found := hash[field]
+ if !found {
+ return nil
+ }
+ return val
+}
diff --git a/pkg/commands/hgetall.go b/pkg/commands/hgetall.go
new file mode 100644
index 0000000..02b3e3f
--- /dev/null
+++ b/pkg/commands/hgetall.go
@@ -0,0 +1,20 @@
+package commands
+
+import (
+ "errors"
+ "redisClone/pkg/core"
+)
+
+func Hgetall(args []string, getShard func(k string) *core.Shard) interface{} {
+ key := args[1]
+ s := getShard(key)
+ item, exists := s.Data[key]
+ if !exists {
+ return nil
+ }
+ hash, ok := item.Value.(map[string]string)
+ if !ok {
+ return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
+ }
+ return hash
+}
diff --git a/pkg/commands/hkeys.go b/pkg/commands/hkeys.go
new file mode 100644
index 0000000..09a5d97
--- /dev/null
+++ b/pkg/commands/hkeys.go
@@ -0,0 +1,24 @@
+package commands
+
+import (
+ "errors"
+ "redisClone/pkg/core"
+)
+
+func Hkeys(args []string, getShard func(k string) *core.Shard) interface{} {
+ key := args[1]
+ s := getShard(key)
+ item, exists := s.Data[key]
+ if !exists {
+ return nil
+ }
+ hash, ok := item.Value.(map[string]string)
+ if !ok {
+ return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
+ }
+ keys := make([]string, 0, len(hash))
+ for k := range hash {
+ keys = append(keys, k)
+ }
+ return keys
+}
diff --git a/pkg/commands/hset.go b/pkg/commands/hset.go
new file mode 100644
index 0000000..33f992f
--- /dev/null
+++ b/pkg/commands/hset.go
@@ -0,0 +1,32 @@
+package commands
+
+import (
+ "errors"
+ "redisClone/pkg/core"
+)
+
+func Hset(args []string, getShard func(k string) *core.Shard) interface{} {
+ if (len(args)-2)%2 != 0 {
+ return errors.New("wrong number of arguments for 'HSET'")
+ }
+ key := args[1]
+ s := getShard(key)
+ item, exists := s.Data[key]
+ var hash map[string]string
+ if exists {
+ var ok bool
+ hash, ok = item.Value.(map[string]string)
+ if !ok {
+ return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
+ }
+ } else {
+ hash = make(map[string]string)
+ }
+ count := 0
+ for i := 2; i < len(args); i += 2 {
+ hash[args[i]] = args[i+1]
+ count++
+ }
+ s.Data[key] = core.Item{Value: hash}
+ return count
+}
diff --git a/pkg/commands/hvalues.go b/pkg/commands/hvalues.go
new file mode 100644
index 0000000..1034fbd
--- /dev/null
+++ b/pkg/commands/hvalues.go
@@ -0,0 +1,24 @@
+package commands
+
+import (
+ "errors"
+ "redisClone/pkg/core"
+)
+
+func Hvalues(args []string, getShard func(k string) *core.Shard) interface{} {
+ key := args[1]
+ s := getShard(key)
+ item, exists := s.Data[key]
+ if !exists {
+ return nil
+ }
+ hash, ok := item.Value.(map[string]string)
+ if !ok {
+ return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
+ }
+ values := make([]string, 0, len(hash))
+ for _, v := range hash {
+ values = append(values, v)
+ }
+ return values
+}
diff --git a/pkg/commands/keys.go b/pkg/commands/keys.go
new file mode 100644
index 0000000..b233964
--- /dev/null
+++ b/pkg/commands/keys.go
@@ -0,0 +1,41 @@
+package commands
+
+import (
+ "errors"
+ "path/filepath"
+ "redisClone/pkg/core"
+ "sort"
+ "strconv"
+)
+
+func Keys(args []string, getShard func(k string) *core.Shard) interface{} {
+ pattern := args[1]
+ limit := -1
+ if len(args) == 3 {
+ val, err := strconv.Atoi(args[2])
+ if err != nil {
+ return errors.New("value is not an integer")
+ }
+ limit = val
+ }
+
+ return dbInstance.ExecuteReadAll(func(shards []*core.Shard) interface{} {
+ var matches []string
+ for _, shard := range shards {
+ for key := range shard.Data {
+ matched, err := filepath.Match(pattern, key)
+ if err != nil {
+ return errors.New("illegal glob pattern")
+ }
+ if matched {
+ matches = append(matches, key)
+ }
+ }
+ }
+ sort.Strings(matches)
+ if limit != -1 && len(matches) > limit {
+ matches = matches[:limit]
+ }
+ return matches
+ })
+}
diff --git a/pkg/commands/lrem.go b/pkg/commands/lrem.go
new file mode 100644
index 0000000..38be270
--- /dev/null
+++ b/pkg/commands/lrem.go
@@ -0,0 +1,75 @@
+package commands
+
+import (
+ "errors"
+ "redisClone/pkg/core"
+ "strconv"
+)
+
+func Lrem(args []string, getShard func(k string) *core.Shard) interface{} {
+ key := args[1]
+ count, err := strconv.Atoi(args[2])
+ toBeRemoved := args[3]
+ if err != nil {
+ return errors.New("value is not an integer")
+ }
+
+ s := getShard(key)
+ item, exists := s.Data[key]
+ if !exists {
+ return 0
+ }
+ list, ok := item.Value.([]string)
+ if !ok {
+ return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value")
+ }
+
+ counter := 0
+ absCount := count
+ if count < 0 {
+ absCount = -count
+ }
+
+ var newList []string
+ if count == 0 {
+ newList = []string{}
+ for _, value := range list {
+ if value == toBeRemoved {
+ counter++
+ } else {
+ newList = append(newList, value)
+ }
+ }
+ } else if count > 0 {
+ newList = []string{}
+ for _, value := range list {
+ if value == toBeRemoved && counter < absCount {
+ counter++
+ } else {
+ newList = append(newList, value)
+ }
+ }
+ } else { // count < 0
+ reversedNewList := []string{}
+ for i := len(list) - 1; i >= 0; i-- {
+ value := list[i]
+ if value == toBeRemoved && counter < absCount {
+ counter++
+ } else {
+ reversedNewList = append(reversedNewList, value)
+ }
+ }
+ newList = make([]string, len(reversedNewList))
+ for i, v := range reversedNewList {
+ newList[len(reversedNewList)-1-i] = v
+ }
+ }
+
+ if len(newList) == 0 {
+ delete(s.Data, key)
+ } else {
+ item.Value = newList
+ s.Data[key] = item
+ }
+ return counter
+}
diff --git a/pkg/commands/ping.go b/pkg/commands/ping.go
new file mode 100644
index 0000000..b56cd20
--- /dev/null
+++ b/pkg/commands/ping.go
@@ -0,0 +1,16 @@
+package commands
+
+import (
+ "errors"
+ "redisClone/pkg/core"
+)
+
+func Ping(args []string, getShard func(k string) *core.Shard) interface{} {
+ if len(args) == 1 {
+ return core.SimpleString("PONG")
+ }
+ if len(args) == 2 {
+ return args[1]