package main import ( "encoding/binary" "fmt" "github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/storage" ) type ErrIkeyCorrupted struct { Ikey []byte Reason string } func (e *ErrIkeyCorrupted) Error() string { return fmt.Sprintf("leveldb: iKey %q corrupted: %s", e.Ikey, e.Reason) } func newErrIkeyCorrupted(ikey []byte, reason string) error { return errors.NewErrCorrupted(storage.FileDesc{}, &ErrIkeyCorrupted{append([]byte{}, ikey...), reason}) } type kType int func (kt kType) String() string { switch kt { case ktDel: return "d" case ktVal: return "v" } return "x" } // Value types encoded as the last component of internal keys. // Don't modify; this value are saved to disk. const ( ktDel kType = iota ktVal ) // ktSeek defines the kType that should be passed when constructing an // internal key for seeking to a particular sequence number (since we // sort sequence numbers in decreasing order and the value type is // embedded as the low 8 bits in the sequence number in internal keys, // we need to use the highest-numbered ValueType, not the lowest). const ktSeek = ktVal const ( // Maximum value possible for sequence number; the 8-bits are // used by value type, so its can packed together in single // 64-bit integer. kMaxSeq uint64 = (uint64(1) << 56) - 1 // Maximum value possible for packed sequence number and type. kMaxNum uint64 = (kMaxSeq << 8) | uint64(ktSeek) ) // Maximum number encoded in bytes. var kMaxNumBytes = make([]byte, 8) func init() { binary.LittleEndian.PutUint64(kMaxNumBytes, kMaxNum) } type iKey []byte func newIkey(ukey []byte, seq uint64, kt kType) iKey { if seq > kMaxSeq { panic("leveldb: invalid sequence number") } else if kt > ktVal { panic("leveldb: invalid type") } ik := make(iKey, len(ukey)+8) copy(ik, ukey) binary.LittleEndian.PutUint64(ik[len(ukey):], (seq<<8)|uint64(kt)) return ik } func parseIkey(ik []byte) (ukey []byte, seq uint64, kt kType, err error) { if len(ik) < 8 { return nil, 0, 0, newErrIkeyCorrupted(ik, "invalid length") } num := binary.LittleEndian.Uint64(ik[len(ik)-8:]) seq, kt = uint64(num>>8), kType(num&0xff) if kt > ktVal { return nil, 0, 0, newErrIkeyCorrupted(ik, "invalid type") } ukey = ik[:len(ik)-8] return } func validIkey(ik []byte) bool { _, _, _, err := parseIkey(ik) return err == nil } func (ik iKey) assert() { if ik == nil { panic("leveldb: nil iKey") } if len(ik) < 8 { panic(fmt.Sprintf("leveldb: iKey %q, len=%d: invalid length", ik, len(ik))) } } func (ik iKey) ukey() []byte { ik.assert() return ik[:len(ik)-8] } func (ik iKey) num() uint64 { ik.assert() return binary.LittleEndian.Uint64(ik[len(ik)-8:]) } func (ik iKey) parseNum() (seq uint64, kt kType) { num := ik.num() seq, kt = uint64(num>>8), kType(num&0xff) if kt > ktVal { panic(fmt.Sprintf("leveldb: iKey %q, len=%d: invalid type %#x", ik, len(ik), kt)) } return } func (ik iKey) String() string { if ik == nil { return "" } if ukey, seq, kt, err := parseIkey(ik); err == nil { return fmt.Sprintf("%x,%s%d", ukey, kt, seq) } else { return "" } }