1.4.5.2
This commit is contained in:
421
EdgeNode/internal/caches/reader_file_mmap_plus.go
Normal file
421
EdgeNode/internal/caches/reader_file_mmap_plus.go
Normal file
@@ -0,0 +1,421 @@
|
||||
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package caches
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||
memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils/mmap"
|
||||
rangeutils "github.com/TeaOSLab/EdgeNode/internal/utils/ranges"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 设置最大能映射的文件尺寸
|
||||
var maxMMAPFileSize int64 = 8 << 20
|
||||
|
||||
func init() {
|
||||
var estimatedSize = int64((memutils.SystemMemoryGB() * 4) << 20)
|
||||
if estimatedSize > maxMMAPFileSize {
|
||||
maxMMAPFileSize = estimatedSize
|
||||
}
|
||||
}
|
||||
|
||||
// MMAPFileReader 通过MMAP读取文件
|
||||
type MMAPFileReader struct {
|
||||
BaseReader
|
||||
|
||||
rawReader *mmap.SharedMMAP
|
||||
path string
|
||||
modifiedAt int64
|
||||
|
||||
meta []byte
|
||||
header []byte
|
||||
|
||||
expiresAt int64
|
||||
status int
|
||||
headerOffset int64
|
||||
headerSize int
|
||||
bodySize int64
|
||||
bodyOffset int64
|
||||
|
||||
currentOffset int64
|
||||
|
||||
isClosed bool
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func NewMMAPFileReaderFromPath(filename string) (*MMAPFileReader, error) {
|
||||
reader, err := mmap.OpenSharedMMAP(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewMMAPFileReader(reader)
|
||||
}
|
||||
|
||||
func NewMMAPFileReader(mmapReader *mmap.SharedMMAP) (*MMAPFileReader, error) {
|
||||
return &MMAPFileReader{
|
||||
path: mmapReader.Name(),
|
||||
rawReader: mmapReader,
|
||||
modifiedAt: mmapReader.Stat().ModTime().Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) Init() error {
|
||||
return this.InitAutoDiscard(true)
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) InitAutoDiscard(autoDiscard bool) error {
|
||||
var isOk = false
|
||||
|
||||
if autoDiscard {
|
||||
defer func() {
|
||||
if !isOk {
|
||||
_ = this.discard()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var buf = this.meta
|
||||
if len(buf) == 0 {
|
||||
buf = make([]byte, SizeMeta)
|
||||
ok, err := this.readNext(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
this.meta = buf
|
||||
}
|
||||
|
||||
this.expiresAt = int64(binary.BigEndian.Uint32(buf[:SizeExpiresAt]))
|
||||
|
||||
var status = types.Int(string(buf[OffsetStatus : OffsetStatus+SizeStatus]))
|
||||
if status < 100 || status > 999 {
|
||||
return errors.New("invalid status")
|
||||
}
|
||||
this.status = status
|
||||
|
||||
// URL
|
||||
var urlLength = binary.BigEndian.Uint32(buf[OffsetURLLength : OffsetURLLength+SizeURLLength])
|
||||
|
||||
// header
|
||||
var headerSize = int(binary.BigEndian.Uint32(buf[OffsetHeaderLength : OffsetHeaderLength+SizeHeaderLength]))
|
||||
if headerSize == 0 {
|
||||
return nil
|
||||
}
|
||||
this.headerSize = headerSize
|
||||
this.headerOffset = int64(SizeMeta) + int64(urlLength)
|
||||
|
||||
// body
|
||||
this.bodyOffset = this.headerOffset + int64(headerSize)
|
||||
var bodySize = int(binary.BigEndian.Uint64(buf[OffsetBodyLength : OffsetBodyLength+SizeBodyLength]))
|
||||
if bodySize == 0 {
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
this.bodySize = int64(bodySize)
|
||||
|
||||
isOk = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) TypeName() string {
|
||||
return "disk"
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) ExpiresAt() int64 {
|
||||
return this.expiresAt
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) Status() int {
|
||||
return this.status
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) LastModified() int64 {
|
||||
if this.modifiedAt > 0 {
|
||||
return this.modifiedAt
|
||||
}
|
||||
stat, err := os.Stat(this.path)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return stat.ModTime().Unix()
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) HeaderSize() int64 {
|
||||
return int64(this.headerSize)
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) BodySize() int64 {
|
||||
return this.bodySize
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) ReadHeader(buf []byte, callback ReaderFunc) error {
|
||||
// 使用缓存
|
||||
if len(this.header) > 0 && len(buf) >= len(this.header) {
|
||||
copy(buf, this.header)
|
||||
_, err := callback(len(this.header))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 移动到Body位置
|
||||
this.moveTo(this.bodyOffset)
|
||||
return nil
|
||||
}
|
||||
|
||||
var isOk = false
|
||||
|
||||
defer func() {
|
||||
if !isOk {
|
||||
_ = this.discard()
|
||||
}
|
||||
}()
|
||||
|
||||
this.moveTo(this.headerOffset)
|
||||
|
||||
var headerSize = this.headerSize
|
||||
|
||||
if len(buf) > headerSize {
|
||||
n, err := this.rawReader.ReadAt(buf[:headerSize], this.headerOffset)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = callback(n)
|
||||
if err != nil {
|
||||
isOk = true
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
for {
|
||||
n, err := this.read(buf)
|
||||
if n > 0 {
|
||||
if n < headerSize {
|
||||
goNext, e := callback(n)
|
||||
if e != nil {
|
||||
isOk = true
|
||||
return e
|
||||
}
|
||||
if !goNext {
|
||||
break
|
||||
}
|
||||
headerSize -= n
|
||||
} else {
|
||||
_, e := callback(headerSize)
|
||||
if e != nil {
|
||||
isOk = true
|
||||
return e
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isOk = true
|
||||
|
||||
// 移动到Body位置
|
||||
this.moveTo(this.bodyOffset)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) ReadBody(buf []byte, callback ReaderFunc) error {
|
||||
if this.bodySize == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var isOk = false
|
||||
|
||||
defer func() {
|
||||
if !isOk {
|
||||
_ = this.discard()
|
||||
}
|
||||
}()
|
||||
|
||||
var offset = this.bodyOffset
|
||||
|
||||
// 开始读Body部分
|
||||
this.moveTo(offset)
|
||||
|
||||
for {
|
||||
n, err := this.read(buf)
|
||||
if n > 0 {
|
||||
goNext, e := callback(n)
|
||||
if e != nil {
|
||||
isOk = true
|
||||
return e
|
||||
}
|
||||
if !goNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
isOk = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) Read(buf []byte) (n int, err error) {
|
||||
if this.bodySize == 0 {
|
||||
n = 0
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
|
||||
n, err = this.read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
_ = this.discard()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) ReadBodyRange(buf []byte, start int64, end int64, callback ReaderFunc) error {
|
||||
var isOk = false
|
||||
|
||||
defer func() {
|
||||
if !isOk {
|
||||
_ = this.discard()
|
||||
}
|
||||
}()
|
||||
|
||||
var offset = start
|
||||
if start < 0 {
|
||||
offset = this.bodyOffset + this.bodySize + end
|
||||
end = this.bodyOffset + this.bodySize - 1
|
||||
} else if end < 0 {
|
||||
offset = this.bodyOffset + start
|
||||
end = this.bodyOffset + this.bodySize - 1
|
||||
} else {
|
||||
offset = this.bodyOffset + start
|
||||
end = this.bodyOffset + end
|
||||
}
|
||||
if offset < 0 || end < 0 || offset > end {
|
||||
isOk = true
|
||||
return ErrInvalidRange
|
||||
}
|
||||
|
||||
this.moveTo(offset)
|
||||
|
||||
for {
|
||||
n, err := this.read(buf)
|
||||
if n > 0 {
|
||||
var n2 = int(end-offset) + 1
|
||||
if n2 <= n {
|
||||
_, e := callback(n2)
|
||||
if e != nil {
|
||||
isOk = true
|
||||
return e
|
||||
}
|
||||
break
|
||||
} else {
|
||||
goNext, e := callback(n)
|
||||
if e != nil {
|
||||
isOk = true
|
||||
return e
|
||||
}
|
||||
if !goNext {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
offset += int64(n)
|
||||
if offset > end {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
isOk = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainsRange 是否包含某些区间内容
|
||||
func (this *MMAPFileReader) ContainsRange(r rangeutils.Range) (r2 rangeutils.Range, ok bool) {
|
||||
return r, true
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) CopyBodyTo(writer io.Writer) (int, error) {
|
||||
return this.rawReader.Write(writer, int(this.bodyOffset))
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) Close() error {
|
||||
if this.isClosed {
|
||||
return nil
|
||||
}
|
||||
this.isClosed = true
|
||||
|
||||
var err error
|
||||
this.once.Do(func() {
|
||||
err = this.rawReader.Close()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) moveTo(offset int64) {
|
||||
this.currentOffset = offset
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) readNext(buf []byte) (ok bool, err error) {
|
||||
n, err := this.rawReader.ReadAt(buf, this.currentOffset)
|
||||
if n > 0 {
|
||||
this.currentOffset += int64(n)
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ok = n == len(buf)
|
||||
return
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) read(p []byte) (n int, err error) {
|
||||
n, err = this.rawReader.ReadAt(p, this.currentOffset)
|
||||
if n > 0 {
|
||||
this.currentOffset += int64(n)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (this *MMAPFileReader) discard() error {
|
||||
this.once.Do(func() {
|
||||
_ = this.rawReader.Close()
|
||||
})
|
||||
|
||||
this.isClosed = true
|
||||
|
||||
// remove file
|
||||
return fsutils.Remove(this.path)
|
||||
}
|
||||
Reference in New Issue
Block a user