1package main
2
3import (
4	"encoding/binary"
5	"fmt"
6
7	"github.com/syndtr/goleveldb/leveldb/errors"
8	"github.com/syndtr/goleveldb/leveldb/storage"
9)
10
11type ErrIkeyCorrupted struct {
12	Ikey   []byte
13	Reason string
14}
15
16func (e *ErrIkeyCorrupted) Error() string {
17	return fmt.Sprintf("leveldb: iKey %q corrupted: %s", e.Ikey, e.Reason)
18}
19
20func newErrIkeyCorrupted(ikey []byte, reason string) error {
21	return errors.NewErrCorrupted(storage.FileDesc{}, &ErrIkeyCorrupted{append([]byte{}, ikey...), reason})
22}
23
24type kType int
25
26func (kt kType) String() string {
27	switch kt {
28	case ktDel:
29		return "d"
30	case ktVal:
31		return "v"
32	}
33	return "x"
34}
35
36// Value types encoded as the last component of internal keys.
37// Don't modify; this value are saved to disk.
38const (
39	ktDel kType = iota
40	ktVal
41)
42
43// ktSeek defines the kType that should be passed when constructing an
44// internal key for seeking to a particular sequence number (since we
45// sort sequence numbers in decreasing order and the value type is
46// embedded as the low 8 bits in the sequence number in internal keys,
47// we need to use the highest-numbered ValueType, not the lowest).
48const ktSeek = ktVal
49
50const (
51	// Maximum value possible for sequence number; the 8-bits are
52	// used by value type, so its can packed together in single
53	// 64-bit integer.
54	kMaxSeq uint64 = (uint64(1) << 56) - 1
55	// Maximum value possible for packed sequence number and type.
56	kMaxNum uint64 = (kMaxSeq << 8) | uint64(ktSeek)
57)
58
59// Maximum number encoded in bytes.
60var kMaxNumBytes = make([]byte, 8)
61
62func init() {
63	binary.LittleEndian.PutUint64(kMaxNumBytes, kMaxNum)
64}
65
66type iKey []byte
67
68func newIkey(ukey []byte, seq uint64, kt kType) iKey {
69	if seq > kMaxSeq {
70		panic("leveldb: invalid sequence number")
71	} else if kt > ktVal {
72		panic("leveldb: invalid type")
73	}
74
75	ik := make(iKey, len(ukey)+8)
76	copy(ik, ukey)
77	binary.LittleEndian.PutUint64(ik[len(ukey):], (seq<<8)|uint64(kt))
78	return ik
79}
80
81func parseIkey(ik []byte) (ukey []byte, seq uint64, kt kType, err error) {
82	if len(ik) < 8 {
83		return nil, 0, 0, newErrIkeyCorrupted(ik, "invalid length")
84	}
85	num := binary.LittleEndian.Uint64(ik[len(ik)-8:])
86	seq, kt = uint64(num>>8), kType(num&0xff)
87	if kt > ktVal {
88		return nil, 0, 0, newErrIkeyCorrupted(ik, "invalid type")
89	}
90	ukey = ik[:len(ik)-8]
91	return
92}
93
94func validIkey(ik []byte) bool {
95	_, _, _, err := parseIkey(ik)
96	return err == nil
97}
98
99func (ik iKey) assert() {
100	if ik == nil {
101		panic("leveldb: nil iKey")
102	}
103	if len(ik) < 8 {
104		panic(fmt.Sprintf("leveldb: iKey %q, len=%d: invalid length", ik, len(ik)))
105	}
106}
107
108func (ik iKey) ukey() []byte {
109	ik.assert()
110	return ik[:len(ik)-8]
111}
112
113func (ik iKey) num() uint64 {
114	ik.assert()
115	return binary.LittleEndian.Uint64(ik[len(ik)-8:])
116}
117
118func (ik iKey) parseNum() (seq uint64, kt kType) {
119	num := ik.num()
120	seq, kt = uint64(num>>8), kType(num&0xff)
121	if kt > ktVal {
122		panic(fmt.Sprintf("leveldb: iKey %q, len=%d: invalid type %#x", ik, len(ik), kt))
123	}
124	return
125}
126
127func (ik iKey) String() string {
128	if ik == nil {
129		return "<nil>"
130	}
131
132	if ukey, seq, kt, err := parseIkey(ik); err == nil {
133		return fmt.Sprintf("%x,%s%d", ukey, kt, seq)
134	} else {
135		return "<invalid>"
136	}
137}
138