mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-05 08:46:56 +08:00
200 lines
4.0 KiB
Go
200 lines
4.0 KiB
Go
package protocol
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"strconv"
|
|
|
|
"github.com/hdt3213/godis/interface/redis"
|
|
)
|
|
|
|
var (
|
|
|
|
// CRLF is the line separator of redis serialization protocol
|
|
CRLF = "\r\n"
|
|
)
|
|
|
|
/* ---- Bulk Reply ---- */
|
|
|
|
// BulkReply stores a binary-safe string
|
|
type BulkReply struct {
|
|
Arg []byte
|
|
}
|
|
|
|
// MakeBulkReply creates BulkReply
|
|
func MakeBulkReply(arg []byte) *BulkReply {
|
|
return &BulkReply{
|
|
Arg: arg,
|
|
}
|
|
}
|
|
|
|
// ToBytes marshal redis.Reply
|
|
func (r *BulkReply) ToBytes() []byte {
|
|
if r.Arg == nil {
|
|
return nullBulkBytes
|
|
}
|
|
return []byte("$" + strconv.Itoa(len(r.Arg)) + CRLF + string(r.Arg) + CRLF)
|
|
}
|
|
|
|
/* ---- Multi Bulk Reply ---- */
|
|
|
|
// MultiBulkReply stores a list of string
|
|
type MultiBulkReply struct {
|
|
Args [][]byte
|
|
}
|
|
|
|
// MakeMultiBulkReply creates MultiBulkReply
|
|
func MakeMultiBulkReply(args [][]byte) *MultiBulkReply {
|
|
return &MultiBulkReply{
|
|
Args: args,
|
|
}
|
|
}
|
|
|
|
// ToBytes marshal redis.Reply
|
|
func (r *MultiBulkReply) ToBytes() []byte {
|
|
var buf bytes.Buffer
|
|
//Calculate the length of buffer
|
|
argLen := len(r.Args)
|
|
bufLen := 1 + len(strconv.Itoa(argLen)) + 2
|
|
for _, arg := range r.Args {
|
|
if arg == nil {
|
|
bufLen += 3 + 2
|
|
} else {
|
|
bufLen += 1 + len(strconv.Itoa(len(arg))) + 2 + len(arg) + 2
|
|
}
|
|
}
|
|
//Allocate memory
|
|
buf.Grow(bufLen)
|
|
//Write string step by step,avoid concat strings
|
|
buf.WriteString("*")
|
|
buf.WriteString(strconv.Itoa(argLen))
|
|
buf.WriteString(CRLF)
|
|
for _, arg := range r.Args {
|
|
if arg == nil {
|
|
buf.WriteString("$-1")
|
|
buf.WriteString(CRLF)
|
|
} else {
|
|
buf.WriteString("$")
|
|
buf.WriteString(strconv.Itoa(len(arg)))
|
|
buf.WriteString(CRLF)
|
|
//Write bytes,avoid slice of byte to string(slicebytetostring)
|
|
buf.Write(arg)
|
|
buf.WriteString(CRLF)
|
|
}
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
/* ---- Multi Raw Reply ---- */
|
|
|
|
// MultiRawReply store complex list structure, for example GeoPos command
|
|
type MultiRawReply struct {
|
|
Replies []redis.Reply
|
|
}
|
|
|
|
// MakeMultiRawReply creates MultiRawReply
|
|
func MakeMultiRawReply(replies []redis.Reply) *MultiRawReply {
|
|
return &MultiRawReply{
|
|
Replies: replies,
|
|
}
|
|
}
|
|
|
|
// ToBytes marshal redis.Reply
|
|
func (r *MultiRawReply) ToBytes() []byte {
|
|
argLen := len(r.Replies)
|
|
var buf bytes.Buffer
|
|
buf.WriteString("*" + strconv.Itoa(argLen) + CRLF)
|
|
for _, arg := range r.Replies {
|
|
buf.Write(arg.ToBytes())
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
/* ---- Status Reply ---- */
|
|
|
|
// StatusReply stores a simple status string
|
|
type StatusReply struct {
|
|
Status string
|
|
}
|
|
|
|
// MakeStatusReply creates StatusReply
|
|
func MakeStatusReply(status string) *StatusReply {
|
|
return &StatusReply{
|
|
Status: status,
|
|
}
|
|
}
|
|
|
|
// ToBytes marshal redis.Reply
|
|
func (r *StatusReply) ToBytes() []byte {
|
|
return []byte("+" + r.Status + CRLF)
|
|
}
|
|
|
|
// IsOKReply returns true if the given protocol is +OK
|
|
func IsOKReply(reply redis.Reply) bool {
|
|
return string(reply.ToBytes()) == "+OK\r\n"
|
|
}
|
|
|
|
/* ---- Int Reply ---- */
|
|
|
|
// IntReply stores an int64 number
|
|
type IntReply struct {
|
|
Code int64
|
|
}
|
|
|
|
// MakeIntReply creates int protocol
|
|
func MakeIntReply(code int64) *IntReply {
|
|
return &IntReply{
|
|
Code: code,
|
|
}
|
|
}
|
|
|
|
// ToBytes marshal redis.Reply
|
|
func (r *IntReply) ToBytes() []byte {
|
|
return []byte(":" + strconv.FormatInt(r.Code, 10) + CRLF)
|
|
}
|
|
|
|
/* ---- Error Reply ---- */
|
|
|
|
// ErrorReply is an error and redis.Reply
|
|
type ErrorReply interface {
|
|
Error() string
|
|
ToBytes() []byte
|
|
}
|
|
|
|
// StandardErrReply represents server error
|
|
type StandardErrReply struct {
|
|
Status string
|
|
}
|
|
|
|
// MakeErrReply creates StandardErrReply
|
|
func MakeErrReply(status string) *StandardErrReply {
|
|
return &StandardErrReply{
|
|
Status: status,
|
|
}
|
|
}
|
|
|
|
// IsErrorReply returns true if the given protocol is error
|
|
func IsErrorReply(reply redis.Reply) bool {
|
|
return reply.ToBytes()[0] == '-'
|
|
}
|
|
|
|
func Try2ErrorReply(reply redis.Reply) error {
|
|
str := string(reply.ToBytes())
|
|
if len(str) == 0 {
|
|
return errors.New("empty reply")
|
|
}
|
|
if str[0] != '-' {
|
|
return nil
|
|
}
|
|
return errors.New(str[1:])
|
|
}
|
|
|
|
// ToBytes marshal redis.Reply
|
|
func (r *StandardErrReply) ToBytes() []byte {
|
|
return []byte("-" + r.Status + CRLF)
|
|
}
|
|
|
|
func (r *StandardErrReply) Error() string {
|
|
return r.Status
|
|
}
|