basic list

This commit is contained in:
hdt3213
2019-07-15 21:55:52 +08:00
committed by wyb
parent 676797cc04
commit 0b24abf9f5
16 changed files with 873 additions and 25 deletions

View File

@@ -14,6 +14,11 @@ type Dict struct {
rehashIndex int32
}
type Shard struct {
head *Node
mutex sync.RWMutex
}
type Node struct {
key string
val interface{}
@@ -21,10 +26,6 @@ type Node struct {
hashCode uint32
}
type Shard struct {
head *Node
mutex sync.RWMutex
}
const (
maxCapacity = 1 << 15

View File

@@ -1,5 +1,7 @@
package list
import "github.com/HDT3213/godis/src/datastruct/utils"
type LinkedList struct {
first *node
last *node
@@ -31,7 +33,7 @@ func (list *LinkedList)Add(val interface{}) {
list.size++
}
func (list *LinkedList)find(index int)(val *node) {
func (list *LinkedList)find(index int)(n *node) {
if index < list.size / 2 {
n := list.first
for i := 0; i < index; i++ {
@@ -57,16 +59,28 @@ func (list *LinkedList)Get(index int)(val interface{}) {
return list.find(index).val
}
func (list *LinkedList)Set(index int, val interface{}) {
if list == nil {
panic("list is nil")
}
if index < 0 || index > list.size {
panic("index out of bound")
}
n := list.find(index)
n.val = val
}
func (list *LinkedList)Insert(index int, val interface{}) {
if list == nil {
panic("list is nil")
}
if index < 0 || index >= list.size {
if index < 0 || index > list.size {
panic("index out of bound")
}
if index == list.size {
list.Add(val)
return
} else {
// list is not empty
pivot := list.find(index)
@@ -81,19 +95,11 @@ func (list *LinkedList)Insert(index int, val interface{}) {
pivot.prev.next = n
}
pivot.prev = n
}
list.size++
}
func (list *LinkedList)Remove(index int) {
if list == nil {
panic("list is nil")
}
if index < 0 || index >= list.size {
panic("index out of bound")
}
n := list.find(index)
func (list *LinkedList)removeNode(n *node) {
if n.prev == nil {
list.first = n.next
} else {
@@ -108,11 +114,132 @@ func (list *LinkedList)Remove(index int) {
// for gc
n.prev = nil
n.next = nil
n.val = nil
list.size--
}
func (list *LinkedList)Remove(index int)(val interface{}) {
if list == nil {
panic("list is nil")
}
if index < 0 || index >= list.size {
panic("index out of bound")
}
n := list.find(index)
list.removeNode(n)
return n.val
}
func (list *LinkedList)RemoveLast()(val interface{}) {
if list == nil {
panic("list is nil")
}
if list.last == nil {
// empty list
return nil
}
n := list.last
list.removeNode(n)
return n.val
}
func (list *LinkedList)RemoveAllByVal(val interface{})int {
if list == nil {
panic("list is nil")
}
n := list.first
removed := 0
for n != nil {
var toRemoveNode *node
if utils.Equals(n.val, val) {
toRemoveNode = n
}
if n.next == nil {
if toRemoveNode != nil {
removed++
list.removeNode(toRemoveNode)
}
break
} else {
n = n.next
}
if toRemoveNode != nil {
removed++
list.removeNode(toRemoveNode)
}
}
return removed
}
/**
* remove at most `count` values of the specified value in this list
* scan from left to right
*/
func (list *LinkedList) RemoveByVal(val interface{}, count int)int {
if list == nil {
panic("list is nil")
}
n := list.first
removed := 0
for n != nil {
var toRemoveNode *node
if utils.Equals(n.val, val) {
toRemoveNode = n
}
if n.next == nil {
if toRemoveNode != nil {
removed++
list.removeNode(toRemoveNode)
}
break
} else {
n = n.next
}
if toRemoveNode != nil {
removed++
list.removeNode(toRemoveNode)
}
if removed == count {
break
}
}
return removed
}
func (list *LinkedList) ReverseRemoveByVal(val interface{}, count int)int {
if list == nil {
panic("list is nil")
}
n := list.last
removed := 0
for n != nil {
var toRemoveNode *node
if utils.Equals(n.val, val) {
toRemoveNode = n
}
if n.prev == nil {
if toRemoveNode != nil {
removed++
list.removeNode(toRemoveNode)
}
break
} else {
n = n.prev
}
if toRemoveNode != nil {
removed++
list.removeNode(toRemoveNode)
}
if removed == count {
break
}
}
return removed
}
func (list *LinkedList)Len()int {
if list == nil {
panic("list is nil")
@@ -137,7 +264,48 @@ func (list *LinkedList)ForEach(consumer func(int, interface{})bool) {
}
}
func Make(vals ...interface{})(*LinkedList) {
func (list *LinkedList)Range(start int, stop int)[]interface{} {
if list == nil {
panic("list is nil")
}
if start < 0 || start >= list.size {
panic("`start` out of range")
}
if stop < start || stop > list.size {
panic("`stop` out of range")
}
sliceSize := stop - start
slice := make([]interface{}, sliceSize)
n := list.first
i := 0
sliceIndex := 0
for n != nil {
if i >= start && i < stop {
slice[sliceIndex] = n.val
sliceIndex++
} else if i >= stop {
break
}
if n.next == nil {
break
} else {
i++
n = n.next
}
}
return slice
}
func Make(vals ...interface{}) *LinkedList {
list := LinkedList{}
for _, v := range vals {
list.Add(v)
}
return &list
}
func MakeBytesList(vals ...[]byte) *LinkedList {
list := LinkedList{}
for _, v := range vals {
list.Add(v)

View File

@@ -64,6 +64,69 @@ func TestRemove(t *testing.T) {
}
}
func TestRemoveVal(t *testing.T) {
list := Make()
for i := 0; i < 10; i++ {
list.Add(i)
list.Add(i)
}
for index := 0; index < list.Len(); index++ {
list.RemoveAllByVal(index)
list.ForEach(func(i int, v interface{}) bool {
intVal, _ := v.(int)
if intVal == index {
t.Error("remove test fail: found " + strconv.Itoa(index) + " at index: " + strconv.Itoa(i))
}
return true
})
}
list = Make()
for i := 0; i < 10; i++ {
list.Add(i)
list.Add(i)
}
for i := 0; i < 10; i++ {
list.RemoveByVal(i, 1)
}
list.ForEach(func(i int, v interface{}) bool {
intVal, _ := v.(int)
if intVal != i {
t.Error("test fail: expected " + strconv.Itoa(i) + ", actual: " + strconv.Itoa(intVal))
}
return true
})
for i := 0; i < 10; i++ {
list.RemoveByVal(i, 1)
}
if list.Len() != 0 {
t.Error("test fail: expected 0, actual: " + strconv.Itoa(list.Len()))
}
list = Make()
for i := 0; i < 10; i++ {
list.Add(i)
list.Add(i)
}
for i := 0; i < 10; i++ {
list.ReverseRemoveByVal(i, 1)
}
list.ForEach(func(i int, v interface{}) bool {
intVal, _ := v.(int)
if intVal != i {
t.Error("test fail: expected " + strconv.Itoa(i) + ", actual: " + strconv.Itoa(intVal))
}
return true
})
for i := 0; i < 10; i++ {
list.ReverseRemoveByVal(i, 1)
}
if list.Len() != 0 {
t.Error("test fail: expected 0, actual: " + strconv.Itoa(list.Len()))
}
}
func TestInsert(t *testing.T) {
list := Make()
for i := 0; i < 10; i++ {
@@ -109,3 +172,44 @@ func TestInsert(t *testing.T) {
}
}
func TestRemoveLast(t *testing.T) {
list := Make()
for i := 0; i < 10; i++ {
list.Add(i)
}
for i := 9; i >= 0; i-- {
val := list.RemoveLast()
intVal, _ := val.(int)
if intVal != i {
t.Error("add test fail: expected " + strconv.Itoa(i) + ", actual: " + strconv.Itoa(intVal))
}
}
}
func TestRange(t *testing.T) {
list := Make()
size := 10
for i := 0; i < size; i++ {
list.Add(i)
}
for start := 0; start < size; start++ {
for stop := start; stop < size; stop++ {
slice := list.Range(start, stop)
if len(slice) != stop - start {
t.Error("expected " + strconv.Itoa(stop - start) + ", get: " + strconv.Itoa(len(slice)) +
", range: [" + strconv.Itoa(start) + "," + strconv.Itoa(stop) + "]")
}
sliceIndex := 0
for i := start; i < stop; i++ {
val := slice[sliceIndex]
intVal, _ := val.(int)
if intVal != i {
t.Error("expected " + strconv.Itoa(i) + ", get: " + strconv.Itoa(intVal) +
", range: [" + strconv.Itoa(start) + "," + strconv.Itoa(stop) + "]")
}
sliceIndex++
}
}
}
}

View File

@@ -0,0 +1,28 @@
package utils
func Equals(a interface{}, b interface{})bool {
sliceA, okA := a.([]byte)
sliceB, okB := b.([]byte)
if okA && okB {
return sliceEquals(sliceA, sliceB)
}
return a == b
}
func sliceEquals(a []byte, b []byte)bool {
if (a == nil && b != nil) || (a != nil && b == nil) {
return false
}
if len(a) != len(b) {
return false
}
size := len(a)
for i := 0; i < size; i++ {
av := a[i]
bv := b[i]
if av != bv {
return false
}
}
return true
}

View File

@@ -1,13 +1,14 @@
package db
import (
"strings"
"github.com/HDT3213/godis/src/redis/reply"
"fmt"
"runtime/debug"
"github.com/HDT3213/godis/src/lib/logger"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/datastruct/dict"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/lib/logger"
"github.com/HDT3213/godis/src/redis/reply"
"runtime/debug"
"strings"
"sync"
)
const (
@@ -22,6 +23,7 @@ type DataEntity struct {
Code uint8
TTL int64 // ttl in seconds, 0 for unlimited ttl
Data interface{}
sync.RWMutex
}
// args don't include cmd line
@@ -44,6 +46,19 @@ func MakeCmdMap()map[string]CmdFunc {
cmdMap["get"] = Get
cmdMap["lpush"] = LPush
cmdMap["lpushx"] = LPushX
cmdMap["rpush"] = RPush
cmdMap["rpushx"] = RPushX
cmdMap["lpop"] = LPop
cmdMap["rpop"] = RPop
cmdMap["rpoplpush"] = RPopLPush
cmdMap["lrem"] = LRem
cmdMap["llen"] = LLen
cmdMap["lindex"] = LIndex
cmdMap["lset"] = LSet
cmdMap["lrange"] = LRange
return cmdMap
}

49
src/db/lindex.go Normal file
View File

@@ -0,0 +1,49 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
"strconv"
)
func LIndex(db *DB, args [][]byte)redis.Reply {
// parse args
if len(args) != 2 {
return reply.MakeErrReply("ERR wrong number of arguments for 'lindex' command")
}
key := string(args[0])
index64, err := strconv.ParseInt(string(args[1]), 10, 64)
if err != nil {
return reply.MakeErrReply("ERR value is not an integer or out of range")
}
index := int(index64)
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return &reply.NullBulkReply{}
} else {
entity, _ = rawEntity.(*DataEntity)
}
entity.RLock()
defer entity.RUnlock()
// check type
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
list, _ := entity.Data.(*List.LinkedList)
size := list.Len() // assert: size > 0
if index < -1 * size {
return &reply.NullBulkReply{}
} else if index < 0 {
index = size + index
} else if index >= size {
return &reply.NullBulkReply{}
}
val, _ := list.Get(index).([]byte)
return reply.MakeBulkReply(val)
}

34
src/db/llen.go Normal file
View File

@@ -0,0 +1,34 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
)
func LLen(db *DB, args [][]byte)redis.Reply {
// parse args
if len(args) != 1 {
return reply.MakeErrReply("ERR wrong number of arguments for 'llen' command")
}
key := string(args[0])
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return reply.MakeIntReply(0)
} else {
entity, _ = rawEntity.(*DataEntity)
}
entity.RLock()
defer entity.RUnlock()
// check type
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
list, _ := entity.Data.(*List.LinkedList)
size := int64(list.Len())
return reply.MakeIntReply(size)
}

38
src/db/lpop.go Normal file
View File

@@ -0,0 +1,38 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
)
func LPop(db *DB, args [][]byte)redis.Reply {
// parse args
if len(args) != 1 {
return reply.MakeErrReply("ERR wrong number of arguments for 'lindex' command")
}
key := string(args[0])
// get data
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return &reply.NullBulkReply{}
} else {
entity, _ = rawEntity.(*DataEntity)
}
entity.Lock()
defer entity.Unlock()
// check type
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
list, _ := entity.Data.(*List.LinkedList)
val, _ := list.Remove(0).([]byte)
if list.Len() == 0 {
db.Data.Remove(key)
}
return reply.MakeBulkReply(val)
}

73
src/db/lpush.go Normal file
View File

@@ -0,0 +1,73 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
)
func LPush(db *DB, args [][]byte)redis.Reply {
if len(args) < 2 {
return reply.MakeErrReply("ERR wrong number of arguments for 'lpush' command")
}
key := string(args[0])
values := args[1:]
// get or init entity
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
entity = &DataEntity{
Code: ListCode,
Data: &List.LinkedList{},
}
} else {
entity, _ = rawEntity.(*DataEntity)
}
entity.Lock()
defer entity.Unlock()
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
// insert
list, _ := entity.Data.(*List.LinkedList)
for _, value := range values {
list.Insert(0, value)
}
db.Data.Put(key, entity)
return reply.MakeIntReply(int64(list.Len()))
}
func LPushX(db *DB, args [][]byte)redis.Reply {
if len(args) < 2 {
return reply.MakeErrReply("ERR wrong number of arguments for 'lpush' command")
}
key := string(args[0])
values := args[1:]
// get or init entity
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return reply.MakeIntReply(0)
} else {
entity, _ = rawEntity.(*DataEntity)
}
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
entity.Lock()
defer entity.Unlock()
// insert
list, _ := entity.Data.(*List.LinkedList)
for _, value := range values {
list.Insert(0, value)
}
db.Data.Put(key, entity)
return reply.MakeIntReply(int64(list.Len()))
}

72
src/db/lrange.go Normal file
View File

@@ -0,0 +1,72 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
"strconv"
)
func LRange(db *DB, args [][]byte)redis.Reply {
// parse args
if len(args) != 3 {
return reply.MakeErrReply("ERR wrong number of arguments for 'lrange' command")
}
key := string(args[0])
start64, err := strconv.ParseInt(string(args[1]), 10, 64)
if err != nil {
return reply.MakeErrReply("ERR value is not an integer or out of range")
}
start := int(start64)
stop64, err := strconv.ParseInt(string(args[2]), 10, 64)
if err != nil {
return reply.MakeErrReply("ERR value is not an integer or out of range")
}
stop := int(stop64)
// get data
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return &reply.EmptyMultiBulkReply{}
} else {
entity, _ = rawEntity.(*DataEntity)
}
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
entity.RLock()
defer entity.RUnlock()
// compute index
list, _ := entity.Data.(*List.LinkedList)
size := list.Len() // assert: size > 0
if start < -1 * size {
start = 0
} else if start < 0 {
start = size + start
} else if start >= size {
return &reply.EmptyMultiBulkReply{}
}
if stop < -1 * size {
stop = 0
} else if stop < 0 {
stop = size + stop + 1
} else if stop < size {
stop = stop + 1
} else {
stop = size
}
if stop < start {
stop = start
}
// assert: start in [0, size - 1], stop in [start, size]
slice := list.Range(start, stop)
result := make([][]byte, len(slice))
for i, raw := range slice {
bytes, _ := raw.([]byte)
result[i] = bytes
}
return reply.MakeMultiBulkReply(result)
}

52
src/db/lrem.go Normal file
View File

@@ -0,0 +1,52 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
"strconv"
)
func LRem(db *DB, args [][]byte)redis.Reply {
// parse args
if len(args) != 3 {
return reply.MakeErrReply("ERR wrong number of arguments for 'lrem' command")
}
key := string(args[0])
count64, err := strconv.ParseInt(string(args[1]), 10, 64)
if err != nil {
return reply.MakeErrReply("ERR value is not an integer or out of range")
}
count := int(count64)
value := args[2]
// get data entity
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return reply.MakeIntReply(0)
} else {
entity, _ = rawEntity.(*DataEntity)
}
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
entity.Lock()
defer entity.Unlock()
list, _ := entity.Data.(*List.LinkedList)
var removed int
if count == 0 {
removed = list.RemoveAllByVal(value)
} else if count > 0 {
removed = list.RemoveByVal(value, count)
} else {
removed = list.ReverseRemoveByVal(value, -count)
}
if list.Len() == 0 {
db.Data.Remove(key)
}
return reply.MakeIntReply(int64(removed))
}

49
src/db/lset.go Normal file
View File

@@ -0,0 +1,49 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
"strconv"
)
func LSet(db *DB, args [][]byte)redis.Reply {
// parse args
if len(args) != 3 {
return reply.MakeErrReply("ERR wrong number of arguments for 'lset' command")
}
key := string(args[0])
index64, err := strconv.ParseInt(string(args[1]), 10, 64)
if err != nil {
return reply.MakeErrReply("ERR value is not an integer or out of range")
}
index := int(index64)
value := args[2]
// get data
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return reply.MakeErrReply("ERR no such key")
} else {
entity, _ = rawEntity.(*DataEntity)
}
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
entity.Lock()
defer entity.Unlock()
list, _ := entity.Data.(*List.LinkedList)
size := list.Len() // assert: size > 0
if index < -1 * size {
return reply.MakeErrReply("ERR index out of range")
} else if index < 0 {
index = size + index
} else if index >= size {
return reply.MakeErrReply("ERR index out of range")
}
list.Set(index, value)
return &reply.OkReply{}
}

36
src/db/rpop.go Normal file
View File

@@ -0,0 +1,36 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
)
func RPop(db *DB, args [][]byte)redis.Reply {
// parse args
if len(args) != 1 {
return reply.MakeErrReply("ERR wrong number of arguments for 'lindex' command")
}
key := string(args[0])
// get data
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return &reply.NullBulkReply{}
} else {
entity, _ = rawEntity.(*DataEntity)
}
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
entity.Lock()
defer entity.Unlock()
list, _ := entity.Data.(*List.LinkedList)
val, _ := list.RemoveLast().([]byte)
if list.Len() == 0 {
db.Data.Remove(key)
}
return reply.MakeBulkReply(val)
}

