github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/writer_partial_file.go (about)

     1  // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
     2  
     3  package caches
     4  
     5  import (
     6  	"encoding/binary"
     7  	fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
     8  	"github.com/iwind/TeaGo/types"
     9  	"io"
    10  	"sync"
    11  )
    12  
    13  type PartialFileWriter struct {
    14  	rawWriter *fsutils.File
    15  	key       string
    16  
    17  	metaHeaderSize int
    18  	headerSize     int64
    19  
    20  	metaBodySize int64
    21  	bodySize     int64
    22  
    23  	expiredAt int64
    24  	endFunc   func()
    25  	once      sync.Once
    26  
    27  	isNew      bool
    28  	isPartial  bool
    29  	bodyOffset int64
    30  
    31  	ranges    *PartialRanges
    32  	rangePath string
    33  }
    34  
    35  func NewPartialFileWriter(rawWriter *fsutils.File, key string, expiredAt int64, metaHeaderSize int, metaBodySize int64, isNew bool, isPartial bool, bodyOffset int64, ranges *PartialRanges, endFunc func()) *PartialFileWriter {
    36  	return &PartialFileWriter{
    37  		key:            key,
    38  		rawWriter:      rawWriter,
    39  		expiredAt:      expiredAt,
    40  		endFunc:        endFunc,
    41  		isNew:          isNew,
    42  		isPartial:      isPartial,
    43  		bodyOffset:     bodyOffset,
    44  		ranges:         ranges,
    45  		rangePath:      PartialRangesFilePath(rawWriter.Name()),
    46  		metaHeaderSize: metaHeaderSize,
    47  		metaBodySize:   metaBodySize,
    48  	}
    49  }
    50  
    51  // WriteHeader 写入数据
    52  func (this *PartialFileWriter) WriteHeader(data []byte) (n int, err error) {
    53  	if !this.isNew {
    54  		return
    55  	}
    56  	n, err = this.rawWriter.Write(data)
    57  	this.headerSize += int64(n)
    58  	if err != nil {
    59  		_ = this.Discard()
    60  	}
    61  	return
    62  }
    63  
    64  func (this *PartialFileWriter) AppendHeader(data []byte) error {
    65  	_, err := this.rawWriter.Write(data)
    66  	if err != nil {
    67  		_ = this.Discard()
    68  	} else {
    69  		var c = len(data)
    70  		this.headerSize += int64(c)
    71  		err = this.WriteHeaderLength(int(this.headerSize))
    72  		if err != nil {
    73  			_ = this.Discard()
    74  		}
    75  	}
    76  	return err
    77  }
    78  
    79  // WriteHeaderLength 写入Header长度数据
    80  func (this *PartialFileWriter) WriteHeaderLength(headerLength int) error {
    81  	if this.metaHeaderSize > 0 && this.metaHeaderSize == headerLength {
    82  		return nil
    83  	}
    84  
    85  	var bytes4 = make([]byte, 4)
    86  	binary.BigEndian.PutUint32(bytes4, uint32(headerLength))
    87  	_, err := this.rawWriter.Seek(SizeExpiresAt+SizeStatus+SizeURLLength, io.SeekStart)
    88  	if err != nil {
    89  		_ = this.Discard()
    90  		return err
    91  	}
    92  	_, err = this.rawWriter.Write(bytes4)
    93  	if err != nil {
    94  		_ = this.Discard()
    95  		return err
    96  	}
    97  	return nil
    98  }
    99  
   100  // Write 写入数据
   101  func (this *PartialFileWriter) Write(data []byte) (n int, err error) {
   102  	n, err = this.rawWriter.Write(data)
   103  	this.bodySize += int64(n)
   104  	if err != nil {
   105  		_ = this.Discard()
   106  	}
   107  	return
   108  }
   109  
   110  // WriteAt 在指定位置写入数据
   111  func (this *PartialFileWriter) WriteAt(offset int64, data []byte) error {
   112  	var c = int64(len(data))
   113  	if c == 0 {
   114  		return nil
   115  	}
   116  	var end = offset + c - 1
   117  
   118  	// 是否已包含在内
   119  	if this.ranges.Contains(offset, end) {
   120  		return nil
   121  	}
   122  
   123  	// prevent extending too much space in a single writing
   124  	var maxOffset = this.ranges.Max()
   125  	if offset-maxOffset > 16<<20 {
   126  		var extendSizePerStep int64 = 1 << 20
   127  		var maxExtendSize int64 = 32 << 20
   128  		if fsutils.DiskIsExtremelyFast() {
   129  			maxExtendSize = 128 << 20
   130  			extendSizePerStep = 4 << 20
   131  		} else if fsutils.DiskIsFast() {
   132  			maxExtendSize = 64 << 20
   133  			extendSizePerStep = 2 << 20
   134  		}
   135  		if offset-maxOffset > maxExtendSize {
   136  			stat, err := this.rawWriter.Stat()
   137  			if err != nil {
   138  				return nil
   139  			}
   140  
   141  			// extend min size to prepare for file tail
   142  			if stat.Size()+extendSizePerStep <= this.bodyOffset+offset+int64(len(data)) {
   143  				_ = this.rawWriter.Truncate(stat.Size() + extendSizePerStep)
   144  				return nil
   145  			}
   146  		}
   147  	}
   148  
   149  	if this.bodyOffset == 0 {
   150  		var keyLength = 0
   151  		if this.ranges.Version == 0 { // 以往的版本包含有Key
   152  			keyLength = len(this.key)
   153  		}
   154  		this.bodyOffset = SizeMeta + int64(keyLength) + this.headerSize
   155  	}
   156  
   157  	_, err := this.rawWriter.WriteAt(data, this.bodyOffset+offset)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	this.ranges.Add(offset, end)
   163  
   164  	return nil
   165  }
   166  
   167  // SetBodyLength 设置内容总长度
   168  func (this *PartialFileWriter) SetBodyLength(bodyLength int64) {
   169  	this.bodySize = bodyLength
   170  }
   171  
   172  // WriteBodyLength 写入Body长度数据
   173  func (this *PartialFileWriter) WriteBodyLength(bodyLength int64) error {
   174  	if this.metaBodySize > 0 && this.metaBodySize == bodyLength {
   175  		return nil
   176  	}
   177  	var bytes8 = make([]byte, 8)
   178  	binary.BigEndian.PutUint64(bytes8, uint64(bodyLength))
   179  	_, err := this.rawWriter.Seek(SizeExpiresAt+SizeStatus+SizeURLLength+SizeHeaderLength, io.SeekStart)
   180  	if err != nil {
   181  		_ = this.Discard()
   182  		return err
   183  	}
   184  	_, err = this.rawWriter.Write(bytes8)
   185  	if err != nil {
   186  		_ = this.Discard()
   187  		return err
   188  	}
   189  	return nil
   190  }
   191  
   192  // Close 关闭
   193  func (this *PartialFileWriter) Close() error {
   194  	defer this.once.Do(func() {
   195  		this.endFunc()
   196  	})
   197  
   198  	this.ranges.BodySize = this.bodySize
   199  	err := this.ranges.WriteToFile(this.rangePath)
   200  	if err != nil {
   201  		_ = this.rawWriter.Close()
   202  		this.remove()
   203  		return err
   204  	}
   205  
   206  	// 关闭当前writer
   207  	if this.isNew {
   208  		err = this.WriteHeaderLength(types.Int(this.headerSize))
   209  		if err != nil {
   210  			_ = this.rawWriter.Close()
   211  			this.remove()
   212  			return err
   213  		}
   214  		err = this.WriteBodyLength(this.bodySize)
   215  		if err != nil {
   216  			_ = this.rawWriter.Close()
   217  			this.remove()
   218  			return err
   219  		}
   220  	}
   221  
   222  	err = this.rawWriter.Close()
   223  	if err != nil {
   224  		this.remove()
   225  	}
   226  
   227  	return err
   228  }
   229  
   230  // Discard 丢弃
   231  func (this *PartialFileWriter) Discard() error {
   232  	defer this.once.Do(func() {
   233  		this.endFunc()
   234  	})
   235  
   236  	_ = this.rawWriter.Close()
   237  
   238  	SharedPartialRangesQueue.Delete(this.rangePath)
   239  
   240  	_ = fsutils.Remove(this.rangePath)
   241  
   242  	err := fsutils.Remove(this.rawWriter.Name())
   243  
   244  	return err
   245  }
   246  
   247  func (this *PartialFileWriter) HeaderSize() int64 {
   248  	return this.headerSize
   249  }
   250  
   251  func (this *PartialFileWriter) BodySize() int64 {
   252  	return this.bodySize
   253  }
   254  
   255  func (this *PartialFileWriter) ExpiredAt() int64 {
   256  	return this.expiredAt
   257  }
   258  
   259  func (this *PartialFileWriter) Key() string {
   260  	return this.key
   261  }
   262  
   263  // ItemType 获取内容类型
   264  func (this *PartialFileWriter) ItemType() ItemType {
   265  	return ItemTypeFile
   266  }
   267  
   268  func (this *PartialFileWriter) IsNew() bool {
   269  	return this.isNew && len(this.ranges.Ranges) == 0
   270  }
   271  
   272  func (this *PartialFileWriter) remove() {
   273  	_ = fsutils.Remove(this.rawWriter.Name())
   274  
   275  	SharedPartialRangesQueue.Delete(this.rangePath)
   276  
   277  	_ = fsutils.Remove(this.rangePath)
   278  }