113 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2014 Wandoujia Inc. All Rights Reserved.
 | |
| // Licensed under the MIT (MIT-LICENSE.txt) license.
 | |
| 
 | |
| package rdb
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/binary"
 | |
| 	"fmt"
 | |
| 	"hash"
 | |
| 	"io"
 | |
| 	"strconv"
 | |
| )
 | |
| 
 | |
| type Loader struct {
 | |
| 	*rdbReader
 | |
| 	crc hash.Hash64
 | |
| 	db  uint32
 | |
| }
 | |
| 
 | |
| func NewLoader(r io.Reader) *Loader {
 | |
| 	l := &Loader{}
 | |
| 	l.crc = newDigest()
 | |
| 	l.rdbReader = newRdbReader(io.TeeReader(r, l.crc))
 | |
| 	return l
 | |
| }
 | |
| 
 | |
| func (l *Loader) LoadHeader() error {
 | |
| 	header := make([]byte, 9)
 | |
| 	if err := l.readFull(header); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if !bytes.Equal(header[:5], []byte("REDIS")) {
 | |
| 		return fmt.Errorf("verify magic string, invalid file format")
 | |
| 	}
 | |
| 	if version, err := strconv.ParseInt(string(header[5:]), 10, 64); err != nil {
 | |
| 		return err
 | |
| 	} else if version <= 0 || version > Version {
 | |
| 		return fmt.Errorf("verify version, invalid RDB version number %d", version)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (l *Loader) LoadChecksum() error {
 | |
| 	crc1 := l.crc.Sum64()
 | |
| 	if crc2, err := l.readUint64(); err != nil {
 | |
| 		return err
 | |
| 	} else if crc1 != crc2 {
 | |
| 		return fmt.Errorf("checksum validation failed")
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type Entry struct {
 | |
| 	DB       uint32
 | |
| 	Key      []byte
 | |
| 	ValDump  []byte
 | |
| 	ExpireAt uint64
 | |
| }
 | |
| 
 | |
| func (l *Loader) LoadEntry() (entry *Entry, err error) {
 | |
| 	var expireat uint64
 | |
| 	for {
 | |
| 		var otype byte
 | |
| 		if otype, err = l.readByte(); err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 		switch otype {
 | |
| 		case rdbFlagExpiryMS:
 | |
| 			if expireat, err = l.readUint64(); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 		case rdbFlagExpiry:
 | |
| 			var sec uint32
 | |
| 			if sec, err = l.readUint32(); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			expireat = uint64(sec) * 1000
 | |
| 		case rdbFlagSelectDB:
 | |
| 			if l.db, err = l.readLength(); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 		case rdbFlagEOF:
 | |
| 			return
 | |
| 		default:
 | |
| 			var key, obj []byte
 | |
| 			if key, err = l.readString(); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			if obj, err = l.readObject(otype); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 			entry = &Entry{}
 | |
| 			entry.DB = l.db
 | |
| 			entry.Key = key
 | |
| 			entry.ValDump = createValDump(otype, obj)
 | |
| 			entry.ExpireAt = expireat
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func createValDump(otype byte, obj []byte) []byte {
 | |
| 	var b bytes.Buffer
 | |
| 	c := newDigest()
 | |
| 	w := io.MultiWriter(&b, c)
 | |
| 	w.Write([]byte{otype})
 | |
| 	w.Write(obj)
 | |
| 	binary.Write(w, binary.LittleEndian, uint16(Version))
 | |
| 	binary.Write(w, binary.LittleEndian, c.Sum64())
 | |
| 	return b.Bytes()
 | |
| }
 |