1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
package rdb
import (
"encoding/binary"
"fmt"
"hash"
"io"
"math"
"strconv"
"github.com/cupcake/rdb/crc64"
)
const Version = 6
type Encoder struct {
w io.Writer
crc hash.Hash
}
func NewEncoder(w io.Writer) *Encoder {
e := &Encoder{crc: crc64.New()}
e.w = io.MultiWriter(w, e.crc)
return e
}
func (e *Encoder) EncodeHeader() error {
_, err := fmt.Fprintf(e.w, "REDIS%04d", Version)
return err
}
func (e *Encoder) EncodeFooter() error {
e.w.Write([]byte{rdbFlagEOF})
_, err := e.w.Write(e.crc.Sum(nil))
return err
}
func (e *Encoder) EncodeDumpFooter() error {
binary.Write(e.w, binary.LittleEndian, uint16(Version))
_, err := e.w.Write(e.crc.Sum(nil))
return err
}
func (e *Encoder) EncodeDatabase(n int) error {
e.w.Write([]byte{rdbFlagSelectDB})
return e.EncodeLength(uint32(n))
}
func (e *Encoder) EncodeExpiry(expiry uint64) error {
b := make([]byte, 9)
b[0] = rdbFlagExpiryMS
binary.LittleEndian.PutUint64(b[1:], expiry)
_, err := e.w.Write(b)
return err
}
func (e *Encoder) EncodeType(v ValueType) error {
_, err := e.w.Write([]byte{byte(v)})
return err
}
func (e *Encoder) EncodeString(s []byte) error {
written, err := e.encodeIntString(s)
if written {
return err
}
e.EncodeLength(uint32(len(s)))
_, err = e.w.Write(s)
return err
}
func (e *Encoder) EncodeLength(l uint32) (err error) {
switch {
case l < 1<<6:
_, err = e.w.Write([]byte{byte(l)})
case l < 1<<14:
_, err = e.w.Write([]byte{byte(l>>8) | rdb14bitLen<<6, byte(l)})
default:
b := make([]byte, 5)
b[0] = rdb32bitLen << 6
binary.BigEndian.PutUint32(b[1:], l)
_, err = e.w.Write(b)
}
return
}
func (e *Encoder) EncodeFloat(f float64) (err error) {
switch {
case math.IsNaN(f):
_, err = e.w.Write([]byte{253})
case math.IsInf(f, 1):
_, err = e.w.Write([]byte{254})
case math.IsInf(f, -1):
_, err = e.w.Write([]byte{255})
default:
b := []byte(strconv.FormatFloat(f, 'g', 17, 64))
e.w.Write([]byte{byte(len(b))})
_, err = e.w.Write(b)
}
return
}
func (e *Encoder) encodeIntString(b []byte) (written bool, err error) {
s := string(b)
i, err := strconv.ParseInt(s, 10, 32)
if err != nil {
return
}
// if the stringified parsed int isn't exactly the same, we can't encode it as an int
if s != strconv.FormatInt(i, 10) {
return
}
switch {
case i >= math.MinInt8 && i <= math.MaxInt8:
_, err = e.w.Write([]byte{rdbEncVal << 6, byte(int8(i))})
case i >= math.MinInt16 && i <= math.MaxInt16:
b := make([]byte, 3)
b[0] = rdbEncVal<<6 | rdbEncInt16
binary.LittleEndian.PutUint16(b[1:], uint16(int16(i)))
_, err = e.w.Write(b)
case i >= math.MinInt32 && i <= math.MaxInt32:
b := make([]byte, 5)
b[0] = rdbEncVal<<6 | rdbEncInt32
binary.LittleEndian.PutUint32(b[1:], uint32(int32(i)))
_, err = e.w.Write(b)
default:
return
}
return true, err
}
|