49
src/db/rpoplpush.go Normal file
View File

@@ -0,0 +1,49 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
)
func RPopLPush(db *DB, args [][]byte)redis.Reply {
if len(args) != 2 {
return reply.MakeErrReply("ERR wrong number of arguments for 'rpoplpush' command")
}
sourceKey := string(args[0])
destKey := string(args[1])
// get source entity
rawEntity, exists := db.Data.Get(sourceKey)
var sourceEntity *DataEntity
if !exists {
return &reply.NullBulkReply{}
} else {
sourceEntity, _ = rawEntity.(*DataEntity)
}
sourceList, _ := sourceEntity.Data.(*List.LinkedList)
sourceEntity.Lock()
defer sourceEntity.Unlock()
// get dest entity
rawEntity, exists = db.Data.Get(destKey)
var destEntity *DataEntity
if !exists {
destEntity = &DataEntity{
Code: ListCode,
Data: &List.LinkedList{},
}
db.Data.Put(destKey, destEntity)
} else {
destEntity, _ = rawEntity.(*DataEntity)
}
destList, _ := destEntity.Data.(*List.LinkedList)
destEntity.Lock()
defer destEntity.Unlock()
// pop and push
val, _ := sourceList.RemoveLast().([]byte)
destList.Insert(0, val)
return reply.MakeBulkReply(val)
}

