github.com/scottcagno/storage@v1.8.0/pkg/lsmtree/_blockfile.go (about) 1 package lsmtree 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "io" 8 "math" 9 "os" 10 "path/filepath" 11 ) 12 13 const ( 14 blockSize = 4096 15 recordHeaderSize = 16 16 maxRecordSize = blockSize * math.MaxUint16 17 ) 18 19 const ( 20 statusFree = uint16(asciiUnitSeparator >> 0 << 10) 21 statusActive = uint16(asciiUnitSeparator >> 1 << 10) 22 statusDeleted = uint16(asciiUnitSeparator >> 2 << 10) 23 ) 24 25 var ( 26 ErrBadOffsetAlignment = errors.New("bad offset; not correctly aligned") 27 ErrOffsetOutOfBounds = errors.New("bad offset; out of bounds") 28 ErrDataTooLarge = errors.New("data exceeds max record size") 29 ErrScannerSkip = errors.New("skip to next place with scanner/iterator") 30 ) 31 32 type blockFile struct { 33 path string // path is the current filepath 34 open bool // open reports true if the file is open 35 size int64 // size is the current size of the file 36 fp *os.File // fp is the file pointer 37 } 38 39 func OpenBlockFile(name string) (*blockFile, error) { 40 return openBlockFile(name) 41 } 42 43 func openBlockFile(name string) (*blockFile, error) { 44 // sanitize base path 45 path, err := filepath.Abs(name) 46 if err != nil { 47 return nil, err 48 } 49 // sanitize any path separators 50 path = filepath.ToSlash(path) 51 // get dir 52 dir, _ := filepath.Split(path) 53 // create any directories if they are not there 54 err = os.MkdirAll(dir, os.ModeDir) 55 if err != nil { 56 return nil, err 57 } 58 // open file 59 f, err := os.OpenFile(path, os.O_CREATE, 0666) 60 if err != nil { 61 return nil, err 62 } 63 // get file size 64 fi, err := f.Stat() 65 if err != nil { 66 return nil, err 67 } 68 // setup new blockFile 69 bf := &blockFile{ 70 path: path, 71 open: true, 72 size: fi.Size(), 73 fp: f, 74 } 75 // call init 76 err = bf.init() 77 if err != nil { 78 return nil, err 79 } 80 // return blockFile 81 return bf, nil 82 } 83 84 func (bf *blockFile) init() error { 85 return nil 86 } 87 88 func (bf *blockFile) Read() ([]byte, error) { 89 // error check 90 // allocate new record to read into 91 rd := new(recordData) 92 // read record 93 _, err := bf.readRecord(rd) 94 if err != nil { 95 return nil, err 96 } 97 // return record data 98 return rd.data, nil 99 } 100 101 func (bf *blockFile) ReadAt(off int64) ([]byte, error) { 102 // error check 103 // allocate new record to read into 104 rd := new(recordData) 105 // read record at 106 _, err := bf.readRecordAt(rd, off) 107 if err != nil { 108 return nil, err 109 } 110 // return record data 111 return rd.data, nil 112 } 113 114 func (bf *blockFile) Write(d []byte) (int, error) { 115 // error check 116 117 // make record 118 rd, err := makeRecord(d) 119 if err != nil { 120 return -1, err 121 } 122 // get record offset 123 off, err := currentOffset(bf.fp) 124 if err != nil { 125 return -1, err 126 } 127 // write record 128 _, err = bf.writeRecord(rd) 129 if err != nil { 130 return -1, err 131 } 132 // return offset of record 133 return int(off), nil 134 } 135 136 func (bf *blockFile) WriteAt(d []byte, off int64) (int, error) { 137 // error check 138 // make record 139 rd, err := makeRecord(d) 140 if err != nil { 141 return -1, err 142 } 143 // write record 144 _, err = bf.writeRecordAt(rd, off) 145 if err != nil { 146 return -1, err 147 } 148 // return bytes written 149 return int(off), nil 150 } 151 152 func (bf *blockFile) Seek(offset int64, whence int) (int64, error) { 153 off, err := bf.fp.Seek(offset, whence) 154 if err != nil { 155 return -1, err 156 } 157 return off, nil 158 } 159 160 type Record = recordData 161 162 func (bf *blockFile) Scan(fn func(rd *Record) error) error { 163 for { 164 // allocate new record to read into 165 rd := new(recordData) 166 // read record 167 _, err := bf.readRecord(rd) 168 if err != nil { 169 if err == io.EOF || err == io.ErrUnexpectedEOF { 170 break 171 } 172 return err 173 } 174 err = fn(rd) 175 if err != nil { 176 if err == ErrScannerSkip { 177 continue 178 } 179 return err 180 } 181 } 182 return nil 183 } 184 185 func (bf *blockFile) Sync() error { 186 err := bf.fp.Sync() 187 if err != nil { 188 return err 189 } 190 return nil 191 } 192 193 func (bf *blockFile) Close() error { 194 err := bf.fp.Sync() 195 if err != nil { 196 return err 197 } 198 err = bf.fp.Close() 199 if err != nil { 200 return err 201 } 202 return nil 203 } 204 205 func (bf *blockFile) readRecord(rd *recordData) (int, error) { 206 // read record data 207 n, err := rd.readData(bf.fp) 208 if err != nil { 209 return -1, err 210 } 211 // skip to the next alignment offset 212 _, err = bf.fp.Seek(int64(rd.padding), io.SeekCurrent) 213 if err != nil { 214 return -1, err 215 } 216 return n, nil 217 } 218 219 func (bf *blockFile) readRecordAt(rd *recordData, offset int64) (int, error) { 220 // read record data 221 n, err := rd.readDataAt(bf.fp, offset) 222 if err != nil { 223 return -1, err 224 } 225 return n, nil 226 } 227 228 func (bf *blockFile) writeRecord(rd *recordData) (int, error) { 229 // write record data 230 n, err := rd.writeData(bf.fp) 231 if err != nil { 232 return -1, err 233 } 234 // skip to the next alignment offset 235 _, err = bf.fp.Seek(int64(rd.padding), io.SeekCurrent) 236 if err != nil { 237 return -1, err 238 } 239 return n, nil 240 } 241 242 func (bf *blockFile) writeRecordAt(rd *recordData, offset int64) (int, error) { 243 // write record data 244 n, err := rd.writeDataAt(bf.fp, offset) 245 if err != nil { 246 return -1, err 247 } 248 return n, nil 249 } 250 251 func align(size int64) int64 { 252 if size > 0 { 253 return ((size + 2) + blockSize - 1) &^ (blockSize - 1) 254 } 255 return blockSize 256 } 257 258 func getOffset(pos int, max int64) (int64, error) { 259 // calculate the 260 offset := int64(pos * blockSize) 261 // return error if offset is not block aligned 262 if offset%blockSize != 0 { 263 return -1, ErrBadOffsetAlignment 264 } 265 // return error if offset is larger than max 266 if offset > max { 267 return -1, ErrOffsetOutOfBounds 268 } 269 // otherwise, return offset 270 return offset, nil 271 } 272 273 func currentOffset(w io.Seeker) (int64, error) { 274 // get current offset (of the beginning of this record) to return 275 off, err := w.Seek(0, io.SeekCurrent) 276 if err != nil { 277 return -1, err 278 } 279 // return err if offset is not block aligned 280 if off%blockSize != 0 { 281 return -1, ErrBadOffsetAlignment 282 } 283 // return offset and a nil error 284 return off, nil 285 } 286 287 type recordHeader struct { 288 status uint16 // max: 65535; status of the record 289 blocks uint16 // max: 65535; blocks occupied by the record data 290 length uint32 // max: 4294967295; length of the raw record data 291 padding uint32 // max: 4294967295; padding is the extra unused bytes in the block 292 magic uint32 // max: 4294967295; magic currently unused 293 } 294 295 func (rh *recordHeader) readHeader(r io.Reader) (int, error) { 296 // make buffer for record header 297 buf := make([]byte, 16) 298 // read in entire record header 299 n, err := r.Read(buf) 300 if err != nil { 301 return n, err 302 } 303 // decode status 304 rh.status = binary.LittleEndian.Uint16(buf[0:2]) 305 // decode blocks 306 rh.blocks = binary.LittleEndian.Uint16(buf[2:4]) 307 // decode length 308 rh.length = binary.LittleEndian.Uint32(buf[4:8]) 309 // decode padding 310 rh.padding = binary.LittleEndian.Uint32(buf[8:12]) 311 // decode magic 312 rh.magic = binary.LittleEndian.Uint32(buf[12:16]) 313 return n, nil 314 } 315 316 func (rh *recordHeader) readHeaderAt(r io.ReaderAt, offset int64) (int, error) { 317 // make buffer for record header 318 buf := make([]byte, 16) 319 // read in entire record header 320 n, err := r.ReadAt(buf, offset) 321 if err != nil { 322 return n, err 323 } 324 // decode status 325 rh.status = binary.LittleEndian.Uint16(buf[0:2]) 326 // decode blocks 327 rh.blocks = binary.LittleEndian.Uint16(buf[2:4]) 328 // decode length 329 rh.length = binary.LittleEndian.Uint32(buf[4:8]) 330 // decode padding 331 rh.padding = binary.LittleEndian.Uint32(buf[8:12]) 332 // decode extra2 333 rh.magic = binary.LittleEndian.Uint32(buf[12:16]) 334 return n, nil 335 } 336 337 func (rh *recordHeader) writeHeader(w io.Writer) (int, error) { 338 // make buffer to encode record header into 339 buf := make([]byte, 16) 340 // encode status 341 binary.LittleEndian.PutUint16(buf[0:2], rh.status) 342 // encode blocks 343 binary.LittleEndian.PutUint16(buf[2:4], rh.blocks) 344 // encode length 345 binary.LittleEndian.PutUint32(buf[4:8], rh.length) 346 // encode padding 347 binary.LittleEndian.PutUint32(buf[8:12], rh.padding) 348 // encode magic 349 binary.LittleEndian.PutUint32(buf[12:16], rh.magic) 350 // write record header 351 return w.Write(buf) 352 } 353 354 func (rh *recordHeader) writeHeaderAt(w io.WriterAt, offset int64) (int, error) { 355 // make buffer to encode record header into 356 buf := make([]byte, 16) 357 // encode status 358 binary.LittleEndian.PutUint16(buf[0:2], rh.status) 359 // encode blocks 360 binary.LittleEndian.PutUint16(buf[2:4], rh.blocks) 361 // encode length 362 binary.LittleEndian.PutUint32(buf[4:8], rh.length) 363 // encode padding 364 binary.LittleEndian.PutUint32(buf[8:12], rh.padding) 365 // encode magic 366 binary.LittleEndian.PutUint32(buf[12:16], rh.magic) 367 // write record header at 368 return w.WriteAt(buf, offset) 369 } 370 371 type recordData struct { 372 *recordHeader // record header 373 data []byte // raw record data 374 } 375 376 func (rd *recordData) String() string { 377 s := fmt.Sprintf("record:\n") 378 s += fmt.Sprintf("\theader:\n") 379 s += fmt.Sprintf("\t\tstatus: %d\n", rd.recordHeader.status) 380 s += fmt.Sprintf("\t\tblocks: %d\n", rd.recordHeader.blocks) 381 s += fmt.Sprintf("\t\tlength: %d\n", rd.recordHeader.length) 382 s += fmt.Sprintf("\t\tpadding: %d\n", rd.recordHeader.padding) 383 s += fmt.Sprintf("\t\tmagic: %d\n", rd.recordHeader.magic) 384 s += fmt.Sprintf("\tdata: %s\n", rd.data) 385 return s 386 } 387 388 func makeRecord(d []byte) (*recordData, error) { 389 // calc "overhead" 390 overhead := recordHeaderSize + int64(len(d)) 391 // get aligned size 392 size := align(overhead) 393 // error check 394 if size > maxRecordSize { 395 return nil, ErrDataTooLarge 396 } 397 // create record 398 rd := &recordData{ 399 recordHeader: &recordHeader{ 400 status: statusActive, 401 blocks: uint16(size / blockSize), 402 length: uint32(len(d)), 403 padding: uint32(size - overhead), 404 magic: uint32(0), 405 }, 406 data: d, 407 } 408 // return record 409 return rd, nil 410 } 411 412 func (rd *recordData) readData(r io.Reader) (int, error) { 413 // create record header 414 rh := new(recordHeader) 415 // read the record header 416 n, err := rh.readHeader(r) 417 if err != nil { 418 return -1, err 419 } 420 // init count 421 count := n 422 // fill out the record and allocate space to read in data 423 rd.recordHeader = rh 424 rd.data = make([]byte, rh.length) 425 // read data into the record 426 n, err = r.Read(rd.data) 427 if err != nil { 428 return -1, err 429 } 430 // update count 431 count += n 432 return count, nil 433 } 434 435 func (rd *recordData) readDataAt(r io.ReaderAt, offset int64) (int, error) { 436 // create record header 437 rh := new(recordHeader) 438 // read the record header 439 n, err := rh.readHeaderAt(r, offset) 440 if err != nil { 441 return -1, err 442 } 443 // update offset for next read 444 offset += int64(n) 445 // fill out the record and allocate space to read in data 446 rd.recordHeader = rh 447 rd.data = make([]byte, rh.length) 448 // read data into the record 449 n, err = r.ReadAt(rd.data, offset) 450 if err != nil { 451 return -1, err 452 } 453 // update offset to return 454 offset += int64(n) 455 return int(offset), nil 456 } 457 458 func (rd *recordData) writeData(w io.Writer) (int, error) { 459 // capture bytes written 460 var wrote int 461 // write the record header 462 n, err := rd.recordHeader.writeHeader(w) 463 if err != nil { 464 return -1, err 465 } 466 // update wrote 467 wrote += n 468 // write the record data 469 n, err = w.Write(rd.data) 470 if err != nil { 471 return -1, err 472 } 473 // update written 474 wrote += n 475 // return bytes written 476 return wrote, nil 477 } 478 479 func (rd *recordData) writeDataAt(w io.WriterAt, offset int64) (int, error) { 480 // capture bytes written 481 var wrote int 482 // write the record header 483 n, err := rd.recordHeader.writeHeaderAt(w, offset) 484 if err != nil { 485 return -1, err 486 } 487 // update wrote and offset 488 wrote += n 489 offset += int64(n) 490 // write the record data 491 n, err = w.WriteAt(rd.data, offset) 492 if err != nil { 493 return -1, err 494 } 495 // update wrote 496 wrote += n 497 // return bytes written 498 return wrote, nil 499 }