mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-27 03:25:56 +08:00
320 lines
7.0 KiB
Go
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
|
|
}
|