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 }