package segment
import (
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/polarsignals/wal/types"
)
type Reader struct {
info types .SegmentInfo
rf types .ReadableFile
scratchFrameHeader []byte
tail tailWriter
}
type tailWriter interface {
OffsetForFrame(idx uint64 ) (uint32 , error )
}
func openReader(info types .SegmentInfo , rf types .ReadableFile ) (*Reader , error ) {
r := &Reader {
info : info ,
rf : rf ,
}
return r , nil
}
func (r *Reader ) Close () error {
return r .rf .Close ()
}
func (r *Reader ) GetLog (idx uint64 , le *types .LogEntry ) error {
offset , err := r .findFrameOffset (idx )
if err != nil {
return err
}
if _ , err := r .readFrame (offset , le ); err != nil {
return err
}
return nil
}
func (r *Reader ) readFrame (offset uint32 , le *types .LogEntry ) (frameHeader , error ) {
if cap (r .scratchFrameHeader ) < frameHeaderLen {
r .scratchFrameHeader = make ([]byte , frameHeaderLen )
}
r .scratchFrameHeader = r .scratchFrameHeader [:frameHeaderLen ]
n , err := r .rf .ReadAt (r .scratchFrameHeader , int64 (offset ))
if errors .Is (err , io .EOF ) && n >= frameHeaderLen {
err = nil
le .Data = le .Data [:n ]
}
if err != nil {
return frameHeader {}, err
}
fh , err := readFrameHeader (r .scratchFrameHeader )
if err != nil {
return fh , err
}
if fh .len > MaxEntrySize {
return fh , fmt .Errorf ("%w: frame header indicates a record larger than MaxEntrySize (%d bytes)" , types .ErrCorrupt , MaxEntrySize )
}
if cap (le .Data ) < int (fh .len ) {
le .Data = make ([]byte , fh .len )
}
le .Data = le .Data [:fh .len ]
if _ , err := r .rf .ReadAt (le .Data , int64 (offset +frameHeaderLen )); err != nil {
return fh , err
}
return fh , nil
}
func (r *Reader ) findFrameOffset (idx uint64 ) (uint32 , error ) {
if r .tail != nil {
return r .tail .OffsetForFrame (idx )
}
if r .info .IndexStart == 0 {
return 0 , fmt .Errorf ("sealed segment has no index block" )
}
if idx < r .info .MinIndex || (r .info .MaxIndex > 0 && idx > r .info .MaxIndex ) {
return 0 , types .ErrNotFound
}
entryOffset := (idx - r .info .BaseIndex )
byteOffset := r .info .IndexStart + (entryOffset * 4 )
var bs [4 ]byte
n , err := r .rf .ReadAt (bs [:], int64 (byteOffset ))
if err == io .EOF && n == 4 {
err = nil
}
if err != nil {
return 0 , fmt .Errorf ("failed to read segment index: %w" , err )
}
offset := binary .LittleEndian .Uint32 (bs [:])
return offset , nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .