Files
Abstraction/vendor/github.com/akrylysov/pogreb/fs/os_mmap.go
2022-12-11 06:18:00 +00:00

164 lines
2.8 KiB
Go

package fs
import (
"io"
"os"
)
const (
initialMmapSize = 1024 << 20 // 1 GiB
)
type osMMapFS struct {
osFS
}
// OSMMap is a file system backed by the os package and memory-mapped files.
var OSMMap FileSystem = &osMMapFS{}
func (fs *osMMapFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
if flag&os.O_APPEND != 0 {
// osMMapFS doesn't support opening files in append-only mode.
// The database doesn't currently use O_APPEND.
return nil, errAppendModeNotSupported
}
f, err := os.OpenFile(name, flag, perm)
if err != nil {
return nil, err
}
stat, err := f.Stat()
if err != nil {
return nil, err
}
mf := &osMMapFile{
File: f,
size: stat.Size(),
}
if err := mf.mremap(); err != nil {
return nil, err
}
return mf, nil
}
type osMMapFile struct {
*os.File
data []byte
offset int64
size int64
mmapSize int64
}
func (f *osMMapFile) WriteAt(p []byte, off int64) (int, error) {
n, err := f.File.WriteAt(p, off)
if err != nil {
return 0, err
}
writeOff := off + int64(n)
if writeOff > f.size {
f.size = writeOff
}
return n, f.mremap()
}
func (f *osMMapFile) Write(p []byte) (int, error) {
n, err := f.File.Write(p)
if err != nil {
return 0, err
}
f.offset += int64(n)
if f.offset > f.size {
f.size = f.offset
}
return n, f.mremap()
}
func (f *osMMapFile) Seek(offset int64, whence int) (int64, error) {
off, err := f.File.Seek(offset, whence)
f.offset = off
return off, err
}
func (f *osMMapFile) Read(p []byte) (int, error) {
n, err := f.File.Read(p)
f.offset += int64(n)
return n, err
}
func (f *osMMapFile) Slice(start int64, end int64) ([]byte, error) {
if end > f.size {
return nil, io.EOF
}
if f.data == nil {
return nil, os.ErrClosed
}
return f.data[start:end], nil
}
func (f *osMMapFile) munmap() error {
if f.data == nil {
return nil
}
if err := munmap(f.data); err != nil {
return err
}
f.data = nil
f.mmapSize = 0
return nil
}
func (f *osMMapFile) mmap(fileSize int64, mappingSize int64) error {
if f.data != nil {
if err := munmap(f.data); err != nil {
return err
}
}
data, err := mmap(f.File, fileSize, mappingSize)
if err != nil {
return err
}
_ = madviceRandom(data)
f.data = data
return nil
}
func (f *osMMapFile) mremap() error {
mmapSize := f.mmapSize
if mmapSize >= f.size {
return nil
}
if mmapSize == 0 {
mmapSize = initialMmapSize
if mmapSize < f.size {
mmapSize = f.size
}
} else {
if err := f.munmap(); err != nil {
return err
}
mmapSize *= 2
}
if err := f.mmap(f.size, mmapSize); err != nil {
return err
}
// On Windows mmap may memory-map less than the requested size.
f.mmapSize = int64(len(f.data))
return nil
}
func (f *osMMapFile) Close() error {
if err := f.munmap(); err != nil {
return err
}
return f.File.Close()
}