github.com/nutsdb/nutsdb@v1.0.4/entry.go (about) 1 // Copyright 2019 The nutsdb Author. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nutsdb 16 17 import ( 18 "encoding/binary" 19 "errors" 20 "hash/crc32" 21 "sort" 22 "strings" 23 24 "github.com/xujiajun/utils/strconv2" 25 ) 26 27 var ( 28 ErrPayLoadSizeMismatch = errors.New("the payload size in Meta mismatch with the payload size needed") 29 ErrHeaderSizeOutOfBounds = errors.New("the header size is out of bounds") 30 ) 31 32 const ( 33 MaxEntryHeaderSize = 4 + binary.MaxVarintLen32*3 + binary.MaxVarintLen64*3 + binary.MaxVarintLen16*3 34 MinEntryHeaderSize = 4 + 9 35 ) 36 37 type ( 38 // Entry represents the data item. 39 Entry struct { 40 Key []byte 41 Value []byte 42 Meta *MetaData 43 } 44 ) 45 46 // Size returns the size of the entry. 47 func (e *Entry) Size() int64 { 48 return e.Meta.Size() + int64(e.Meta.KeySize+e.Meta.ValueSize) 49 } 50 51 // Encode returns the slice after the entry be encoded. 52 // 53 // the entry stored format: 54 // |----------------------------------------------------------------------------------------------------------| 55 // | crc | timestamp | ksz | valueSize | flag | TTL | status | ds | txId | bucketId | key | value | 56 // |----------------------------------------------------------------------------------------------------------| 57 // | uint32| uint64 |uint32 | uint32 | uint16 | uint32| uint16 | uint16 |uint64 | uint64 | []byte | []byte | 58 // |----------------------------------------------------------------------------------------------------------| 59 func (e *Entry) Encode() []byte { 60 keySize := e.Meta.KeySize 61 valueSize := e.Meta.ValueSize 62 63 buf := make([]byte, MaxEntryHeaderSize+keySize+valueSize) 64 65 index := e.setEntryHeaderBuf(buf) 66 copy(buf[index:], e.Key) 67 index += int(keySize) 68 copy(buf[index:], e.Value) 69 index += int(valueSize) 70 71 buf = buf[:index] 72 73 c32 := crc32.ChecksumIEEE(buf[4:]) 74 binary.LittleEndian.PutUint32(buf[0:4], c32) 75 76 return buf 77 } 78 79 // setEntryHeaderBuf sets the entry header buff. 80 func (e *Entry) setEntryHeaderBuf(buf []byte) int { 81 index := 4 82 83 index += binary.PutUvarint(buf[index:], e.Meta.Timestamp) 84 index += binary.PutUvarint(buf[index:], uint64(e.Meta.KeySize)) 85 index += binary.PutUvarint(buf[index:], uint64(e.Meta.ValueSize)) 86 index += binary.PutUvarint(buf[index:], uint64(e.Meta.Flag)) 87 index += binary.PutUvarint(buf[index:], uint64(e.Meta.TTL)) 88 index += binary.PutUvarint(buf[index:], uint64(e.Meta.Status)) 89 index += binary.PutUvarint(buf[index:], uint64(e.Meta.Ds)) 90 index += binary.PutUvarint(buf[index:], e.Meta.TxID) 91 index += binary.PutUvarint(buf[index:], e.Meta.BucketId) 92 93 return index 94 } 95 96 // IsZero checks if the entry is zero or not. 97 func (e *Entry) IsZero() bool { 98 if e.Meta.Crc == 0 && e.Meta.KeySize == 0 && e.Meta.ValueSize == 0 && e.Meta.Timestamp == 0 { 99 return true 100 } 101 return false 102 } 103 104 // GetCrc returns the crc at given buf slice. 105 func (e *Entry) GetCrc(buf []byte) uint32 { 106 crc := crc32.ChecksumIEEE(buf[4:]) 107 crc = crc32.Update(crc, crc32.IEEETable, e.Key) 108 crc = crc32.Update(crc, crc32.IEEETable, e.Value) 109 110 return crc 111 } 112 113 // ParsePayload means this function will parse a byte array to bucket, key, size of an entry 114 func (e *Entry) ParsePayload(data []byte) error { 115 meta := e.Meta 116 keyLowBound := 0 117 keyHighBound := meta.KeySize 118 valueLowBound := keyHighBound 119 valueHighBound := meta.KeySize + meta.ValueSize 120 121 // parse key 122 e.Key = data[keyLowBound:keyHighBound] 123 // parse value 124 e.Value = data[valueLowBound:valueHighBound] 125 return nil 126 } 127 128 // checkPayloadSize checks the payload size 129 func (e *Entry) checkPayloadSize(size int64) error { 130 if e.Meta.PayloadSize() != size { 131 return ErrPayLoadSizeMismatch 132 } 133 return nil 134 } 135 136 // ParseMeta parse Meta object to entry 137 func (e *Entry) ParseMeta(buf []byte) (int64, error) { 138 // If the length of the header is less than MinEntryHeaderSize, 139 // it means that the final remaining capacity of the file is not enough to write a record, 140 // and an error needs to be returned. 141 if len(buf) < MinEntryHeaderSize { 142 return 0, ErrHeaderSizeOutOfBounds 143 } 144 145 e.Meta = NewMetaData() 146 147 e.Meta.WithCrc(binary.LittleEndian.Uint32(buf[0:4])) 148 149 index := 4 150 151 timestamp, n := binary.Uvarint(buf[index:]) 152 index += n 153 keySize, n := binary.Uvarint(buf[index:]) 154 index += n 155 valueSize, n := binary.Uvarint(buf[index:]) 156 index += n 157 flag, n := binary.Uvarint(buf[index:]) 158 index += n 159 ttl, n := binary.Uvarint(buf[index:]) 160 index += n 161 status, n := binary.Uvarint(buf[index:]) 162 index += n 163 ds, n := binary.Uvarint(buf[index:]) 164 index += n 165 txId, n := binary.Uvarint(buf[index:]) 166 index += n 167 bucketId, n := binary.Uvarint(buf[index:]) 168 index += n 169 170 e.Meta. 171 WithTimeStamp(timestamp). 172 WithKeySize(uint32(keySize)). 173 WithValueSize(uint32(valueSize)). 174 WithFlag(uint16(flag)). 175 WithTTL(uint32(ttl)). 176 WithStatus(uint16(status)). 177 WithDs(uint16(ds)). 178 WithTxID(txId). 179 WithBucketId(bucketId) 180 181 return int64(index), nil 182 } 183 184 // isFilter to confirm if this entry is can be filtered 185 func (e *Entry) isFilter() bool { 186 meta := e.Meta 187 var filterDataSet = []uint16{ 188 DataDeleteFlag, 189 DataRPopFlag, 190 DataLPopFlag, 191 DataLRemFlag, 192 DataLTrimFlag, 193 DataZRemFlag, 194 DataZRemRangeByRankFlag, 195 DataZPopMaxFlag, 196 DataZPopMinFlag, 197 DataLRemByIndex, 198 } 199 return OneOfUint16Array(meta.Flag, filterDataSet) 200 } 201 202 // valid check the entry fields valid or not 203 func (e *Entry) valid() error { 204 if len(e.Key) == 0 { 205 return ErrKeyEmpty 206 } 207 if len(e.Key) > MAX_SIZE || len(e.Value) > MAX_SIZE { 208 return ErrDataSizeExceed 209 } 210 return nil 211 } 212 213 // NewEntry new Entry Object 214 func NewEntry() *Entry { 215 return new(Entry) 216 } 217 218 // WithKey set key to Entry 219 func (e *Entry) WithKey(key []byte) *Entry { 220 e.Key = key 221 return e 222 } 223 224 // WithValue set value to Entry 225 func (e *Entry) WithValue(value []byte) *Entry { 226 e.Value = value 227 return e 228 } 229 230 // WithMeta set Meta to Entry 231 func (e *Entry) WithMeta(meta *MetaData) *Entry { 232 e.Meta = meta 233 return e 234 } 235 236 // GetTxIDBytes return the bytes of TxID 237 func (e *Entry) GetTxIDBytes() []byte { 238 return []byte(strconv2.Int64ToStr(int64(e.Meta.TxID))) 239 } 240 241 func (e *Entry) IsBelongsToBPlusTree() bool { 242 return e.Meta.IsBPlusTree() 243 } 244 245 func (e *Entry) IsBelongsToList() bool { 246 return e.Meta.IsList() 247 } 248 249 func (e *Entry) IsBelongsToSet() bool { 250 return e.Meta.IsSet() 251 } 252 253 func (e *Entry) IsBelongsToSortSet() bool { 254 return e.Meta.IsSortSet() 255 } 256 257 // Entries represents entries 258 type Entries []*Entry 259 260 func (e Entries) Len() int { return len(e) } 261 262 func (e Entries) Less(i, j int) bool { 263 l := string(e[i].Key) 264 r := string(e[j].Key) 265 266 return strings.Compare(l, r) == -1 267 } 268 269 func (e Entries) Swap(i, j int) { e[i], e[j] = e[j], e[i] } 270 271 func (e Entries) processEntriesScanOnDisk() (result []*Entry) { 272 sort.Sort(e) 273 for _, ele := range e { 274 curE := ele 275 if !IsExpired(curE.Meta.TTL, curE.Meta.Timestamp) && curE.Meta.Flag != DataDeleteFlag { 276 result = append(result, curE) 277 } 278 } 279 280 return result 281 } 282 283 func (e Entries) ToCEntries(lFunc func(l, r string) bool) CEntries { 284 return CEntries{ 285 Entries: e, 286 LessFunc: lFunc, 287 } 288 } 289 290 type CEntries struct { 291 Entries 292 LessFunc func(l, r string) bool 293 } 294 295 func (c CEntries) Len() int { return len(c.Entries) } 296 297 func (c CEntries) Less(i, j int) bool { 298 l := string(c.Entries[i].Key) 299 r := string(c.Entries[j].Key) 300 if c.LessFunc != nil { 301 return c.LessFunc(l, r) 302 } 303 304 return c.Entries.Less(i, j) 305 } 306 307 func (c CEntries) Swap(i, j int) { c.Entries[i], c.Entries[j] = c.Entries[j], c.Entries[i] } 308 309 func (c CEntries) processEntriesScanOnDisk() (result []*Entry) { 310 sort.Sort(c) 311 for _, ele := range c.Entries { 312 curE := ele 313 if !IsExpired(curE.Meta.TTL, curE.Meta.Timestamp) && curE.Meta.Flag != DataDeleteFlag { 314 result = append(result, curE) 315 } 316 } 317 318 return result 319 } 320 321 type EntryWhenRecovery struct { 322 Entry 323 fid int64 324 off int64 325 } 326 327 type dataInTx struct { 328 es []*EntryWhenRecovery 329 txId uint64 330 startOff int64 331 } 332 333 func (dt *dataInTx) isSameTx(e *EntryWhenRecovery) bool { 334 return dt.txId == e.Meta.TxID 335 } 336 337 func (dt *dataInTx) appendEntry(e *EntryWhenRecovery) { 338 dt.es = append(dt.es, e) 339 } 340 341 func (dt *dataInTx) reset() { 342 dt.es = make([]*EntryWhenRecovery, 0) 343 dt.txId = 0 344 }