github.com/scottcagno/storage@v1.8.0/cmd/index/mockdb.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "log" 8 "math" 9 "os" 10 "path/filepath" 11 "sort" 12 "sync" 13 ) 14 15 const ( 16 maxKeySize = math.MaxUint8 17 maxValSize = math.MaxUint16 18 ) 19 20 var ( 21 ErrIncomplete = errors.New("key or value is incomplete or missing") 22 ErrTooLarge = errors.New( 23 fmt.Sprintf("key or value exceeded max size allowed (maxKey=%d, maxVal=%d)", maxKeySize, maxValSize)) 24 ErrNotFound = errors.New("entry not found") 25 26 IterSkip = errors.New("skipping this item") 27 IterStop = errors.New("breaking out and stopping iteration") 28 ) 29 30 type mockEntry struct { 31 k string 32 v string 33 } 34 35 func (me *mockEntry) check() error { 36 if me.k == "" || me.v == "" { 37 return ErrIncomplete 38 } 39 if len(me.k) > maxKeySize || len(me.v) > maxValSize { 40 return ErrTooLarge 41 } 42 return nil 43 } 44 45 func (me *mockEntry) String() string { 46 return fmt.Sprintf("{%q:%q}", me.k, me.v) 47 } 48 49 type mockIndex struct { 50 mdat map[string]int64 // mapped keys 51 odat []string // ordered keys 52 } 53 54 func makeMockIndex() *mockIndex { 55 return &mockIndex{ 56 mdat: make(map[string]int64), 57 odat: make([]string, 0), 58 } 59 } 60 61 func (mi *mockIndex) put(k string, off int64) { 62 // check if key is in map 63 if _, ok := mi.mdat[k]; !ok { 64 // add to ordered set if not in map 65 mi.odat = append(mi.odat, k) 66 if !sort.StringsAreSorted(mi.odat) { 67 sort.Strings(mi.odat) 68 } 69 } 70 // upsert into map 71 mi.mdat[k] = off 72 } 73 74 func (mi *mockIndex) get(k string) (int64, error) { 75 // check if key is in map 76 off, ok := mi.mdat[k] 77 if !ok { 78 // if not, return -1 79 return -1, ErrNotFound 80 } 81 return off, nil 82 } 83 84 func (mi *mockIndex) del(k string) { 85 // check if key is in map 86 if _, ok := mi.mdat[k]; !ok { 87 // if not, just return 88 return 89 } 90 // otherwise, delete from map 91 delete(mi.mdat, k) 92 // and delete from ordered set 93 deleteFromSlice(&mi.odat, k) 94 } 95 96 func (mi *mockIndex) scan(fn func(k string, off int64) error) { 97 for _, key := range mi.odat { 98 err := fn(key, mi.mdat[key]) 99 if err != nil { 100 if err == IterSkip { 101 continue 102 } 103 break 104 } 105 } 106 } 107 108 // deleteFromSliceV1 expects a sorted slice!!! It will FAIL HARD if it doesn't get one 109 func deleteFromSlice(a *[]string, s string) { 110 i := binarySearch(*a, s) 111 if (*a)[i] != s { 112 return // s was not found 113 } 114 *a = deleteAtI(*a, i) 115 } 116 117 // binarySearch expects a sorted slice!!! It will FAIL HARD if it doesn't get one 118 func binarySearch(a []string, x string) int { 119 i, j := 0, len(a) 120 for i < j { 121 h := int(uint(i+j) >> 1) 122 if !(a[h] >= x) { 123 i = h + 1 124 } else { 125 j = h 126 } 127 } 128 return i 129 } 130 131 func deleteAtI(a []string, i int) []string { 132 if i < len(a)-1 { 133 copy(a[i:], a[i+1:]) 134 } 135 a[len(a)-1] = "" 136 a = a[:len(a)-1] 137 return a 138 } 139 140 type mockDB struct { 141 lock sync.RWMutex 142 f *os.File 143 index *mockIndex 144 } 145 146 func OpenMockDB(base string) (*mockDB, error) { 147 dir, file := cleanPath(base) 148 f, err := openFile(filepath.Join(dir, file)) 149 if err != nil { 150 return nil, err 151 } 152 mdb := &mockDB{ 153 f: f, 154 } 155 err = mdb.loadIndex() 156 if err != nil { 157 return nil, err 158 } 159 return mdb, err 160 } 161 162 func (mdb *mockDB) loadIndex() error { 163 mdb.lock.Lock() 164 defer mdb.lock.Unlock() 165 if mdb.index == nil { 166 mdb.index = makeMockIndex() 167 } 168 for { 169 // get offset of entry 170 off, err := offset(mdb.f) 171 if err != nil { 172 return err 173 } 174 // read entry at offset 175 me, err := read(mdb.f) 176 if err != nil { 177 if err == io.EOF || err == io.ErrUnexpectedEOF { 178 break 179 } 180 return err 181 } 182 // read entry successfully, add to index 183 mdb.index.put(me.k, off) 184 } 185 return nil 186 } 187 188 func (mdb *mockDB) Scan(fn func(me *mockEntry) error) { 189 mdb.index.scan(func(k string, off int64) error { 190 me, err := readAt(mdb.f, off) 191 if err != nil { 192 return err 193 } 194 err = fn(me) 195 if err != nil { 196 return err 197 } 198 return nil 199 }) 200 } 201 202 func (mdb *mockDB) Get(k string) (string, error) { 203 mdb.lock.Lock() 204 defer mdb.lock.Unlock() 205 off, err := mdb.index.get(k) 206 if err != nil { 207 return "", err 208 } 209 me, err := readAt(mdb.f, off) 210 if err != nil { 211 return "", err 212 } 213 return me.v, nil 214 } 215 216 func (mdb *mockDB) Put(k string, v string) error { 217 mdb.lock.Lock() 218 defer mdb.lock.Unlock() 219 off, err := write(mdb.f, &mockEntry{k, v}) 220 if err != nil { 221 return err 222 } 223 mdb.index.put(k, off) 224 return nil 225 } 226 227 var tombstone = &mockEntry{"_TS", "_TS"} 228 229 func (mdb *mockDB) Del(k string) error { 230 mdb.lock.Lock() 231 defer mdb.lock.Unlock() 232 _, err := write(mdb.f, tombstone) 233 if err != nil { 234 return err 235 } 236 mdb.index.del(k) 237 return nil 238 } 239 240 func (mdb *mockDB) PutBatch(b *Batch) error { 241 mdb.lock.Lock() 242 defer mdb.lock.Unlock() 243 sort.Stable(b) 244 for _, me := range b.entries { 245 off, err := write(mdb.f, me) 246 if err != nil { 247 return err 248 } 249 mdb.index.put(me.k, off) 250 } 251 return nil 252 } 253 254 func (mdb *mockDB) Sync() error { 255 mdb.lock.Lock() 256 defer mdb.lock.Unlock() 257 err := mdb.f.Sync() 258 if err != nil { 259 return err 260 } 261 return nil 262 } 263 264 func (mdb *mockDB) Close() error { 265 mdb.lock.Lock() 266 defer mdb.lock.Unlock() 267 err := mdb.f.Sync() 268 if err != nil { 269 return err 270 } 271 err = mdb.f.Close() 272 if err != nil { 273 return err 274 } 275 return nil 276 } 277 278 func (mdb *mockDB) Count() int { 279 return len(mdb.index.odat) 280 } 281 282 type Batch struct { 283 entries []*mockEntry 284 } 285 286 func NewBatch() *Batch { 287 return &Batch{ 288 entries: make([]*mockEntry, 0), 289 } 290 } 291 292 func (b *Batch) Write(k string, v string) error { 293 me := &mockEntry{k: k, v: v} 294 err := me.check() 295 if err != nil { 296 return err 297 } 298 b.entries = append(b.entries, me) 299 return nil 300 } 301 302 func (b *Batch) Len() int { 303 return len(b.entries) 304 } 305 306 func (b *Batch) Less(i, j int) bool { 307 return b.entries[i].k < b.entries[j].k 308 } 309 310 func (b *Batch) Swap(i, j int) { 311 b.entries[i], b.entries[j] = b.entries[j], b.entries[i] 312 } 313 314 func getUint16(b []byte) uint16 { 315 _ = b[1] // early bounds check 316 return uint16(b[0]) | uint16(b[1])<<8 317 } 318 319 func putUint16(b []byte, v uint16) { 320 _ = b[1] // early bounds check 321 b[0] = byte(v) 322 b[1] = byte(v >> 8) 323 } 324 325 func read(r io.Reader) (*mockEntry, error) { 326 // make entry header 327 hdr := make([]byte, 3) 328 _, err := r.Read(hdr) 329 if err != nil { 330 return nil, err 331 } 332 // get key and value length 333 klen := hdr[0] 334 vlen := getUint16(hdr[1:]) 335 // read key 336 k := make([]byte, klen) 337 _, err = r.Read(k) 338 if err != nil { 339 return nil, err 340 } 341 // read value 342 v := make([]byte, vlen) 343 _, err = r.Read(v) 344 if err != nil { 345 return nil, err 346 } 347 // make and return entry 348 me := &mockEntry{ 349 k: string(k), 350 v: string(v), 351 } 352 return me, nil 353 } 354 355 func readAt(r io.ReaderAt, off int64) (*mockEntry, error) { 356 // make entry header 357 hdr := make([]byte, 3) 358 n, err := r.ReadAt(hdr, off) 359 if err != nil { 360 return nil, err 361 } 362 // update offset 363 off += int64(n) 364 // get key and value length 365 klen := hdr[0] 366 vlen := getUint16(hdr[1:]) 367 // read key 368 k := make([]byte, klen) 369 n, err = r.ReadAt(k, off) 370 if err != nil { 371 return nil, err 372 } 373 // update offset 374 off += int64(n) 375 // read value 376 v := make([]byte, vlen) 377 _, err = r.ReadAt(v, off) 378 if err != nil { 379 return nil, err 380 } 381 // update offset 382 off += int64(n) 383 // make and return entry 384 me := &mockEntry{ 385 k: string(k), 386 v: string(v), 387 } 388 return me, nil 389 } 390 391 func write(w io.WriteSeeker, me *mockEntry) (int64, error) { 392 // err check entry 393 err := me.check() 394 if err != nil { 395 return -1, err 396 } 397 // get the file pointer offset for the entry 398 off, err := offset(w) 399 if err != nil { 400 return -1, err 401 } 402 // make and encode entry header 403 hdr := make([]byte, 3) 404 hdr[0] = uint8(len(me.k)) 405 putUint16(hdr[1:], uint16(len(me.v))) 406 // write entry header 407 _, err = w.Write(hdr) 408 if err != nil { 409 return -1, err 410 } 411 // write entry key and value 412 _, err = w.Write([]byte(me.k + me.v)) 413 if err != nil { 414 return -1, err 415 } 416 // return offset 417 return off, nil 418 } 419 420 func offset(ws io.WriteSeeker) (int64, error) { 421 // get and return current offset 422 return ws.Seek(0, io.SeekCurrent) 423 } 424 425 func cleanPath(path string) (string, string) { 426 path, err := filepath.Abs(path) 427 if err != nil { 428 log.Panicf("cleaning path: %v\n", err) 429 } 430 return filepath.Split(filepath.ToSlash(path)) 431 } 432 433 func openFile(path string) (*os.File, error) { 434 _, err := os.Stat(path) 435 if os.IsNotExist(err) { 436 dir, file := filepath.Split(path) 437 err = os.MkdirAll(dir, os.ModeDir) 438 if err != nil { 439 return nil, err 440 } 441 fd, err := os.Create(dir + file) 442 if err != nil { 443 return nil, err 444 } 445 err = fd.Close() 446 if err != nil { 447 return fd, err 448 } 449 } 450 fd, err := os.OpenFile(path, os.O_RDWR, 0666) // os.ModeSticky 451 if err != nil { 452 return nil, err 453 } 454 return fd, nil 455 }