github.com/scottcagno/storage@v1.8.0/pkg/_junk/_lsmtree/sstable/index.go (about) 1 package sstable 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "path/filepath" 9 "strconv" 10 ) 11 12 func IndexFileNameFromIndex(index int64) string { 13 hexa := strconv.FormatInt(index, 16) 14 return fmt.Sprintf("%s%010s%s", filePrefix, hexa, indexFileSuffix) 15 } 16 17 func IndexFromIndexFileName(name string) (int64, error) { 18 hexa := name[len(filePrefix) : len(name)-len(indexFileSuffix)] 19 return strconv.ParseInt(hexa, 16, 32) 20 } 21 22 type sstIndexEntry struct { 23 key string // key is a key 24 offset int64 // offset is the offset of this data entry 25 } 26 27 func (i *sstIndexEntry) String() string { 28 return fmt.Sprintf("sstIndexEntry.key=%q, sstIndexEntry.offset=%d", i.key, i.offset) 29 } 30 31 type indexEntryMeta struct { 32 path string // filepath 33 data *sstIndexEntry // index entry 34 } 35 36 type indexEntryMetaSet []*indexEntryMeta 37 38 // Len [implementing sort interface] 39 func (ie indexEntryMetaSet) Len() int { 40 return len(ie) 41 } 42 43 // Less [implementing sort interface] 44 func (ie indexEntryMetaSet) Less(i, j int) bool { 45 return ie[i].data.key < ie[j].data.key 46 } 47 48 // Swap [implementing sort interface] 49 func (ie indexEntryMetaSet) Swap(i, j int) { 50 ie[i], ie[j] = ie[j], ie[i] 51 } 52 53 type SSIndex struct { 54 //lock sync.RWMutex 55 path string // base is the base path of the index file 56 file *os.File // file is the index file, file descriptor 57 open bool // reports if the file is open or closed 58 first string // first is the first key 59 last string // last is the last key 60 data []*sstIndexEntry // data is the sstIndexEntry 61 } 62 63 func OpenSSIndex(base string, index int64) (*SSIndex, error) { 64 // make sure we are working with absolute paths 65 base, err := filepath.Abs(base) 66 if err != nil { 67 return nil, err 68 } 69 // sanitize any path separators 70 base = filepath.ToSlash(base) 71 // create new index file path 72 path := filepath.Join(base, IndexFileNameFromIndex(index)) 73 // open (or create) index file 74 file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0666) 75 if err != nil { 76 return nil, err 77 } 78 // init and return SSIndex 79 ssi := &SSIndex{ 80 path: path, // path is the full path of the index file 81 file: file, // file is the index file, file descriptor 82 open: true, 83 } 84 // load sst data index info 85 err = ssi.LoadSSIndexData() 86 if err != nil { 87 return nil, err 88 } 89 return ssi, nil 90 } 91 92 func (ssi *SSIndex) LoadSSIndexData() error { 93 // check to make sure file exists 94 _, err := os.Stat(ssi.path) 95 if os.IsNotExist(err) { 96 return err 97 } 98 // open file to read header 99 fd, err := os.OpenFile(ssi.path, os.O_RDONLY, 0666) 100 if err != nil { 101 return err 102 } 103 // make sure we close! 104 defer fd.Close() 105 // read and decode index entries 106 for { 107 // decode next index entry 108 ei, err := DecodeIndexEntry(fd) 109 if err != nil { 110 if err == io.EOF || err == io.ErrUnexpectedEOF { 111 break 112 } 113 return err 114 } 115 // add index entry to sst index 116 ssi.data = append(ssi.data, ei) 117 } 118 // update sst first and last and then return 119 if len(ssi.data) > 0 { 120 ssi.first = ssi.data[0].key 121 ssi.last = ssi.data[len(ssi.data)-1].key 122 } 123 return nil 124 } 125 126 func (ssi *SSIndex) errorCheckFileAndIndex() error { 127 // make sure file is not closed 128 if !ssi.open { 129 return ErrFileClosed 130 } 131 // make sure index is loaded 132 if ssi.data == nil { 133 err := ssi.LoadSSIndexData() 134 if err != nil { 135 return err 136 } 137 } 138 return nil 139 } 140 141 func (ssi *SSIndex) WriteIndexEntry(key string, offset int64) error { 142 // error check 143 err := ssi.errorCheckFileAndIndex() 144 if err != nil { 145 return err 146 } 147 // create new index 148 ie := &sstIndexEntry{key: key, offset: offset} 149 // write entry info to index file 150 _, err = EncodeIndexEntry(ssi.file, ie) 151 if err != nil { 152 return err 153 } 154 // add to index 155 ssi.data = append(ssi.data, ie) 156 // check last 157 if ssi.last != ssi.lastKey() { 158 ssi.last = ssi.lastKey() 159 } 160 return nil 161 } 162 163 func (ssi *SSIndex) ReadDataEntry(r io.ReaderAt, key string) (*sstDataEntry, error) { 164 // error check 165 err := ssi.errorCheckFileAndIndex() 166 if err != nil { 167 return nil, err 168 } 169 // check index for entry offset 170 offset, err := ssi.GetEntryOffset(key) 171 if err != nil || offset == -1 { 172 return nil, err 173 } 174 // attempt to read and decode data entry using provided reader 175 de, err := DecodeDataEntryAt(r, offset) 176 if err != nil { 177 return nil, err 178 } 179 // return data entry 180 return de, nil 181 } 182 183 func (ssi *SSIndex) ReadDataEntryAt(r io.ReaderAt, offset int64) (*sstDataEntry, error) { 184 // error check 185 err := ssi.errorCheckFileAndIndex() 186 if err != nil { 187 return nil, err 188 } 189 // attempt to read and decode data entry using provided reader at provided offset 190 de, err := DecodeDataEntryAt(r, offset) 191 if err != nil { 192 return nil, err 193 } 194 // return data entry 195 return de, nil 196 } 197 198 func (ssi *SSIndex) searchDataIndex(key string) int { 199 // declare for later 200 i, j := 0, len(ssi.data) 201 // otherwise, perform binary search 202 for i < j { 203 h := i + (j-i)/2 204 if key >= ssi.data[h].key { 205 i = h + 1 206 } else { 207 j = h 208 } 209 } 210 return i - 1 211 } 212 213 func (ssi *SSIndex) GetEntryOffset(key string) (int64, error) { 214 // if data index is not loaded, then load it 215 if ssi.data == nil || len(ssi.data) == 0 { 216 err := ssi.LoadSSIndexData() 217 if err != nil { 218 return -1, err 219 } 220 } 221 222 // try binary search 223 in := ssi.searchDataIndex(key) 224 ki := ssi.data[in] 225 log.Printf("get offset for key=%q, offset=%d\n", key, ki.offset) 226 // double check we have a match 227 if ki.key == key { 228 return ki.offset, nil 229 } 230 // otherwise, we return not found 231 return -1, ErrIndexEntryNotFound 232 } 233 234 func (ssi *SSIndex) Scan(iter func(key string, offset int64) bool) { 235 for i := range ssi.data { 236 ie := ssi.data[i] 237 if !iter(ie.key, ie.offset) { 238 continue 239 } 240 } 241 } 242 243 func (ssi *SSIndex) lastKey() string { 244 return ssi.data[len(ssi.data)-1].key 245 } 246 247 func (ssi *SSIndex) Len() int { 248 return len(ssi.data) 249 } 250 251 func (ssi *SSIndex) Close() error { 252 if !ssi.open { 253 return nil 254 } 255 err := ssi.file.Sync() 256 if err != nil { 257 return err 258 } 259 err = ssi.file.Close() 260 if err != nil { 261 return err 262 } 263 ssi.open = false 264 return nil 265 }