github.com/scottcagno/storage@v1.8.0/pkg/lsmtree/entry.go (about) 1 package lsmtree 2 3 import ( 4 "bytes" 5 "fmt" 6 "hash/crc32" 7 "unsafe" 8 ) 9 10 // Index is a binary entry index 11 type Index struct { 12 Key []byte 13 Offset int64 14 } 15 16 // String is the stringer method for a *Index 17 func (i *Index) String() string { 18 return fmt.Sprintf("index.key=%q, index.offset=%d", i.Key, i.Offset) 19 } 20 21 // Tombstone is a marker for an entry that has been deleted 22 var Tombstone = []byte{0xDE, 0xAD, 0xBE, 0xEF} 23 24 func makeTombstone() []byte { 25 data := make([]byte, 4) 26 copy(data, Tombstone) 27 return data 28 } 29 30 // EntryHeader is mainly used for serialization 31 type EntryHeader struct { 32 klen uint32 33 vlen uint32 34 crc uint32 35 } 36 37 func (e *Entry) getEntryHeader() *EntryHeader { 38 return &EntryHeader{ 39 klen: uint32(len(e.Key)), 40 vlen: uint32(len(e.Value)), 41 crc: e.CRC, 42 } 43 } 44 45 // Entry represents a single entry or record 46 type Entry struct { 47 Key []byte 48 Value []byte 49 CRC uint32 50 } 51 52 func (e *Entry) hasTombstone() bool { 53 return bytes.Equal(e.Value, Tombstone) 54 } 55 56 // String is the stringer method for an *Entry 57 func (e *Entry) String() string { 58 return fmt.Sprintf("entry.Key=%q, entry.Value=%q, entry.CRC=%d", 59 e.Key, e.Value, e.CRC) 60 } 61 62 // Size returns the size in bytes for an entry 63 func (e *Entry) Size() int64 { 64 ks := int(unsafe.Sizeof(e.Key)) + len(e.Key) 65 vs := int(unsafe.Sizeof(e.Value)) + len(e.Value) 66 cs := unsafe.Sizeof(e.CRC) 67 return int64(ks) + int64(vs) + int64(cs) 68 } 69 70 // Batch is a set of entries 71 type Batch struct { 72 Entries []*Entry 73 size int64 74 } 75 76 // NewBatch instantiates a new batch of entries 77 func NewBatch() *Batch { 78 return &Batch{ 79 Entries: make([]*Entry, 0), 80 } 81 } 82 83 // Size returns the batch size in bytes 84 func (b *Batch) Size() int64 { 85 return b.size 86 } 87 88 // Write writes a new entry to the batch 89 func (b *Batch) Write(key, value []byte) error { 90 return b.writeEntry(&Entry{Key: key, Value: value}) 91 } 92 93 // writeEntry is the internal write implementation 94 func (b *Batch) writeEntry(e *Entry) error { 95 // make checksum for entry 96 e.CRC = checksum(append(e.Key, e.Value...)) 97 // check entry 98 err := checkEntry(e) 99 if err != nil { 100 return err 101 } 102 // add size 103 b.size += e.Size() 104 // write entry to batch 105 b.Entries = append(b.Entries, e) 106 // check size 107 if b.size >= defaultFlushThreshold { 108 // if batch has met or exceeded flush threshold 109 return ErrFlushThreshold 110 } 111 return nil 112 } 113 114 // Discard just vaporizes a batch 115 func (b *Batch) Discard() { 116 for i := range b.Entries { 117 b.Entries[i].Key = nil 118 b.Entries[i].Value = nil 119 } 120 b.Entries = nil 121 b.size = -1 122 b = nil 123 } 124 125 // Len [implementing sort interface] 126 func (b *Batch) Len() int { 127 return len(b.Entries) 128 } 129 130 // Less [implementing sort interface] 131 func (b *Batch) Less(i, j int) bool { 132 return bytes.Compare(b.Entries[i].Key, b.Entries[j].Key) == -1 133 } 134 135 // Swap [implementing sort interface] 136 func (b *Batch) Swap(i, j int) { 137 b.Entries[i], b.Entries[j] = b.Entries[j], b.Entries[i] 138 } 139 140 // checksum is the checksum calculator used with an entry 141 func checksum(data []byte) uint32 { 142 return crc32.Checksum(data, crc32.MakeTable(crc32.Koopman)) 143 } 144 145 // checkEntry ensures the entry does not violate the max key and value config 146 func checkEntry(e *Entry) error { 147 // init err 148 var err error 149 // key checks 150 err = checkKey(e) 151 if err != nil { 152 return err 153 } 154 // value checks 155 err = checkValue(e) 156 if err != nil { 157 return err 158 } 159 return nil 160 } 161 162 // checkKey checks the entry key size is okay 163 func checkKey(e *Entry) error { 164 if e.Key == nil || len(e.Key) < minKeySizeAllowed { 165 return ErrBadKey 166 } 167 if int64(len(e.Key)) > maxKeySizeAllowed { 168 return ErrKeyTooLarge 169 } 170 return nil 171 } 172 173 // checkValue checks the entry value size is okay 174 func checkValue(e *Entry) error { 175 if e.Value == nil || len(e.Value) < minValueSizeAllowed { 176 return ErrBadValue 177 } 178 if int64(len(e.Value)) > maxValueSizeAllowed { 179 return ErrValueTooLarge 180 } 181 return nil 182 } 183 184 // checkCRC verifies the crc32 checksum is correct 185 func checkCRC(e *Entry, crc uint32) error { 186 if e.CRC != crc { 187 return ErrBadChecksum 188 } 189 return nil 190 }