| package badger |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| "fmt" |
| "hash/crc32" |
| "time" |
| |
| "github.com/dgraph-io/badger/y" |
| ) |
| |
| type valuePointer struct { |
| Fid uint32 |
| Len uint32 |
| Offset uint32 |
| } |
| |
| func (p valuePointer) Less(o valuePointer) bool { |
| if p.Fid != o.Fid { |
| return p.Fid < o.Fid |
| } |
| if p.Offset != o.Offset { |
| return p.Offset < o.Offset |
| } |
| return p.Len < o.Len |
| } |
| |
| func (p valuePointer) IsZero() bool { |
| return p.Fid == 0 && p.Offset == 0 && p.Len == 0 |
| } |
| |
| const vptrSize = 12 |
| |
| // Encode encodes Pointer into byte buffer. |
| func (p valuePointer) Encode(b []byte) []byte { |
| binary.BigEndian.PutUint32(b[:4], p.Fid) |
| binary.BigEndian.PutUint32(b[4:8], p.Len) |
| binary.BigEndian.PutUint32(b[8:12], p.Offset) |
| return b[:vptrSize] |
| } |
| |
| func (p *valuePointer) Decode(b []byte) { |
| p.Fid = binary.BigEndian.Uint32(b[:4]) |
| p.Len = binary.BigEndian.Uint32(b[4:8]) |
| p.Offset = binary.BigEndian.Uint32(b[8:12]) |
| } |
| |
| // header is used in value log as a header before Entry. |
| type header struct { |
| klen uint32 |
| vlen uint32 |
| expiresAt uint64 |
| meta byte |
| userMeta byte |
| } |
| |
| const ( |
| headerBufSize = 18 |
| ) |
| |
| func (h header) Encode(out []byte) { |
| y.AssertTrue(len(out) >= headerBufSize) |
| binary.BigEndian.PutUint32(out[0:4], h.klen) |
| binary.BigEndian.PutUint32(out[4:8], h.vlen) |
| binary.BigEndian.PutUint64(out[8:16], h.expiresAt) |
| out[16] = h.meta |
| out[17] = h.userMeta |
| } |
| |
| // Decodes h from buf. |
| func (h *header) Decode(buf []byte) { |
| h.klen = binary.BigEndian.Uint32(buf[0:4]) |
| h.vlen = binary.BigEndian.Uint32(buf[4:8]) |
| h.expiresAt = binary.BigEndian.Uint64(buf[8:16]) |
| h.meta = buf[16] |
| h.userMeta = buf[17] |
| } |
| |
| // Entry provides Key, Value, UserMeta and ExpiresAt. This struct can be used by |
| // the user to set data. |
| type Entry struct { |
| Key []byte |
| Value []byte |
| UserMeta byte |
| ExpiresAt uint64 // time.Unix |
| meta byte |
| |
| // Fields maintained internally. |
| offset uint32 |
| skipVlog bool |
| } |
| |
| func (e *Entry) estimateSize(threshold int) int { |
| if len(e.Value) < threshold { |
| return len(e.Key) + len(e.Value) + 2 // Meta, UserMeta |
| } |
| return len(e.Key) + 12 + 2 // 12 for ValuePointer, 2 for metas. |
| } |
| |
| // Encodes e to buf. Returns number of bytes written. |
| func encodeEntry(e *Entry, buf *bytes.Buffer) (int, error) { |
| h := header{ |
| klen: uint32(len(e.Key)), |
| vlen: uint32(len(e.Value)), |
| expiresAt: e.ExpiresAt, |
| meta: e.meta, |
| userMeta: e.UserMeta, |
| } |
| |
| var headerEnc [headerBufSize]byte |
| h.Encode(headerEnc[:]) |
| |
| hash := crc32.New(y.CastagnoliCrcTable) |
| |
| buf.Write(headerEnc[:]) |
| if _, err := hash.Write(headerEnc[:]); err != nil { |
| return 0, err |
| } |
| |
| buf.Write(e.Key) |
| if _, err := hash.Write(e.Key); err != nil { |
| return 0, err |
| } |
| |
| buf.Write(e.Value) |
| if _, err := hash.Write(e.Value); err != nil { |
| return 0, err |
| } |
| |
| var crcBuf [crc32.Size]byte |
| binary.BigEndian.PutUint32(crcBuf[:], hash.Sum32()) |
| buf.Write(crcBuf[:]) |
| |
| return len(headerEnc) + len(e.Key) + len(e.Value) + len(crcBuf), nil |
| } |
| |
| func (e Entry) print(prefix string) { |
| fmt.Printf("%s Key: %s Meta: %d UserMeta: %d Offset: %d len(val)=%d", |
| prefix, e.Key, e.meta, e.UserMeta, e.offset, len(e.Value)) |
| } |
| |
| // NewEntry creates a new entry with key and value passed in args. This newly created entry can be |
| // set in a transaction by calling txn.SetEntry(). All other properties of Entry can be set by |
| // calling WithMeta, WithDiscard, WithTTL methods on it. |
| // This function uses key and value reference, hence users must |
| // not modify key and value until the end of transaction. |
| func NewEntry(key, value []byte) *Entry { |
| return &Entry{ |
| Key: key, |
| Value: value, |
| } |
| } |
| |
| // WithMeta adds meta data to Entry e. This byte is stored alongside the key |
| // and can be used as an aid to interpret the value or store other contextual |
| // bits corresponding to the key-value pair of entry. |
| func (e *Entry) WithMeta(meta byte) *Entry { |
| e.UserMeta = meta |
| return e |
| } |
| |
| // WithDiscard adds a marker to Entry e. This means all the previous versions of the key (of the |
| // Entry) will be eligible for garbage collection. |
| // This method is only useful if you have set a higher limit for options.NumVersionsToKeep. The |
| // default setting is 1, in which case, this function doesn't add any more benefit. If however, you |
| // have a higher setting for NumVersionsToKeep (in Dgraph, we set it to infinity), you can use this |
| // method to indicate that all the older versions can be discarded and removed during compactions. |
| func (e *Entry) WithDiscard() *Entry { |
| e.meta = bitDiscardEarlierVersions |
| return e |
| } |
| |
| // WithTTL adds time to live duration to Entry e. Entry stored with a TTL would automatically expire |
| // after the time has elapsed, and will be eligible for garbage collection. |
| func (e *Entry) WithTTL(dur time.Duration) *Entry { |
| e.ExpiresAt = uint64(time.Now().Add(dur).Unix()) |
| return e |
| } |
| |
| // withMergeBit sets merge bit in entry's metadata. This |
| // function is called by MergeOperator's Add method. |
| func (e *Entry) withMergeBit() *Entry { |
| e.meta = bitMergeEntry |
| return e |
| } |