github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/soliton/mvmap/mvmap.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package mvmap 15 16 import ( 17 "bytes" 18 ) 19 20 type entry struct { 21 addr dataAddr 22 keyLen uint32 23 valLen uint32 24 next entryAddr 25 } 26 27 type entryStore struct { 28 slices [][]entry 29 sliceIdx uint32 30 sliceLen uint32 31 } 32 33 type dataStore struct { 34 slices [][]byte 35 sliceIdx uint32 36 sliceLen uint32 37 } 38 39 type entryAddr struct { 40 sliceIdx uint32 41 offset uint32 42 } 43 44 type dataAddr struct { 45 sliceIdx uint32 46 offset uint32 47 } 48 49 const ( 50 maxDataSliceLen = 64 * 1024 51 maxEntrySliceLen = 8 * 1024 52 ) 53 54 func (ds *dataStore) put(key, value []byte) dataAddr { 55 dataLen := uint32(len(key) + len(value)) 56 if ds.sliceLen != 0 && ds.sliceLen+dataLen > maxDataSliceLen { 57 ds.slices = append(ds.slices, make([]byte, 0, max(maxDataSliceLen, int(dataLen)))) 58 ds.sliceLen = 0 59 ds.sliceIdx++ 60 } 61 addr := dataAddr{sliceIdx: ds.sliceIdx, offset: ds.sliceLen} 62 slice := ds.slices[ds.sliceIdx] 63 slice = append(slice, key...) 64 slice = append(slice, value...) 65 ds.slices[ds.sliceIdx] = slice 66 ds.sliceLen += dataLen 67 return addr 68 } 69 70 func max(a, b int) int { 71 if a > b { 72 return a 73 } 74 return b 75 } 76 77 func (ds *dataStore) get(e entry, key []byte) []byte { 78 slice := ds.slices[e.addr.sliceIdx] 79 valOffset := e.addr.offset + e.keyLen 80 if !bytes.Equal(key, slice[e.addr.offset:valOffset]) { 81 return nil 82 } 83 return slice[valOffset : valOffset+e.valLen] 84 } 85 86 func (ds *dataStore) getEntryData(e entry) (key, value []byte) { 87 slice := ds.slices[e.addr.sliceIdx] 88 keyOffset := e.addr.offset 89 key = slice[keyOffset : keyOffset+e.keyLen] 90 valOffset := e.addr.offset + e.keyLen 91 value = slice[valOffset : valOffset+e.valLen] 92 return 93 } 94 95 var nullEntryAddr = entryAddr{} 96 97 func (es *entryStore) put(e entry) entryAddr { 98 if es.sliceLen == maxEntrySliceLen { 99 es.slices = append(es.slices, make([]entry, 0, maxEntrySliceLen)) 100 es.sliceLen = 0 101 es.sliceIdx++ 102 } 103 addr := entryAddr{sliceIdx: es.sliceIdx, offset: es.sliceLen} 104 slice := es.slices[es.sliceIdx] 105 slice = append(slice, e) 106 es.slices[es.sliceIdx] = slice 107 es.sliceLen++ 108 return addr 109 } 110 111 func (es *entryStore) get(addr entryAddr) entry { 112 return es.slices[addr.sliceIdx][addr.offset] 113 } 114 115 // MVMap stores multiple value for a given key with minimum GC overhead. 116 // A given key can causetstore multiple values. 117 // It is not thread-safe, should only be used in one goroutine. 118 type MVMap struct { 119 entryStore entryStore 120 dataStore dataStore 121 hashBlock map[uint64]entryAddr 122 length int 123 } 124 125 // NewMVMap creates a new multi-value map. 126 func NewMVMap() *MVMap { 127 m := new(MVMap) 128 m.hashBlock = make(map[uint64]entryAddr) 129 m.entryStore.slices = [][]entry{make([]entry, 0, 64)} 130 // Append the first empty entry, so the zero entryAddr can represent null. 131 m.entryStore.put(entry{}) 132 m.dataStore.slices = [][]byte{make([]byte, 0, 1024)} 133 return m 134 } 135 136 // Put puts the key/value pairs to the MVMap, if the key already exists, old value will not be overwritten, 137 // values are stored in a list. 138 func (m *MVMap) Put(key, value []byte) { 139 hashKey := fnvHash64(key) 140 oldEntryAddr := m.hashBlock[hashKey] 141 dataAddr := m.dataStore.put(key, value) 142 e := entry{ 143 addr: dataAddr, 144 keyLen: uint32(len(key)), 145 valLen: uint32(len(value)), 146 next: oldEntryAddr, 147 } 148 newEntryAddr := m.entryStore.put(e) 149 m.hashBlock[hashKey] = newEntryAddr 150 m.length++ 151 } 152 153 // Get gets the values of the "key" and appends them to "values". 154 func (m *MVMap) Get(key []byte, values [][]byte) [][]byte { 155 hashKey := fnvHash64(key) 156 entryAddr := m.hashBlock[hashKey] 157 for entryAddr != nullEntryAddr { 158 e := m.entryStore.get(entryAddr) 159 entryAddr = e.next 160 val := m.dataStore.get(e, key) 161 if val == nil { 162 continue 163 } 164 values = append(values, val) 165 } 166 // Keep the order of input. 167 for i := 0; i < len(values)/2; i++ { 168 j := len(values) - 1 - i 169 values[i], values[j] = values[j], values[i] 170 } 171 return values 172 } 173 174 // Len returns the number of values in th mv map, the number of keys may be less than Len 175 // if the same key is put more than once. 176 func (m *MVMap) Len() int { 177 return m.length 178 } 179 180 // Iterator is used to iterate the MVMap. 181 type Iterator struct { 182 m *MVMap 183 sliceCur int 184 entryCur int 185 } 186 187 // Next returns the next key/value pair of the MVMap. 188 // It returns (nil, nil) when there is no more entries to iterate. 189 func (i *Iterator) Next() (key, value []byte) { 190 for { 191 if i.sliceCur >= len(i.m.entryStore.slices) { 192 return nil, nil 193 } 194 entrySlice := i.m.entryStore.slices[i.sliceCur] 195 if i.entryCur >= len(entrySlice) { 196 i.sliceCur++ 197 i.entryCur = 0 198 continue 199 } 200 entry := entrySlice[i.entryCur] 201 key, value = i.m.dataStore.getEntryData(entry) 202 i.entryCur++ 203 return 204 } 205 } 206 207 // NewIterator creates a iterator for the MVMap. 208 func (m *MVMap) NewIterator() *Iterator { 209 // The first entry is empty, so init entryCur to 1. 210 return &Iterator{m: m, entryCur: 1} 211 }