Skip to content

Commit cebd383

Browse files
committed
feat: RedisResult.AsBytes without copy
1 parent cbe2684 commit cebd383

File tree

5 files changed

+59
-11
lines changed

5 files changed

+59
-11
lines changed

binary.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ import (
88
// BinaryString convert the provided []byte into a string without copy. It does what strings.Builder.String() does.
99
// Redis Strings are binary safe, this means that it is safe to store any []byte into Redis directly.
1010
// Users can use this BinaryString helper to insert a []byte as the part of redis command. For example:
11-
// client.B().Set().Key(rueidis.BinaryString([]byte{0})).Value(rueidis.BinaryString([]byte{0})).Build()
11+
//
12+
// client.B().Set().Key(rueidis.BinaryString([]byte{0})).Value(rueidis.BinaryString([]byte{0})).Build()
13+
//
1214
// To read back the []byte of the string returned from the Redis, it is recommended to use the RedisMessage.AsReader.
1315
func BinaryString(bs []byte) string {
14-
return *(*string)(unsafe.Pointer(&bs))
16+
return unsafe.String(unsafe.SliceData(bs), len(bs))
1517
}
1618

1719
// JSON convert the provided parameter into a JSON string. Users can use this JSON helper to work with RedisJSON commands.
1820
// For example:
19-
// client.B().JsonSet().Key("a").Path("$.myField").Value(rueidis.JSON("str")).Build()
21+
//
22+
// client.B().JsonSet().Key("a").Path("$.myField").Value(rueidis.JSON("str")).Build()
2023
func JSON(in any) string {
2124
bs, err := json.Marshal(in)
2225
if err != nil {

message.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ func (r RedisResult) AsReader() (v io.Reader, err error) {
165165
return
166166
}
167167

168+
// AsBytes delegates to RedisMessage.AsBytes
169+
func (r RedisResult) AsBytes() (v []byte, err error) {
170+
if r.err != nil {
171+
err = r.err
172+
} else {
173+
v, err = r.val.AsBytes()
174+
}
175+
return
176+
}
177+
168178
// DecodeJSON delegates to RedisMessage.DecodeJSON
169179
func (r RedisResult) DecodeJSON(v any) (err error) {
170180
if r.err != nil {
@@ -473,6 +483,15 @@ func (m *RedisMessage) AsReader() (reader io.Reader, err error) {
473483
return strings.NewReader(str), nil
474484
}
475485

486+
// AsBytes check if message is a redis string response and return it as an immutable []byte
487+
func (m *RedisMessage) AsBytes() (bs []byte, err error) {
488+
str, err := m.ToString()
489+
if err != nil {
490+
return nil, err
491+
}
492+
return unsafe.Slice(unsafe.StringData(str), len(str)), nil
493+
}
494+
476495
// DecodeJSON check if message is a redis string response and treat it as json, then unmarshal it into provided value
477496
func (m *RedisMessage) DecodeJSON(v any) (err error) {
478497
str, err := m.ToString()

message_test.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,29 @@ func TestRedisResult(t *testing.T) {
108108
}
109109
})
110110

111+
t.Run("AsBytes", func(t *testing.T) {
112+
if _, err := (RedisResult{err: errors.New("other")}).AsBytes(); err == nil {
113+
t.Fatal("AsBytes not failed as expected")
114+
}
115+
if _, err := (RedisResult{val: RedisMessage{typ: '-'}}).AsBytes(); err == nil {
116+
t.Fatal("AsBytes not failed as expected")
117+
}
118+
bs, _ := (RedisResult{val: RedisMessage{typ: '+', string: "0.1"}}).AsBytes()
119+
if !bytes.Equal(bs, []byte("0.1")) {
120+
t.Fatalf("AsBytes not get value as expected %v", bs)
121+
}
122+
})
123+
111124
t.Run("DecodeJSON", func(t *testing.T) {
112125
v := map[string]string{}
113126
if err := (RedisResult{err: errors.New("other")}).DecodeJSON(&v); err == nil {
114-
t.Fatal("AsReader not failed as expected")
127+
t.Fatal("DecodeJSON not failed as expected")
115128
}
116129
if err := (RedisResult{val: RedisMessage{typ: '-'}}).DecodeJSON(&v); err == nil {
117-
t.Fatal("AsReader not failed as expected")
130+
t.Fatal("DecodeJSON not failed as expected")
118131
}
119132
if _ = (RedisResult{val: RedisMessage{typ: '+', string: `{"k":"v"}`}}).DecodeJSON(&v); v["k"] != "v" {
120-
t.Fatalf("AsReader not get value as expected %v", v)
133+
t.Fatalf("DecodeJSON not get value as expected %v", v)
121134
}
122135
})
123136

@@ -629,6 +642,19 @@ func TestRedisMessage(t *testing.T) {
629642
(&RedisMessage{typ: ':'}).AsReader()
630643
})
631644

645+
t.Run("AsBytes", func(t *testing.T) {
646+
if _, err := (&RedisMessage{typ: '_'}).AsBytes(); err == nil {
647+
t.Fatal("AsBytes not failed as expected")
648+
}
649+
650+
defer func() {
651+
if !strings.Contains(recover().(string), "redis message type : is not a string") {
652+
t.Fatal("AsBytes not panic as expected")
653+
}
654+
}()
655+
(&RedisMessage{typ: ':'}).AsBytes()
656+
})
657+
632658
t.Run("DecodeJSON", func(t *testing.T) {
633659
if err := (&RedisMessage{typ: '_'}).DecodeJSON(nil); err == nil {
634660
t.Fatal("DecodeJSON not failed as expected")

om/conv.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import (
44
"fmt"
55
"reflect"
66
"strconv"
7-
"unsafe"
7+
8+
"github.com/rueian/rueidis"
89
)
910

1011
func newHashConvFactory(t reflect.Type, schema schema) *hashConvFactory {
@@ -164,7 +165,7 @@ var converters = struct {
164165
if !ok {
165166
return "", false
166167
}
167-
return *(*string)(unsafe.Pointer(&buf)), true
168+
return rueidis.BinaryString(buf), true
168169
},
169170
StringToValue: func(value string) (reflect.Value, error) {
170171
buf := []byte(value)

resp.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"io"
77
"strconv"
88
"strings"
9-
"unsafe"
109
)
1110

1211
var errChunked = errors.New("unbounded redis message")
@@ -122,7 +121,7 @@ func readS(i *bufio.Reader) (string, error) {
122121
} else {
123122
bs = bs[:trim]
124123
}
125-
return *(*string)(unsafe.Pointer(&bs)), nil
124+
return BinaryString(bs), nil
126125
}
127126

128127
func readI(i *bufio.Reader) (int64, error) {
@@ -170,7 +169,7 @@ func readB(i *bufio.Reader) (string, error) {
170169
if _, err = i.Discard(2); err != nil {
171170
return "", err
172171
}
173-
return *(*string)(unsafe.Pointer(&bs)), nil
172+
return BinaryString(bs), nil
174173
}
175174

176175
func readE(i *bufio.Reader) ([]RedisMessage, error) {

0 commit comments

Comments
 (0)