Skip to content

Commit

Permalink
introducing object encodings and implementing INCR
Browse files Browse the repository at this point in the history
  • Loading branch information
arpitbbhayani committed Sep 27, 2022
1 parent 249cb3a commit 9acc214
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 17 deletions.
48 changes: 39 additions & 9 deletions core/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,41 @@ func evalPING(args []string) []byte {

func evalSET(args []string) []byte {
if len(args) <= 1 {
return Encode(errors.New("(error) ERR wrong number of arguments for 'set' command"), false)
return Encode(errors.New("ERR wrong number of arguments for 'set' command"), false)
}

var key, value string
var exDurationMs int64 = -1

key, value = args[0], args[1]
oType, oEnc := deduceTypeEncoding(value)

for i := 2; i < len(args); i++ {
switch args[i] {
case "EX", "ex":
i++
if i == len(args) {
return Encode(errors.New("(error) ERR syntax error"), false)
return Encode(errors.New("ERR syntax error"), false)
}

exDurationSec, err := strconv.ParseInt(args[3], 10, 64)
if err != nil {
return Encode(errors.New("(error) ERR value is not an integer or out of range"), false)
return Encode(errors.New("ERR value is not an integer or out of range"), false)
}
exDurationMs = exDurationSec * 1000
default:
return Encode(errors.New("(error) ERR syntax error"), false)
return Encode(errors.New("ERR syntax error"), false)
}
}

// putting the k and value in a Hash Table
Put(key, NewObj(value, exDurationMs))
Put(key, NewObj(value, exDurationMs, oType, oEnc))
return RESP_OK
}

func evalGET(args []string) []byte {
if len(args) != 1 {
return Encode(errors.New("(error) ERR wrong number of arguments for 'get' command"), false)
return Encode(errors.New("ERR wrong number of arguments for 'get' command"), false)
}

var key string = args[0]
Expand All @@ -90,7 +91,7 @@ func evalGET(args []string) []byte {

func evalTTL(args []string) []byte {
if len(args) != 1 {
return Encode(errors.New("(error) ERR wrong number of arguments for 'ttl' command"), false)
return Encode(errors.New("ERR wrong number of arguments for 'ttl' command"), false)
}

var key string = args[0]
Expand Down Expand Up @@ -133,13 +134,13 @@ func evalDEL(args []string) []byte {

func evalEXPIRE(args []string) []byte {
if len(args) <= 1 {
return Encode(errors.New("(error) ERR wrong number of arguments for 'expire' command"), false)
return Encode(errors.New("ERR wrong number of arguments for 'expire' command"), false)
}

var key string = args[0]
exDurationSec, err := strconv.ParseInt(args[1], 10, 64)
if err != nil {
return Encode(errors.New("(error) ERR value is not an integer or out of range"), false)
return Encode(errors.New("ERR value is not an integer or out of range"), false)
}

obj := Get(key)
Expand All @@ -161,6 +162,33 @@ func evalBGREWRITEAOF(args []string) []byte {
return RESP_OK
}

func evalINCR(args []string) []byte {
if len(args) != 1 {
return Encode(errors.New("ERR wrong number of arguments for 'incr' command"), false)
}

var key string = args[0]
obj := Get(key)
if obj == nil {
obj = NewObj("0", -1, OBJ_TYPE_STRING, OBJ_ENCODING_INT)
Put(key, obj)
}

if err := assertType(obj.TypeEncoding, OBJ_TYPE_STRING); err != nil {
return Encode(err, false)
}

if err := assertEncoding(obj.TypeEncoding, OBJ_ENCODING_INT); err != nil {
return Encode(err, false)
}

i, _ := strconv.ParseInt(obj.Value.(string), 10, 64)
i++
obj.Value = strconv.FormatInt(i, 10)

return Encode(i, false)
}

func EvalAndRespond(cmds RedisCmds, c io.ReadWriter) {
var response []byte
buf := bytes.NewBuffer(response)
Expand All @@ -181,6 +209,8 @@ func EvalAndRespond(cmds RedisCmds, c io.ReadWriter) {
buf.Write(evalEXPIRE(cmd.Args))
case "BGREWRITEAOF":
buf.Write(evalBGREWRITEAOF(cmd.Args))
case "INCR":
buf.Write(evalINCR(cmd.Args))
default:
buf.Write(evalPING(cmd.Args))
}
Expand Down
14 changes: 14 additions & 0 deletions core/object.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package core

// TODO: change ExpiresAt it to LRU Bits as handled by Redis
type Obj struct {
TypeEncoding uint8
Value interface{}
ExpiresAt int64
}

var OBJ_TYPE_STRING uint8 = 0 << 4

var OBJ_ENCODING_RAW uint8 = 0
var OBJ_ENCODING_INT uint8 = 1
var OBJ_ENCODING_EMBSTR uint8 = 8
12 changes: 4 additions & 8 deletions core/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,20 @@ import (

var store map[string]*Obj

type Obj struct {
Value interface{}
ExpiresAt int64
}

func init() {
store = make(map[string]*Obj)
}

func NewObj(value interface{}, durationMs int64) *Obj {
func NewObj(value interface{}, durationMs int64, oType uint8, oEnc uint8) *Obj {
var expiresAt int64 = -1
if durationMs > 0 {
expiresAt = time.Now().UnixMilli() + durationMs
}

return &Obj{
Value: value,
ExpiresAt: expiresAt,
Value: value,
TypeEncoding: oType | oEnc,
ExpiresAt: expiresAt,
}
}

Expand Down
16 changes: 16 additions & 0 deletions core/type_string.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package core

import "strconv"

// Similar to
// tryObjectEncoding function in Redis
func deduceTypeEncoding(v string) (uint8, uint8) {
oType := OBJ_TYPE_STRING
if _, err := strconv.ParseInt(v, 10, 64); err == nil {
return oType, OBJ_ENCODING_INT
}
if len(v) <= 44 {
return oType, OBJ_ENCODING_EMBSTR
}
return oType, OBJ_ENCODING_RAW
}
25 changes: 25 additions & 0 deletions core/typeencoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package core

import "errors"

func getType(te uint8) uint8 {
return (te >> 4) << 4
}

func getEncoding(te uint8) uint8 {
return te & 0b00001111
}

func assertType(te uint8, t uint8) error {
if getType(te) != t {
return errors.New("the operation is not permitted on this type")
}
return nil
}

func assertEncoding(te uint8, e uint8) error {
if getEncoding(te) != e {
return errors.New("the operation is not permitted on this encoding")
}
return nil
}

0 comments on commit 9acc214

Please sign in to comment.