Files
monibuca/pkg/storage/mmap.go
2025-09-26 11:12:44 +08:00

320 lines
7.0 KiB
Go

package storage
import (
"fmt"
"os"
"golang.org/x/exp/mmap"
)
// MmapFile 使用内存映射的文件实现
type MmapFile struct {
file *os.File
mmapFile *mmap.ReaderAt
data []byte
size int64
}
// NewMmapFile 创建新的内存映射文件
func NewMmapFile(filename string) (*MmapFile, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
mmapFile, err := mmap.Open(filename)
if err != nil {
file.Close()
return nil, fmt.Errorf("failed to mmap file: %w", err)
}
// 获取文件大小
stat, err := file.Stat()
if err != nil {
mmapFile.Close()
file.Close()
return nil, fmt.Errorf("failed to stat file: %w", err)
}
// 获取内存映射的数据
data := make([]byte, stat.Size())
_, err = mmapFile.ReadAt(data, 0)
if err != nil {
mmapFile.Close()
file.Close()
return nil, fmt.Errorf("failed to read mmap data: %w", err)
}
return &MmapFile{
file: file,
mmapFile: mmapFile,
data: data,
size: stat.Size(),
}, nil
}
// Read 实现 io.Reader 接口
func (m *MmapFile) Read(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
// 使用内存映射的数据进行零拷贝读取
n = copy(p, m.data)
if n == 0 {
return 0, fmt.Errorf("no data available")
}
// 更新数据指针,模拟读取进度
m.data = m.data[n:]
return n, nil
}
// ReadAt 实现 io.ReaderAt 接口
func (m *MmapFile) ReadAt(p []byte, off int64) (n int, err error) {
if off >= m.size {
return 0, fmt.Errorf("offset beyond file size")
}
// 使用内存映射的数据进行零拷贝读取
available := int(m.size - off)
if len(p) > available {
p = p[:available]
}
// 直接从内存映射区域复制数据,避免系统调用
start := int(off)
end := start + len(p)
if end > len(m.data) {
end = len(m.data)
}
n = copy(p, m.data[start:end])
return n, nil
}
// Write 实现 io.Writer 接口
func (m *MmapFile) Write(p []byte) (n int, err error) {
// 内存映射文件通常是只读的,这里返回错误
return 0, fmt.Errorf("mmap file is read-only")
}
// WriteAt 实现 io.WriterAt 接口
func (m *MmapFile) WriteAt(p []byte, off int64) (n int, err error) {
// 内存映射文件通常是只读的,这里返回错误
return 0, fmt.Errorf("mmap file is read-only")
}
// Seek 实现 io.Seeker 接口
func (m *MmapFile) Seek(offset int64, whence int) (int64, error) {
// 对于内存映射文件,我们通过调整数据指针来模拟 seek
switch whence {
case 0: // io.SeekStart
if offset < 0 || offset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[offset:]
return offset, nil
case 1: // io.SeekCurrent
current := m.size - int64(len(m.data))
newOffset := current + offset
if newOffset < 0 || newOffset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[offset:]
return newOffset, nil
case 2: // io.SeekEnd
newOffset := m.size + offset
if newOffset < 0 || newOffset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[newOffset:]
return newOffset, nil
default:
return 0, fmt.Errorf("invalid whence")
}
}
// Close 关闭文件
func (m *MmapFile) Close() error {
var err error
if m.mmapFile != nil {
err = m.mmapFile.Close()
}
if m.file != nil {
if closeErr := m.file.Close(); closeErr != nil && err == nil {
err = closeErr
}
}
return err
}
// Stat 返回文件信息
func (m *MmapFile) Stat() (os.FileInfo, error) {
return m.file.Stat()
}
// Name 返回文件名
func (m *MmapFile) Name() string {
return m.file.Name()
}
// Size 返回文件大小
func (m *MmapFile) Size() int64 {
return m.size
}
// Data 返回内存映射的数据切片(零拷贝访问)
func (m *MmapFile) Data() []byte {
return m.data
}
// MmapFileWriter 支持写入的内存映射文件
type MmapFileWriter struct {
file *os.File
filename string
data []byte
size int64
}
// NewMmapFileWriter 创建新的可写内存映射文件
func NewMmapFileWriter(filename string) (*MmapFileWriter, error) {
file, err := os.Create(filename)
if err != nil {
return nil, fmt.Errorf("failed to create file: %w", err)
}
return &MmapFileWriter{
file: file,
filename: filename,
data: make([]byte, 0),
size: 0,
}, nil
}
// Write 实现 io.Writer 接口
func (m *MmapFileWriter) Write(p []byte) (n int, err error) {
// 将数据追加到内存缓冲区
m.data = append(m.data, p...)
m.size += int64(len(p))
return len(p), nil
}
// WriteAt 实现 io.WriterAt 接口
func (m *MmapFileWriter) WriteAt(p []byte, off int64) (n int, err error) {
// 确保数据缓冲区足够大
if int64(len(m.data)) < off+int64(len(p)) {
newSize := off + int64(len(p))
newData := make([]byte, newSize)
copy(newData, m.data)
m.data = newData
m.size = newSize
}
// 写入数据到指定位置
copy(m.data[off:], p)
return len(p), nil
}
// Sync 同步数据到磁盘
func (m *MmapFileWriter) Sync() error {
// 将内存中的数据写入文件
_, err := m.file.WriteAt(m.data, 0)
if err != nil {
return fmt.Errorf("failed to write data: %w", err)
}
// 同步到磁盘
return m.file.Sync()
}
// Close 关闭文件
func (m *MmapFileWriter) Close() error {
// 先同步数据
if err := m.Sync(); err != nil {
m.file.Close()
return err
}
return m.file.Close()
}
// Read 实现 io.Reader 接口
func (m *MmapFileWriter) Read(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
n = copy(p, m.data)
if n == 0 {
return 0, fmt.Errorf("no data available")
}
// 更新数据指针
m.data = m.data[n:]
return n, nil
}
// ReadAt 实现 io.ReaderAt 接口
func (m *MmapFileWriter) ReadAt(p []byte, off int64) (n int, err error) {
if off >= m.size {
return 0, fmt.Errorf("offset beyond file size")
}
available := int(m.size - off)
if len(p) > available {
p = p[:available]
}
n = copy(p, m.data[off:])
return n, nil
}
// Seek 实现 io.Seeker 接口
func (m *MmapFileWriter) Seek(offset int64, whence int) (int64, error) {
switch whence {
case 0: // io.SeekStart
if offset < 0 || offset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[offset:]
return offset, nil
case 1: // io.SeekCurrent
current := m.size - int64(len(m.data))
newOffset := current + offset
if newOffset < 0 || newOffset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[offset:]
return newOffset, nil
case 2: // io.SeekEnd
newOffset := m.size + offset
if newOffset < 0 || newOffset > m.size {
return 0, fmt.Errorf("invalid offset")
}
m.data = m.data[newOffset:]
return newOffset, nil
default:
return 0, fmt.Errorf("invalid whence")
}
}
// Stat 返回文件信息
func (m *MmapFileWriter) Stat() (os.FileInfo, error) {
return m.file.Stat()
}
// Name 返回文件名
func (m *MmapFileWriter) Name() string {
return m.filename
}
// Size 返回文件大小
func (m *MmapFileWriter) Size() int64 {
return m.size
}
// Data 返回内存中的数据切片(零拷贝访问)
func (m *MmapFileWriter) Data() []byte {
return m.data
}