mirror of https://github.com/ledisdb/ledisdb.git
333 lines
6.4 KiB
Go
333 lines
6.4 KiB
Go
// Copyright 2014 Wandoujia Inc. All Rights Reserved.
|
|
// Licensed under the MIT (MIT-LICENSE.txt) license.
|
|
|
|
package rdb
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
Version = 6
|
|
)
|
|
|
|
const (
|
|
rdbTypeString = 0
|
|
rdbTypeList = 1
|
|
rdbTypeSet = 2
|
|
rdbTypeZSet = 3
|
|
rdbTypeHash = 4
|
|
|
|
rdbTypeHashZipmap = 9
|
|
rdbTypeListZiplist = 10
|
|
rdbTypeSetIntset = 11
|
|
rdbTypeZSetZiplist = 12
|
|
rdbTypeHashZiplist = 13
|
|
|
|
rdbFlagExpiryMS = 0xfc
|
|
rdbFlagExpiry = 0xfd
|
|
rdbFlagSelectDB = 0xfe
|
|
rdbFlagEOF = 0xff
|
|
)
|
|
|
|
const (
|
|
rdb6bitLen = 0
|
|
rdb14bitLen = 1
|
|
rdb32bitLen = 2
|
|
rdbEncVal = 3
|
|
|
|
rdbEncInt8 = 0
|
|
rdbEncInt16 = 1
|
|
rdbEncInt32 = 2
|
|
rdbEncLZF = 3
|
|
|
|
rdbZiplist6bitlenString = 0
|
|
rdbZiplist14bitlenString = 1
|
|
rdbZiplist32bitlenString = 2
|
|
|
|
rdbZiplistInt16 = 0xc0
|
|
rdbZiplistInt32 = 0xd0
|
|
rdbZiplistInt64 = 0xe0
|
|
rdbZiplistInt24 = 0xf0
|
|
rdbZiplistInt8 = 0xfe
|
|
rdbZiplistInt4 = 15
|
|
)
|
|
|
|
type rdbReader struct {
|
|
raw io.Reader
|
|
buf [8]byte
|
|
nread int64
|
|
}
|
|
|
|
func newRdbReader(r io.Reader) *rdbReader {
|
|
return &rdbReader{raw: r}
|
|
}
|
|
|
|
func (r *rdbReader) Read(p []byte) (int, error) {
|
|
n, err := r.raw.Read(p)
|
|
r.nread += int64(n)
|
|
return n, err
|
|
}
|
|
|
|
func (r *rdbReader) offset() int64 {
|
|
return r.nread
|
|
}
|
|
|
|
func (r *rdbReader) readObject(otype byte) ([]byte, error) {
|
|
var b bytes.Buffer
|
|
r = newRdbReader(io.TeeReader(r, &b))
|
|
switch otype {
|
|
default:
|
|
return nil, fmt.Errorf("unknown object-type %02x", otype)
|
|
case rdbTypeHashZipmap:
|
|
fallthrough
|
|
case rdbTypeListZiplist:
|
|
fallthrough
|
|
case rdbTypeSetIntset:
|
|
fallthrough
|
|
case rdbTypeZSetZiplist:
|
|
fallthrough
|
|
case rdbTypeHashZiplist:
|
|
fallthrough
|
|
case rdbTypeString:
|
|
if _, err := r.readString(); err != nil {
|
|
return nil, err
|
|
}
|
|
case rdbTypeList, rdbTypeSet:
|
|
if n, err := r.readLength(); err != nil {
|
|
return nil, err
|
|
} else {
|
|
for i := 0; i < int(n); i++ {
|
|
if _, err := r.readString(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
case rdbTypeZSet:
|
|
if n, err := r.readLength(); err != nil {
|
|
return nil, err
|
|
} else {
|
|
for i := 0; i < int(n); i++ {
|
|
if _, err := r.readString(); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := r.readFloat(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
case rdbTypeHash:
|
|
if n, err := r.readLength(); err != nil {
|
|
return nil, err
|
|
} else {
|
|
for i := 0; i < int(n); i++ {
|
|
if _, err := r.readString(); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := r.readString(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
func (r *rdbReader) readString() ([]byte, error) {
|
|
length, encoded, err := r.readEncodedLength()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !encoded {
|
|
return r.readBytes(int(length))
|
|
}
|
|
switch t := uint8(length); t {
|
|
default:
|
|
return nil, fmt.Errorf("invalid encoded-string %02x", t)
|
|
case rdbEncInt8:
|
|
i, err := r.readInt8()
|
|
return []byte(strconv.FormatInt(int64(i), 10)), err
|
|
case rdbEncInt16:
|
|
i, err := r.readInt16()
|
|
return []byte(strconv.FormatInt(int64(i), 10)), err
|
|
case rdbEncInt32:
|
|
i, err := r.readInt32()
|
|
return []byte(strconv.FormatInt(int64(i), 10)), err
|
|
case rdbEncLZF:
|
|
var inlen, outlen uint32
|
|
if inlen, err = r.readLength(); err != nil {
|
|
return nil, err
|
|
}
|
|
if outlen, err = r.readLength(); err != nil {
|
|
return nil, err
|
|
}
|
|
if in, err := r.readBytes(int(inlen)); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return lzfDecompress(in, int(outlen))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *rdbReader) readEncodedLength() (length uint32, encoded bool, err error) {
|
|
var u uint8
|
|
if u, err = r.readUint8(); err != nil {
|
|
return
|
|
}
|
|
length = uint32(u & 0x3f)
|
|
switch u >> 6 {
|
|
case rdb6bitLen:
|
|
case rdb14bitLen:
|
|
u, err = r.readUint8()
|
|
length = (length << 8) + uint32(u)
|
|
case rdbEncVal:
|
|
encoded = true
|
|
default:
|
|
length, err = r.readUint32BigEndian()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (r *rdbReader) readLength() (uint32, error) {
|
|
length, encoded, err := r.readEncodedLength()
|
|
if err == nil && encoded {
|
|
err = fmt.Errorf("encoded-length")
|
|
}
|
|
return length, err
|
|
}
|
|
|
|
func (r *rdbReader) readFloat() (float64, error) {
|
|
u, err := r.readUint8()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
switch u {
|
|
case 253:
|
|
return math.NaN(), nil
|
|
case 254:
|
|
return math.Inf(0), nil
|
|
case 255:
|
|
return math.Inf(-1), nil
|
|
default:
|
|
if b, err := r.readBytes(int(u)); err != nil {
|
|
return 0, err
|
|
} else {
|
|
v, err := strconv.ParseFloat(string(b), 64)
|
|
return v, err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *rdbReader) readByte() (byte, error) {
|
|
b := r.buf[:1]
|
|
_, err := r.Read(b)
|
|
return b[0], err
|
|
}
|
|
|
|
func (r *rdbReader) readFull(p []byte) error {
|
|
_, err := io.ReadFull(r, p)
|
|
return err
|
|
}
|
|
|
|
func (r *rdbReader) readBytes(n int) ([]byte, error) {
|
|
p := make([]byte, n)
|
|
return p, r.readFull(p)
|
|
}
|
|
|
|
func (r *rdbReader) readUint8() (uint8, error) {
|
|
b, err := r.readByte()
|
|
return uint8(b), err
|
|
}
|
|
|
|
func (r *rdbReader) readUint16() (uint16, error) {
|
|
b := r.buf[:2]
|
|
err := r.readFull(b)
|
|
return binary.LittleEndian.Uint16(b), err
|
|
}
|
|
|
|
func (r *rdbReader) readUint32() (uint32, error) {
|
|
b := r.buf[:4]
|
|
err := r.readFull(b)
|
|
return binary.LittleEndian.Uint32(b), err
|
|
}
|
|
|
|
func (r *rdbReader) readUint64() (uint64, error) {
|
|
b := r.buf[:8]
|
|
err := r.readFull(b)
|
|
return binary.LittleEndian.Uint64(b), err
|
|
}
|
|
|
|
func (r *rdbReader) readUint32BigEndian() (uint32, error) {
|
|
b := r.buf[:4]
|
|
err := r.readFull(b)
|
|
return binary.BigEndian.Uint32(b), err
|
|
}
|
|
|
|
func (r *rdbReader) readInt8() (int8, error) {
|
|
u, err := r.readUint8()
|
|
return int8(u), err
|
|
}
|
|
|
|
func (r *rdbReader) readInt16() (int16, error) {
|
|
u, err := r.readUint16()
|
|
return int16(u), err
|
|
}
|
|
|
|
func (r *rdbReader) readInt32() (int32, error) {
|
|
u, err := r.readUint32()
|
|
return int32(u), err
|
|
}
|
|
|
|
func (r *rdbReader) readInt64() (int64, error) {
|
|
u, err := r.readUint64()
|
|
return int64(u), err
|
|
}
|
|
|
|
func (r *rdbReader) readInt32BigEndian() (int32, error) {
|
|
u, err := r.readUint32BigEndian()
|
|
return int32(u), err
|
|
}
|
|
|
|
func lzfDecompress(in []byte, outlen int) (out []byte, err error) {
|
|
defer func() {
|
|
if x := recover(); x != nil {
|
|
err = fmt.Errorf("decompress exception: %v", x)
|
|
}
|
|
}()
|
|
out = make([]byte, outlen)
|
|
i, o := 0, 0
|
|
for i < len(in) {
|
|
ctrl := int(in[i])
|
|
i++
|
|
if ctrl < 32 {
|
|
for x := 0; x <= ctrl; x++ {
|
|
out[o] = in[i]
|
|
i++
|
|
o++
|
|
}
|
|
} else {
|
|
length := ctrl >> 5
|
|
if length == 7 {
|
|
length = length + int(in[i])
|
|
i++
|
|
}
|
|
ref := o - ((ctrl & 0x1f) << 8) - int(in[i]) - 1
|
|
i++
|
|
for x := 0; x <= length+1; x++ {
|
|
out[o] = out[ref]
|
|
ref++
|
|
o++
|
|
}
|
|
}
|
|
}
|
|
if o != outlen {
|
|
return nil, fmt.Errorf("decompress length is %d != expected %d", o, outlen)
|
|
}
|
|
return out, nil
|
|
}
|