72
src/db/rpush.go Normal file
View File

@@ -0,0 +1,72 @@
package db
import (
List "github.com/HDT3213/godis/src/datastruct/list"
"github.com/HDT3213/godis/src/interface/redis"
"github.com/HDT3213/godis/src/redis/reply"
)
func RPush(db *DB, args [][]byte)redis.Reply {
if len(args) < 2 {
return reply.MakeErrReply("ERR wrong number of arguments for 'rpush' command")
}
key := string(args[0])
values := args[1:]
// get or init entity
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
entity = &DataEntity{
Code: ListCode,
Data: &List.LinkedList{},
}
} else {
entity, _ = rawEntity.(*DataEntity)
}
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
entity.Lock()
defer entity.Unlock()
// put list
list, _ := entity.Data.(*List.LinkedList)
for _, value := range values {
list.Add(value)
}
db.Data.Put(key, entity)
return reply.MakeIntReply(int64(list.Len()))
}
func RPushX(db *DB, args [][]byte)redis.Reply {
if len(args) < 2 {
return reply.MakeErrReply("ERR wrong number of arguments for 'rpush' command")
}
key := string(args[0])
values := args[1:]
// get or init entity
rawEntity, exists := db.Data.Get(key)
var entity *DataEntity
if !exists {
return reply.MakeIntReply(0)
} else {
entity, _ = rawEntity.(*DataEntity)
}
if entity.Code != ListCode {
return &reply.WrongTypeErrReply{}
}
entity.Lock()
defer entity.Unlock()
// put list
list, _ := entity.Data.(*List.LinkedList)
for _, value := range values {
list.Add(value)
}
db.Data.Put(key, entity)
return reply.MakeIntReply(int64(list.Len()))
}

View File

@@ -10,10 +10,10 @@ func (r *PongReply)ToBytes()[]byte {
type OkReply struct {}
var OkBytes = []byte("+OK\r\n")
var okBytes = []byte("+OK\r\n")
func (r *OkReply)ToBytes()[]byte {
return OkBytes
return okBytes
}
var nullBulkBytes = []byte("$-1\r\n")
@@ -23,3 +23,11 @@ type NullBulkReply struct {}
func (r *NullBulkReply)ToBytes()[]byte {
return nullBulkBytes
}
var emptyMultiBulkBytes = []byte("*0\r\n")
type EmptyMultiBulkReply struct {}
func (r *EmptyMultiBulkReply)ToBytes()[]byte {
return emptyMultiBulkBytes
}