github.com/TeaOSLab/EdgeNode@v1.3.8/internal/utils/bfs/file_header.go (about)

     1  // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
     2  
     3  package bfs
     4  
     5  import (
     6  	"encoding/json"
     7  	"github.com/TeaOSLab/EdgeNode/internal/utils"
     8  	"sort"
     9  )
    10  
    11  type FileHeader struct {
    12  	Version         int         `json:"1,omitempty"`
    13  	ModifiedAt      int64       `json:"2,omitempty"`
    14  	ExpiresAt       int64       `json:"3,omitempty"`
    15  	Status          int         `json:"4,omitempty"`
    16  	HeaderSize      int64       `json:"5,omitempty"`
    17  	BodySize        int64       `json:"6,omitempty"`
    18  	ExpiredBodySize int64       `json:"7,omitempty"`
    19  	HeaderBlocks    []BlockInfo `json:"8,omitempty"`
    20  	BodyBlocks      []BlockInfo `json:"9,omitempty"`
    21  	IsCompleted     bool        `json:"10,omitempty"`
    22  	IsWriting       bool        `json:"11,omitempty"`
    23  }
    24  
    25  func (this *FileHeader) BlockAt(offset int64) (blockInfo BlockInfo, ok bool) {
    26  	var l = len(this.BodyBlocks)
    27  	if l == 1 {
    28  		if this.BodyBlocks[0].Contains(offset) {
    29  			return this.BodyBlocks[0], true
    30  		}
    31  		return
    32  	}
    33  
    34  	sort.Search(l, func(i int) bool {
    35  		if this.BodyBlocks[i].Contains(offset) {
    36  			blockInfo = this.BodyBlocks[i]
    37  			ok = true
    38  			return true
    39  		}
    40  		return this.BodyBlocks[i].OriginOffsetFrom > offset
    41  	})
    42  
    43  	return
    44  }
    45  
    46  func (this *FileHeader) MaxOffset() int64 {
    47  	var l = len(this.BodyBlocks)
    48  	if l > 0 {
    49  		return this.BodyBlocks[l-1].OriginOffsetTo
    50  	}
    51  	return 0
    52  }
    53  
    54  // Compact blocks
    55  func (this *FileHeader) Compact() {
    56  	this.compactHeader()
    57  	this.compactBody()
    58  }
    59  
    60  // compact header blocks
    61  func (this *FileHeader) compactHeader() {
    62  	var l = len(this.HeaderBlocks)
    63  	if l > 1 {
    64  		// 合并
    65  		var newBlocks []BlockInfo
    66  		var newIndex int
    67  		for index, currentBlock := range this.HeaderBlocks {
    68  			if index == 0 {
    69  				newBlocks = append(newBlocks, currentBlock)
    70  				newIndex++
    71  				continue
    72  			}
    73  
    74  			var lastBlock = newBlocks[newIndex-1]
    75  			if currentBlock.OriginOffsetFrom >= lastBlock.OriginOffsetFrom &&
    76  				currentBlock.OriginOffsetFrom <= /* MUST gte */ lastBlock.OriginOffsetTo &&
    77  				currentBlock.OriginOffsetFrom-lastBlock.OriginOffsetFrom == currentBlock.BFileOffsetFrom-lastBlock.BFileOffsetFrom /* 两侧距离一致 */ {
    78  				if currentBlock.OriginOffsetTo > lastBlock.OriginOffsetTo {
    79  					lastBlock.OriginOffsetTo = currentBlock.OriginOffsetTo
    80  					lastBlock.BFileOffsetTo = currentBlock.BFileOffsetTo
    81  					newBlocks[newIndex-1] = lastBlock
    82  				}
    83  			} else {
    84  				newBlocks = append(newBlocks, currentBlock)
    85  				newIndex++
    86  			}
    87  		}
    88  		this.HeaderBlocks = newBlocks
    89  	}
    90  }
    91  
    92  // sort and compact body blocks
    93  func (this *FileHeader) compactBody() {
    94  	var l = len(this.BodyBlocks)
    95  
    96  	if l > 0 {
    97  		if l > 1 {
    98  			// 排序
    99  			sort.Slice(this.BodyBlocks, func(i, j int) bool {
   100  				var block1 = this.BodyBlocks[i]
   101  				var block2 = this.BodyBlocks[j]
   102  				if block1.OriginOffsetFrom == block1.OriginOffsetFrom {
   103  					return block1.OriginOffsetTo < block2.OriginOffsetTo
   104  				}
   105  				return block1.OriginOffsetFrom < block2.OriginOffsetFrom
   106  			})
   107  
   108  			// 合并
   109  			var newBlocks []BlockInfo
   110  			var newIndex int
   111  			for index, currentBlock := range this.BodyBlocks {
   112  				if index == 0 {
   113  					newBlocks = append(newBlocks, currentBlock)
   114  					newIndex++
   115  					continue
   116  				}
   117  
   118  				var lastBlock = newBlocks[newIndex-1]
   119  				if currentBlock.OriginOffsetFrom >= lastBlock.OriginOffsetFrom &&
   120  					currentBlock.OriginOffsetFrom <= /* MUST gte */ lastBlock.OriginOffsetTo &&
   121  					currentBlock.OriginOffsetFrom-lastBlock.OriginOffsetFrom == currentBlock.BFileOffsetFrom-lastBlock.BFileOffsetFrom /* 两侧距离一致 */ {
   122  					if currentBlock.OriginOffsetTo > lastBlock.OriginOffsetTo {
   123  						lastBlock.OriginOffsetTo = currentBlock.OriginOffsetTo
   124  						lastBlock.BFileOffsetTo = currentBlock.BFileOffsetTo
   125  						newBlocks[newIndex-1] = lastBlock
   126  					}
   127  				} else {
   128  					newBlocks = append(newBlocks, currentBlock)
   129  					newIndex++
   130  				}
   131  			}
   132  			this.BodyBlocks = newBlocks
   133  			l = len(this.BodyBlocks)
   134  		}
   135  
   136  		// 检查是否已完成
   137  		var isCompleted = true
   138  		if this.BodyBlocks[0].OriginOffsetFrom != 0 || this.BodyBlocks[len(this.BodyBlocks)-1].OriginOffsetTo != this.BodySize {
   139  			isCompleted = false
   140  		} else {
   141  			for index, block := range this.BodyBlocks {
   142  				// 是否有不连续的
   143  				if index > 0 && block.OriginOffsetFrom > this.BodyBlocks[index-1].OriginOffsetTo {
   144  					isCompleted = false
   145  					break
   146  				}
   147  			}
   148  		}
   149  		this.IsCompleted = isCompleted
   150  	}
   151  }
   152  
   153  // Clone current header
   154  func (this *FileHeader) Clone() *FileHeader {
   155  	return &FileHeader{
   156  		Version:         this.Version,
   157  		ModifiedAt:      this.ModifiedAt,
   158  		ExpiresAt:       this.ExpiresAt,
   159  		Status:          this.Status,
   160  		HeaderSize:      this.HeaderSize,
   161  		BodySize:        this.BodySize,
   162  		ExpiredBodySize: this.ExpiredBodySize,
   163  		HeaderBlocks:    this.HeaderBlocks,
   164  		BodyBlocks:      this.BodyBlocks,
   165  		IsCompleted:     this.IsCompleted,
   166  		IsWriting:       this.IsWriting,
   167  	}
   168  }
   169  
   170  func (this *FileHeader) Encode(hash string) ([]byte, error) {
   171  	headerJSON, err := json.Marshal(this)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	// we do not compress data which size is less than 100 bytes
   177  	if len(headerJSON) < 100 {
   178  		return EncodeMetaBlock(MetaActionNew, hash, append([]byte("json:"), headerJSON...))
   179  	}
   180  
   181  	var buf = utils.SharedBufferPool.Get()
   182  	defer utils.SharedBufferPool.Put(buf)
   183  
   184  	compressor, err := SharedCompressPool.Get(buf)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	_, err = compressor.Write(headerJSON)
   190  	if err != nil {
   191  		_ = compressor.Close()
   192  		SharedCompressPool.Put(compressor)
   193  		return nil, err
   194  	}
   195  
   196  	err = compressor.Close()
   197  	SharedCompressPool.Put(compressor)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	return EncodeMetaBlock(MetaActionNew, hash, buf.Bytes())
   203  }