You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
2.9 KiB
113 lines
2.9 KiB
package structure
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"github.com/alibaba/RedisShake/internal/log"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
zipStr06B = 0x00 // 0000 ZIP_STR_06B
|
|
zipStr14B = 0x01 // 0001
|
|
zipStr32B = 0x02 // 0010
|
|
|
|
zipInt04B = 0x0f // high 4 bits of Int 04 encoding
|
|
|
|
zipInt08B = 0xfe // 11111110
|
|
zipInt16B = 0xc0 // 11000000
|
|
zipInt24B = 0xf0 // 11110000
|
|
zipInt32B = 0xd0 // 11010000
|
|
zipInt64B = 0xe0 // 11100000
|
|
)
|
|
|
|
func ReadZipList(rd io.Reader) []string {
|
|
rd = bufio.NewReader(strings.NewReader(ReadString(rd)))
|
|
|
|
// The general layout of the ziplist is as follows:
|
|
// <zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>
|
|
_ = ReadUint32(rd) // zlbytes
|
|
_ = ReadUint32(rd) // zltail
|
|
|
|
size := int(ReadUint16(rd))
|
|
var elements []string
|
|
if size == 65535 { // 2^16-1, we need to traverse the entire list to know how many items it holds.
|
|
for firstByte := ReadByte(rd); firstByte != 0xFE; firstByte = ReadByte(rd) {
|
|
ele := readZipListEntry(rd, firstByte)
|
|
elements = append(elements, ele)
|
|
}
|
|
} else {
|
|
for i := 0; i < size; i++ {
|
|
firstByte := ReadByte(rd)
|
|
ele := readZipListEntry(rd, firstByte)
|
|
elements = append(elements, ele)
|
|
}
|
|
if lastByte := ReadByte(rd); lastByte != 0xFF {
|
|
log.Panicf("invalid zipList lastByte encoding: %d", lastByte)
|
|
}
|
|
}
|
|
return elements
|
|
}
|
|
|
|
/*
|
|
* So practically an entry is encoded in the following way:
|
|
*
|
|
* <prevlen from 0 to 253> <encoding> <entry>
|
|
*
|
|
* Or alternatively if the previous entry length is greater than 253 bytes
|
|
* the following encoding is used:
|
|
*
|
|
* 0xFE <4 bytes unsigned little endian prevlen> <encoding> <entry>
|
|
*/
|
|
func readZipListEntry(rd io.Reader, firstByte byte) string {
|
|
// read prevlen
|
|
if firstByte == 0xFE {
|
|
_ = ReadUint32(rd) // read 4 bytes prevlen
|
|
}
|
|
|
|
// read encoding
|
|
firstByte = ReadByte(rd)
|
|
first2bits := (firstByte & 0xc0) >> 6 // first 2 bits of encoding
|
|
switch first2bits {
|
|
case zipStr06B:
|
|
length := int(firstByte & 0x3f) // 0x3f = 00111111
|
|
return string(ReadBytes(rd, length))
|
|
case zipStr14B:
|
|
secondByte := ReadByte(rd)
|
|
length := (int(firstByte&0x3f) << 8) | int(secondByte)
|
|
return string(ReadBytes(rd, length))
|
|
case zipStr32B:
|
|
lenBytes := ReadBytes(rd, 4)
|
|
length := binary.BigEndian.Uint32(lenBytes)
|
|
return string(ReadBytes(rd, int(length)))
|
|
}
|
|
switch firstByte {
|
|
case zipInt08B:
|
|
v := ReadInt8(rd)
|
|
return strconv.FormatInt(int64(v), 10)
|
|
case zipInt16B:
|
|
v := ReadInt16(rd)
|
|
return strconv.FormatInt(int64(v), 10)
|
|
case zipInt24B:
|
|
v := ReadInt24(rd)
|
|
return strconv.FormatInt(int64(v), 10)
|
|
case zipInt32B:
|
|
v := ReadInt32(rd)
|
|
return strconv.FormatInt(int64(v), 10)
|
|
case zipInt64B:
|
|
v := ReadInt64(rd)
|
|
return strconv.FormatInt(v, 10)
|
|
}
|
|
if (firstByte >> 4) == zipInt04B {
|
|
v := int64(firstByte & 0x0f) // 0x0f = 00001111
|
|
v = v - 1 // 1-13 -> 0-12
|
|
if v < 0 || v > 12 {
|
|
log.Panicf("invalid zipInt04B encoding: %d", v)
|
|
}
|
|
return strconv.FormatInt(v, 10)
|
|
}
|
|
log.Panicf("invalid encoding: %d", firstByte)
|
|
return ""
|
|
}
|
|
|