From f64aecdc91bcdc5692baeaf9b64316c1a87bb40f Mon Sep 17 00:00:00 2001 From: xgzlucario <912156837@qq.com> Date: Tue, 18 Jun 2024 00:55:55 +0800 Subject: [PATCH] add: add sadd command for db --- command.go | 34 ++++++++++++++++++++++++++++------ command_test.go | 5 +++++ go.mod | 1 - go.sum | 2 -- resp.go | 13 +++++++------ structx/map.go | 34 +++++++++++++--------------------- structx/set.go | 34 +++++++++------------------------- 7 files changed, 62 insertions(+), 61 deletions(-) diff --git a/command.go b/command.go index ae66e5c..7c0f1fc 100644 --- a/command.go +++ b/command.go @@ -7,8 +7,7 @@ import ( ) type Command struct { - // name is command string name. - // it should consist of all lowercase letters. + // name is lowercase letters command name. name string // handler is this command real database handler function. @@ -24,18 +23,19 @@ type Command struct { // cmdTable is the list of all available commands. var cmdTable []*Command = []*Command{ - {"ping", pingCommand, 0, false}, {"set", setCommand, 2, true}, {"get", getCommand, 1, false}, {"incr", incrCommand, 1, true}, {"hset", hsetCommand, 3, true}, {"hget", hgetCommand, 2, false}, {"hdel", hdelCommand, 2, true}, - {"hgetall", hgetallCommand, 1, false}, {"rpush", rpushCommand, 2, true}, {"lpush", lpushCommand, 2, true}, {"rpop", rpopCommand, 1, true}, {"lpop", lpopCommand, 1, true}, + {"sadd", saddCommand, 2, true}, + {"ping", pingCommand, 0, false}, + {"hgetall", hgetallCommand, 1, false}, {"lrange", lrangeCommand, 3, false}, } @@ -130,7 +130,7 @@ func hsetCommand(args []Arg) Value { var newFields int for i := 0; i < len(args); i += 2 { key := args[i].ToString() - value := args[i+1].ToBytes() + value := args[i+1].Clone() if hmap.Set(key, value) { newFields++ } @@ -146,7 +146,7 @@ func hgetCommand(args []Arg) Value { if err != nil { return newErrValue(ErrWrongType) } - value, _, ok := hmap.Get(key) + value, ok := hmap.Get(key) if !ok { return ValueNull } @@ -266,6 +266,24 @@ func lrangeCommand(args []Arg) Value { return newArrayValue(res) } +func saddCommand(args []Arg) Value { + key := args[0].ToString() + args = args[1:] + + set, err := fetchSet(key, true) + if err != nil { + return newErrValue(err) + } + + var newItems int + for i := 0; i < len(args); i++ { + if set.Add(args[i].ToString()) { + newItems++ + } + } + return newIntegerValue(newItems) +} + func fetchMap(key string, setnx ...bool) (Map, error) { return fetch(key, func() Map { return structx.NewMap() }, setnx...) } @@ -274,6 +292,10 @@ func fetchList(key string, setnx ...bool) (List, error) { return fetch(key, func() List { return structx.NewList() }, setnx...) } +func fetchSet(key string, setnx ...bool) (Set, error) { + return fetch(key, func() Set { return structx.NewSet() }, setnx...) +} + func fetch[T any](key string, new func() T, setnx ...bool) (v T, err error) { item, ok := db.extras[key] if ok { diff --git a/command_test.go b/command_test.go index 06f9124..0d021a2 100644 --- a/command_test.go +++ b/command_test.go @@ -129,6 +129,11 @@ func TestCommand(t *testing.T) { assert.Equal(val, "f") }) + t.Run("set", func(t *testing.T) { + n, _ := rdb.SAdd(ctx, "set", "k1", "k2", "k3").Result() + assert.Equal(n, int64(3)) + }) + t.Run("client-closed", func(t *testing.T) { rdb.Close() }) diff --git a/go.mod b/go.mod index 61be74e..dc7f51b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.22 require ( github.com/cockroachdb/swiss v0.0.0-20240612210725-f4de07ae6964 - github.com/deckarep/golang-set/v2 v2.6.0 github.com/redis/go-redis/v9 v9.5.2 github.com/rs/zerolog v1.33.0 github.com/sakeven/RbTree v1.1.1 diff --git a/go.sum b/go.sum index eb67f37..22f0cc9 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,6 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= diff --git a/resp.go b/resp.go index d6836c2..7c16881 100644 --- a/resp.go +++ b/resp.go @@ -3,6 +3,7 @@ package main import ( "fmt" "io" + "slices" "strconv" "unsafe" ) @@ -79,10 +80,6 @@ func cutByCRLF(buf []byte) (before, after []byte, found bool) { return } -func parseInt(b []byte) (int, error) { - return strconv.Atoi(b2s(b)) -} - func (r *Resp) ReadNextCommand(argsBuf []Arg) (args []Arg, err error) { if len(r.b) == 0 { return nil, io.EOF @@ -96,7 +93,7 @@ func (r *Resp) ReadNextCommand(argsBuf []Arg) (args []Arg, err error) { if !ok { return nil, ErrCRLFNotFound } - count, err := parseInt(before) + count, err := strconv.Atoi(b2s(before)) if err != nil { return nil, err } @@ -114,7 +111,7 @@ func (r *Resp) ReadNextCommand(argsBuf []Arg) (args []Arg, err error) { if !ok { return nil, ErrCRLFNotFound } - count, err := parseInt(before) + count, err := strconv.Atoi(b2s(before)) if err != nil { return nil, err } @@ -152,6 +149,10 @@ func (a Arg) ToBytes() []byte { return a } +func (a Arg) Clone() []byte { + return slices.Clone(a) +} + func b2s(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } diff --git a/structx/map.go b/structx/map.go index e67accb..5248283 100644 --- a/structx/map.go +++ b/structx/map.go @@ -1,44 +1,36 @@ package structx import ( - "github.com/xgzlucario/rotom/dict" + "github.com/cockroachdb/swiss" ) type Map struct { - m *dict.Dict + m *swiss.Map[string, []byte] } -func defaultOptions() dict.Options { - options := dict.DefaultOptions - options.ShardCount = 1 - options.IndexSize = 8 - options.BufferSize = 32 - return options +func NewMap() *Map { + return &Map{m: swiss.New[string, []byte](8)} } -func NewMap() (s *Map) { - return &Map{m: dict.New(defaultOptions())} -} - -func (m *Map) Get(key string) ([]byte, int64, bool) { +func (m *Map) Get(key string) ([]byte, bool) { return m.m.Get(key) } -func (m *Map) Set(key string, val []byte) (newField bool) { - return m.m.Set(key, val) +func (m *Map) Set(key string, val []byte) bool { + _, ok := m.m.Get(key) + m.m.Put(key, val) + return !ok } func (m *Map) Remove(key string) bool { - return m.m.Remove(key) + _, ok := m.m.Get(key) + m.m.Delete(key) + return ok } func (m *Map) Scan(fn func(key string, value []byte)) { - m.m.Scan(func(key string, val []byte, _ int64) (next bool) { + m.m.All(func(key string, val []byte) (next bool) { fn(key, val) return true }) } - -func (m *Map) Len() (n int) { - return m.m.GetStats().Len -} diff --git a/structx/set.go b/structx/set.go index 9bc068d..ea9cd99 100644 --- a/structx/set.go +++ b/structx/set.go @@ -1,35 +1,19 @@ package structx -import ( - mapset "github.com/deckarep/golang-set/v2" -) +import "github.com/cockroachdb/swiss" -// Set type Set struct { - mapset.Set[string] + m *swiss.Map[string, struct{}] } -// NewSet func NewSet() *Set { - return &Set{mapset.NewSet[string]()} + return &Set{m: swiss.New[string, struct{}](8)} } -// Clone -func (s *Set) Clone() *Set { - return &Set{s.Set.Clone()} -} - -// Union -func (s *Set) Union(other *Set) { - s.Set = s.Set.Union(other.Set) -} - -// Intersect -func (s *Set) Intersect(other *Set) { - s.Set = s.Set.Intersect(other.Set) -} - -// Difference -func (s *Set) Difference(other *Set) { - s.Set = s.Set.SymmetricDifference(other.Set) +func (s *Set) Add(key string) bool { + if _, ok := s.m.Get(key); ok { + return false + } + s.m.Put(key, struct{}{}) + return true }