This is an automated email from the ASF dual-hosted git repository.
hulk pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 67208e6e Fix incorrent bytes to calculate length of ziplist/listpack
32bit string (#2124)
67208e6e is described below
commit 67208e6e94a7a98477ee214cda3dd7e890da5a55
Author: hulk <[email protected]>
AuthorDate: Thu Feb 29 00:37:05 2024 +0800
Fix incorrent bytes to calculate length of ziplist/listpack 32bit string
(#2124)
Currently, it calculates the length of 32-bit string from position 0 to 3,
but the right position should be 1 to 4 in ziplist/listpack.
So it will retrieve a wrong length and throw an error.
---
src/storage/rdb_listpack.cc | 4 +-
src/storage/rdb_ziplist.cc | 4 +-
tests/gocase/unit/rdb/rdb_test.go | 61 +++++++++++++++++++++++++++++++
tests/gocase/unit/restore/restore_test.go | 7 ++++
4 files changed, 72 insertions(+), 4 deletions(-)
diff --git a/src/storage/rdb_listpack.cc b/src/storage/rdb_listpack.cc
index 4eb3597d..cd4bba4d 100644
--- a/src/storage/rdb_listpack.cc
+++ b/src/storage/rdb_listpack.cc
@@ -207,8 +207,8 @@ StatusOr<std::string> ListPack::Next() {
pos_ += value_len + encodeBackLen(value_len + 2);
} else if ((c & ListPack32BitStringMask) == ListPack32BitString) { // 32bit
string
GET_OR_RET(peekOK(5));
- value_len = (static_cast<uint32_t>(data[pos_])) |
(static_cast<uint32_t>(data[pos_ + 1]) << 8) |
- (static_cast<uint32_t>(data[pos_ + 2]) << 16) |
(static_cast<uint32_t>(data[pos_ + 3]) << 24);
+ value_len = (static_cast<uint32_t>(data[pos_ + 1])) |
(static_cast<uint32_t>(data[pos_ + 2]) << 8) |
+ (static_cast<uint32_t>(data[pos_ + 3]) << 16) |
(static_cast<uint32_t>(data[pos_ + 4]) << 24);
// skip 5byte encoding type
pos_ += 5;
GET_OR_RET(peekOK(value_len));
diff --git a/src/storage/rdb_ziplist.cc b/src/storage/rdb_ziplist.cc
index 3d7cfd84..772226ea 100644
--- a/src/storage/rdb_ziplist.cc
+++ b/src/storage/rdb_ziplist.cc
@@ -63,8 +63,8 @@ StatusOr<std::string> ZipList::Next() {
} else if ((encoding) == ZIP_STR_32B) {
GET_OR_RET(peekOK(5));
len_bytes = 5;
- len = (static_cast<uint32_t>(data[pos_]) << 24) |
(static_cast<uint32_t>(data[pos_ + 1]) << 16) |
- (static_cast<uint32_t>(data[pos_ + 2]) << 8) |
static_cast<uint32_t>(data[pos_ + 3]);
+ len = (static_cast<uint32_t>(data[pos_ + 1]) << 24) |
(static_cast<uint32_t>(data[pos_ + 2]) << 16) |
+ (static_cast<uint32_t>(data[pos_ + 3]) << 8) |
static_cast<uint32_t>(data[pos_ + 4]);
} else {
return {Status::NotOK, "invalid ziplist encoding"};
}
diff --git a/tests/gocase/unit/rdb/rdb_test.go
b/tests/gocase/unit/rdb/rdb_test.go
new file mode 100644
index 00000000..619bd4ea
--- /dev/null
+++ b/tests/gocase/unit/rdb/rdb_test.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package rdb
+
+import (
+ "context"
+ "encoding/base64"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/apache/kvrocks/tests/gocase/util"
+ "github.com/stretchr/testify/require"
+)
+
+func TestLoadRDB(t *testing.T) {
+
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ client := srv.NewClient()
+ defer func() { require.NoError(t, client.Close()) }()
+
+ t.Run("key with ziplist 32bit string", func(t *testing.T) {
+ encodedData :=
"UkVESVMwMDA5+glyZWRpcy12ZXIGNi4yLjEw+gpyZWRpcy1iaXRzwED6BWN0aW1lwtbG3GX6CHVzZWQtbWVtwsA9FAD6DGFvZi1wcmVhbWJsZcAA/gD7AQAOBEFCQ0QNwxdB6wTrAQAA6CADBPAAAAAC4P8B4MsBAQD/w0HNgAAAi5EOkYsAAAoAAAABAACAAACLIAMBwH/g/wPhDQfg/wDg/wDg/wDg/wDg/wDg/wDg/wDg/wDg/wDg/wDg/wDg/wDg/wDg/wDg/wDgYwBP++D/AOD/AOD/AOD/AEAA5P8n4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P8A4LcAAcB/4LfB4P8A4P8A4P8A4P8A4P8A4P8A4P8A4P
[...]
+ decoder := base64.NewDecoder(base64.StdEncoding,
strings.NewReader(encodedData))
+ decodedData, err := io.ReadAll(decoder)
+ require.NoError(t, err)
+
+ rdbFileName := "dump-with-32bit-string.rdb"
+ require.NoError(t, os.WriteFile(rdbFileName, decodedData, 0644))
+ defer func() {
+ _ = os.Remove(rdbFileName)
+ }()
+
+ absFilePath, err := filepath.Abs(rdbFileName)
+ require.NoError(t, err)
+ require.NoError(t, client.Do(ctx, "RDB", "LOAD",
absFilePath).Err())
+ require.EqualValues(t, 601, client.LLen(ctx, "ABCD").Val())
+ })
+}
diff --git a/tests/gocase/unit/restore/restore_test.go
b/tests/gocase/unit/restore/restore_test.go
index 81d4a076..e4c8bc8c 100644
--- a/tests/gocase/unit/restore/restore_test.go
+++ b/tests/gocase/unit/restore/restore_test.go
@@ -193,6 +193,13 @@ func TestRestore_List(t *testing.T) {
}, rdb.LRange(ctx, key, 0, -1).Val())
require.EqualValues(t, -1, rdb.TTL(ctx, key).Val())
})
+
+ t.Run("List pack with 32bit string", func(t *testing.T) {
+ key := util.RandString(32, 64, util.Alpha)
+ value :=
"\x12\r\x02\xc3\x12A\xe7\a\xe7\x01\x00\x00\xf0\x00\x80\x01\xe0\xff\x01\xe0\xcc\x01\x01\x01\xff\x02\xc3A\xca\x80\x00\x00\x8b\x8f\a\x8f\x8b\x00\x00\x01\x00\xf0\x80
\x06\x03\x00\x00\xc0\x7f\xe0\xff\x03\xe1\r\a\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0c\x00O\xfb\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00\xe0\xff\x00@\x00\xe4\xff'\xe0\xff\x00\xe0\xf
[...]
+ require.NoError(t, rdb.Restore(ctx, key, 0, value).Err())
+ require.EqualValues(t, 601, rdb.LLen(ctx, key).Val())
+ })
}
func TestRestore_Set(t *testing.T) {