mirror of
https://github.com/taigrr/bitcask
synced 2025-01-18 04:03:17 -08:00
176 lines
2.6 KiB
Go
176 lines
2.6 KiB
Go
package internal
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/pkg/errors"
|
|
"golang.org/x/exp/mmap"
|
|
)
|
|
|
|
const (
|
|
DefaultDatafileFilename = "%09d.data"
|
|
)
|
|
|
|
var (
|
|
ErrReadonly = errors.New("error: read only datafile")
|
|
ErrReadError = errors.New("error: read error")
|
|
|
|
mxMemPool sync.RWMutex
|
|
)
|
|
|
|
type Datafile struct {
|
|
sync.RWMutex
|
|
|
|
id int
|
|
r *os.File
|
|
ra *mmap.ReaderAt
|
|
w *os.File
|
|
offset int64
|
|
dec *Decoder
|
|
enc *Encoder
|
|
}
|
|
|
|
func NewDatafile(path string, id int, readonly bool) (*Datafile, error) {
|
|
var (
|
|
r *os.File
|
|
ra *mmap.ReaderAt
|
|
w *os.File
|
|
err error
|
|
)
|
|
|
|
fn := filepath.Join(path, fmt.Sprintf(DefaultDatafileFilename, id))
|
|
|
|
if !readonly {
|
|
w, err = os.OpenFile(fn, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
r, err = os.Open(fn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stat, err := r.Stat()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error calling Stat()")
|
|
}
|
|
|
|
ra, err = mmap.Open(fn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
offset := stat.Size()
|
|
|
|
dec := NewDecoder(r)
|
|
enc := NewEncoder(w)
|
|
|
|
return &Datafile{
|
|
id: id,
|
|
r: r,
|
|
ra: ra,
|
|
w: w,
|
|
offset: offset,
|
|
dec: dec,
|
|
enc: enc,
|
|
}, nil
|
|
}
|
|
|
|
func (df *Datafile) FileID() int {
|
|
return df.id
|
|
}
|
|
|
|
func (df *Datafile) Name() string {
|
|
return df.r.Name()
|
|
}
|
|
|
|
func (df *Datafile) Close() error {
|
|
defer func() {
|
|
df.ra.Close()
|
|
df.r.Close()
|
|
}()
|
|
|
|
// Readonly Datafile -- Nothing further to close on the write side
|
|
if df.w == nil {
|
|
return nil
|
|
}
|
|
|
|
err := df.Sync()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return df.w.Close()
|
|
}
|
|
|
|
func (df *Datafile) Sync() error {
|
|
if df.w == nil {
|
|
return nil
|
|
}
|
|
return df.w.Sync()
|
|
}
|
|
|
|
func (df *Datafile) Size() int64 {
|
|
df.RLock()
|
|
defer df.RUnlock()
|
|
return df.offset
|
|
}
|
|
|
|
func (df *Datafile) Read() (e Entry, n int64, err error) {
|
|
df.Lock()
|
|
defer df.Unlock()
|
|
|
|
n, err = df.dec.Decode(&e)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (df *Datafile) ReadAt(index, size int64) (e Entry, err error) {
|
|
var n int
|
|
|
|
b := make([]byte, size)
|
|
|
|
if df.w == nil {
|
|
n, err = df.ra.ReadAt(b, index)
|
|
} else {
|
|
n, err = df.r.ReadAt(b, index)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
if int64(n) != size {
|
|
err = ErrReadError
|
|
return
|
|
}
|
|
|
|
valueOffset, _ := GetKeyValueSizes(b)
|
|
DecodeWithoutPrefix(b[KeySize+ValueSize:], valueOffset, &e)
|
|
|
|
return
|
|
}
|
|
|
|
func (df *Datafile) Write(e Entry) (int64, int64, error) {
|
|
if df.w == nil {
|
|
return -1, 0, ErrReadonly
|
|
}
|
|
|
|
df.Lock()
|
|
defer df.Unlock()
|
|
|
|
e.Offset = df.offset
|
|
|
|
n, err := df.enc.Encode(e)
|
|
if err != nil {
|
|
return -1, 0, err
|
|
}
|
|
df.offset += n
|
|
|
|
return e.Offset, n, nil
|
|
}
|