github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/swarm/storage/pyramid.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 // 10 // 11 // 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 25 package storage 26 27 import ( 28 "context" 29 "encoding/binary" 30 "errors" 31 "io" 32 "io/ioutil" 33 "sync" 34 "time" 35 36 "github.com/ethereum/go-ethereum/swarm/chunk" 37 "github.com/ethereum/go-ethereum/swarm/log" 38 ) 39 40 /* 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 */ 71 72 73 var ( 74 errLoadingTreeRootChunk = errors.New("LoadTree Error: Could not load root chunk") 75 errLoadingTreeChunk = errors.New("LoadTree Error: Could not load chunk") 76 ) 77 78 const ( 79 ChunkProcessors = 8 80 splitTimeout = time.Minute * 5 81 ) 82 83 const ( 84 DataChunk = 0 85 TreeChunk = 1 86 ) 87 88 type PyramidSplitterParams struct { 89 SplitterParams 90 getter Getter 91 } 92 93 func NewPyramidSplitterParams(addr Address, reader io.Reader, putter Putter, getter Getter, chunkSize int64) *PyramidSplitterParams { 94 hashSize := putter.RefSize() 95 return &PyramidSplitterParams{ 96 SplitterParams: SplitterParams{ 97 ChunkerParams: ChunkerParams{ 98 chunkSize: chunkSize, 99 hashSize: hashSize, 100 }, 101 reader: reader, 102 putter: putter, 103 addr: addr, 104 }, 105 getter: getter, 106 } 107 } 108 109 /* 110 111 112 */ 113 114 func PyramidSplit(ctx context.Context, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) { 115 return NewPyramidSplitter(NewPyramidSplitterParams(nil, reader, putter, getter, chunk.DefaultSize)).Split(ctx) 116 } 117 118 func PyramidAppend(ctx context.Context, addr Address, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) { 119 return NewPyramidSplitter(NewPyramidSplitterParams(addr, reader, putter, getter, chunk.DefaultSize)).Append(ctx) 120 } 121 122 // 123 type TreeEntry struct { 124 level int 125 branchCount int64 126 subtreeSize uint64 127 chunk []byte 128 key []byte 129 index int // 130 updatePending bool // 131 } 132 133 func NewTreeEntry(pyramid *PyramidChunker) *TreeEntry { 134 return &TreeEntry{ 135 level: 0, 136 branchCount: 0, 137 subtreeSize: 0, 138 chunk: make([]byte, pyramid.chunkSize+8), 139 key: make([]byte, pyramid.hashSize), 140 index: 0, 141 updatePending: false, 142 } 143 } 144 145 // 146 type chunkJob struct { 147 key Address 148 chunk []byte 149 parentWg *sync.WaitGroup 150 } 151 152 type PyramidChunker struct { 153 chunkSize int64 154 hashSize int64 155 branches int64 156 reader io.Reader 157 putter Putter 158 getter Getter 159 key Address 160 workerCount int64 161 workerLock sync.RWMutex 162 jobC chan *chunkJob 163 wg *sync.WaitGroup 164 errC chan error 165 quitC chan bool 166 rootKey []byte 167 chunkLevel [][]*TreeEntry 168 } 169 170 func NewPyramidSplitter(params *PyramidSplitterParams) (pc *PyramidChunker) { 171 pc = &PyramidChunker{} 172 pc.reader = params.reader 173 pc.hashSize = params.hashSize 174 pc.branches = params.chunkSize / pc.hashSize 175 pc.chunkSize = pc.hashSize * pc.branches 176 pc.putter = params.putter 177 pc.getter = params.getter 178 pc.key = params.addr 179 pc.workerCount = 0 180 pc.jobC = make(chan *chunkJob, 2*ChunkProcessors) 181 pc.wg = &sync.WaitGroup{} 182 pc.errC = make(chan error) 183 pc.quitC = make(chan bool) 184 pc.rootKey = make([]byte, pc.hashSize) 185 pc.chunkLevel = make([][]*TreeEntry, pc.branches) 186 return 187 } 188 189 func (pc *PyramidChunker) Join(addr Address, getter Getter, depth int) LazySectionReader { 190 return &LazyChunkReader{ 191 key: addr, 192 depth: depth, 193 chunkSize: pc.chunkSize, 194 branches: pc.branches, 195 hashSize: pc.hashSize, 196 getter: getter, 197 } 198 } 199 200 func (pc *PyramidChunker) incrementWorkerCount() { 201 pc.workerLock.Lock() 202 defer pc.workerLock.Unlock() 203 pc.workerCount += 1 204 } 205 206 func (pc *PyramidChunker) getWorkerCount() int64 { 207 pc.workerLock.Lock() 208 defer pc.workerLock.Unlock() 209 return pc.workerCount 210 } 211 212 func (pc *PyramidChunker) decrementWorkerCount() { 213 pc.workerLock.Lock() 214 defer pc.workerLock.Unlock() 215 pc.workerCount -= 1 216 } 217 218 func (pc *PyramidChunker) Split(ctx context.Context) (k Address, wait func(context.Context) error, err error) { 219 log.Debug("pyramid.chunker: Split()") 220 221 pc.wg.Add(1) 222 pc.prepareChunks(false) 223 224 // 225 go func() { 226 227 // 228 pc.wg.Wait() 229 230 // 231 // 232 // 233 close(pc.errC) 234 }() 235 236 defer close(pc.quitC) 237 defer pc.putter.Close() 238 239 select { 240 case err := <-pc.errC: 241 if err != nil { 242 return nil, nil, err 243 } 244 case <-time.NewTimer(splitTimeout).C: 245 } 246 return pc.rootKey, pc.putter.Wait, nil 247 248 } 249 250 func (pc *PyramidChunker) Append(ctx context.Context) (k Address, wait func(context.Context) error, err error) { 251 log.Debug("pyramid.chunker: Append()") 252 // 253 pc.loadTree() 254 255 pc.wg.Add(1) 256 pc.prepareChunks(true) 257 258 // 259 go func() { 260 261 // 262 pc.wg.Wait() 263 264 close(pc.errC) 265 }() 266 267 defer close(pc.quitC) 268 defer pc.putter.Close() 269 270 select { 271 case err := <-pc.errC: 272 if err != nil { 273 return nil, nil, err 274 } 275 case <-time.NewTimer(splitTimeout).C: 276 } 277 278 return pc.rootKey, pc.putter.Wait, nil 279 280 } 281 282 func (pc *PyramidChunker) processor(id int64) { 283 defer pc.decrementWorkerCount() 284 for { 285 select { 286 287 case job, ok := <-pc.jobC: 288 if !ok { 289 return 290 } 291 pc.processChunk(id, job) 292 case <-pc.quitC: 293 return 294 } 295 } 296 } 297 298 func (pc *PyramidChunker) processChunk(id int64, job *chunkJob) { 299 log.Debug("pyramid.chunker: processChunk()", "id", id) 300 301 ref, err := pc.putter.Put(context.TODO(), job.chunk) 302 if err != nil { 303 pc.errC <- err 304 } 305 306 // 307 copy(job.key, ref) 308 309 // 310 job.parentWg.Done() 311 } 312 313 func (pc *PyramidChunker) loadTree() error { 314 log.Debug("pyramid.chunker: loadTree()") 315 // 316 chunkData, err := pc.getter.Get(context.TODO(), Reference(pc.key)) 317 if err != nil { 318 return errLoadingTreeRootChunk 319 } 320 chunkSize := chunkData.Size() 321 log.Trace("pyramid.chunker: root chunk", "chunk.Size", chunkSize, "pc.chunkSize", pc.chunkSize) 322 323 // 324 if chunkSize <= pc.chunkSize { 325 newEntry := &TreeEntry{ 326 level: 0, 327 branchCount: 1, 328 subtreeSize: uint64(chunkSize), 329 chunk: make([]byte, pc.chunkSize+8), 330 key: make([]byte, pc.hashSize), 331 index: 0, 332 updatePending: true, 333 } 334 copy(newEntry.chunk[8:], pc.key) 335 pc.chunkLevel[0] = append(pc.chunkLevel[0], newEntry) 336 return nil 337 } 338 339 var treeSize int64 340 var depth int 341 treeSize = pc.chunkSize 342 for ; treeSize < chunkSize; treeSize *= pc.branches { 343 depth++ 344 } 345 log.Trace("pyramid.chunker", "depth", depth) 346 347 // 348 branchCount := int64(len(chunkData)-8) / pc.hashSize 349 newEntry := &TreeEntry{ 350 level: depth - 1, 351 branchCount: branchCount, 352 subtreeSize: uint64(chunkSize), 353 chunk: chunkData, 354 key: pc.key, 355 index: 0, 356 updatePending: true, 357 } 358 pc.chunkLevel[depth-1] = append(pc.chunkLevel[depth-1], newEntry) 359 360 // 361 for lvl := depth - 1; lvl >= 1; lvl-- { 362 363 // 364 // 365 for _, ent := range pc.chunkLevel[lvl] { 366 branchCount = int64(len(ent.chunk)-8) / pc.hashSize 367 for i := int64(0); i < branchCount; i++ { 368 key := ent.chunk[8+(i*pc.hashSize) : 8+((i+1)*pc.hashSize)] 369 newChunkData, err := pc.getter.Get(context.TODO(), Reference(key)) 370 if err != nil { 371 return errLoadingTreeChunk 372 } 373 newChunkSize := newChunkData.Size() 374 bewBranchCount := int64(len(newChunkData)-8) / pc.hashSize 375 newEntry := &TreeEntry{ 376 level: lvl - 1, 377 branchCount: bewBranchCount, 378 subtreeSize: uint64(newChunkSize), 379 chunk: newChunkData, 380 key: key, 381 index: 0, 382 updatePending: true, 383 } 384 pc.chunkLevel[lvl-1] = append(pc.chunkLevel[lvl-1], newEntry) 385 386 } 387 388 // 389 if int64(len(pc.chunkLevel[lvl-1])) >= pc.branches { 390 pc.chunkLevel[lvl-1] = nil 391 } 392 } 393 } 394 395 return nil 396 } 397 398 func (pc *PyramidChunker) prepareChunks(isAppend bool) { 399 log.Debug("pyramid.chunker: prepareChunks", "isAppend", isAppend) 400 defer pc.wg.Done() 401 402 chunkWG := &sync.WaitGroup{} 403 404 pc.incrementWorkerCount() 405 406 go pc.processor(pc.workerCount) 407 408 parent := NewTreeEntry(pc) 409 var unfinishedChunkData ChunkData 410 var unfinishedChunkSize int64 411 412 if isAppend && len(pc.chunkLevel[0]) != 0 { 413 lastIndex := len(pc.chunkLevel[0]) - 1 414 ent := pc.chunkLevel[0][lastIndex] 415 416 if ent.branchCount < pc.branches { 417 parent = &TreeEntry{ 418 level: 0, 419 branchCount: ent.branchCount, 420 subtreeSize: ent.subtreeSize, 421 chunk: ent.chunk, 422 key: ent.key, 423 index: lastIndex, 424 updatePending: true, 425 } 426 427 lastBranch := parent.branchCount - 1 428 lastKey := parent.chunk[8+lastBranch*pc.hashSize : 8+(lastBranch+1)*pc.hashSize] 429 430 var err error 431 unfinishedChunkData, err = pc.getter.Get(context.TODO(), lastKey) 432 if err != nil { 433 pc.errC <- err 434 } 435 unfinishedChunkSize = unfinishedChunkData.Size() 436 if unfinishedChunkSize < pc.chunkSize { 437 parent.subtreeSize = parent.subtreeSize - uint64(unfinishedChunkSize) 438 parent.branchCount = parent.branchCount - 1 439 } else { 440 unfinishedChunkData = nil 441 } 442 } 443 } 444 445 for index := 0; ; index++ { 446 var err error 447 chunkData := make([]byte, pc.chunkSize+8) 448 449 var readBytes int 450 451 if unfinishedChunkData != nil { 452 copy(chunkData, unfinishedChunkData) 453 readBytes += int(unfinishedChunkSize) 454 unfinishedChunkData = nil 455 log.Trace("pyramid.chunker: found unfinished chunk", "readBytes", readBytes) 456 } 457 458 var res []byte 459 res, err = ioutil.ReadAll(io.LimitReader(pc.reader, int64(len(chunkData)-(8+readBytes)))) 460 461 // 462 // 463 // 464 if len(res) == 0 && err == nil { 465 err = io.EOF 466 } 467 copy(chunkData[8+readBytes:], res) 468 469 readBytes += len(res) 470 log.Trace("pyramid.chunker: copied all data", "readBytes", readBytes) 471 472 if err != nil { 473 if err == io.EOF || err == io.ErrUnexpectedEOF { 474 475 pc.cleanChunkLevels() 476 477 // 478 if parent.branchCount == 1 && (pc.depth() == 0 || isAppend) { 479 // 480 chunkWG.Wait() 481 lastChunksKey := parent.chunk[8 : 8+pc.hashSize] 482 copy(pc.rootKey, lastChunksKey) 483 break 484 } 485 } else { 486 close(pc.quitC) 487 break 488 } 489 } 490 491 // 492 if readBytes == 0 { 493 pc.buildTree(isAppend, parent, chunkWG, true, nil) 494 break 495 } else { 496 pkey := pc.enqueueDataChunk(chunkData, uint64(readBytes), parent, chunkWG) 497 498 // 499 parent.subtreeSize += uint64(readBytes) 500 parent.branchCount++ 501 502 // 503 if int64(readBytes) < pc.chunkSize { 504 505 pc.cleanChunkLevels() 506 507 // 508 if parent.branchCount <= 1 { 509 chunkWG.Wait() 510 511 if isAppend || pc.depth() == 0 { 512 // 513 // 514 // 515 copy(pc.rootKey, pkey) 516 } else { 517 // 518 // 519 pc.buildTree(isAppend, parent, chunkWG, true, pkey) 520 } 521 break 522 } 523 524 pc.buildTree(isAppend, parent, chunkWG, true, nil) 525 break 526 } 527 528 if parent.branchCount == pc.branches { 529 pc.buildTree(isAppend, parent, chunkWG, false, nil) 530 parent = NewTreeEntry(pc) 531 } 532 533 } 534 535 workers := pc.getWorkerCount() 536 if int64(len(pc.jobC)) > workers && workers < ChunkProcessors { 537 pc.incrementWorkerCount() 538 go pc.processor(pc.workerCount) 539 } 540 541 } 542 543 } 544 545 func (pc *PyramidChunker) buildTree(isAppend bool, ent *TreeEntry, chunkWG *sync.WaitGroup, last bool, lonelyChunkKey []byte) { 546 chunkWG.Wait() 547 pc.enqueueTreeChunk(ent, chunkWG, last) 548 549 compress := false 550 endLvl := pc.branches 551 for lvl := int64(0); lvl < pc.branches; lvl++ { 552 lvlCount := int64(len(pc.chunkLevel[lvl])) 553 if lvlCount >= pc.branches { 554 endLvl = lvl + 1 555 compress = true 556 break 557 } 558 } 559 560 if !compress && !last { 561 return 562 } 563 564 // 565 chunkWG.Wait() 566 567 for lvl := int64(ent.level); lvl < endLvl; lvl++ { 568 569 lvlCount := int64(len(pc.chunkLevel[lvl])) 570 if lvlCount == 1 && last { 571 copy(pc.rootKey, pc.chunkLevel[lvl][0].key) 572 return 573 } 574 575 for startCount := int64(0); startCount < lvlCount; startCount += pc.branches { 576 577 endCount := startCount + pc.branches 578 if endCount > lvlCount { 579 endCount = lvlCount 580 } 581 582 var nextLvlCount int64 583 var tempEntry *TreeEntry 584 if len(pc.chunkLevel[lvl+1]) > 0 { 585 nextLvlCount = int64(len(pc.chunkLevel[lvl+1]) - 1) 586 tempEntry = pc.chunkLevel[lvl+1][nextLvlCount] 587 } 588 if isAppend && tempEntry != nil && tempEntry.updatePending { 589 updateEntry := &TreeEntry{ 590 level: int(lvl + 1), 591 branchCount: 0, 592 subtreeSize: 0, 593 chunk: make([]byte, pc.chunkSize+8), 594 key: make([]byte, pc.hashSize), 595 index: int(nextLvlCount), 596 updatePending: true, 597 } 598 for index := int64(0); index < lvlCount; index++ { 599 updateEntry.branchCount++ 600 updateEntry.subtreeSize += pc.chunkLevel[lvl][index].subtreeSize 601 copy(updateEntry.chunk[8+(index*pc.hashSize):8+((index+1)*pc.hashSize)], pc.chunkLevel[lvl][index].key[:pc.hashSize]) 602 } 603 604 pc.enqueueTreeChunk(updateEntry, chunkWG, last) 605 606 } else { 607 608 noOfBranches := endCount - startCount 609 newEntry := &TreeEntry{ 610 level: int(lvl + 1), 611 branchCount: noOfBranches, 612 subtreeSize: 0, 613 chunk: make([]byte, (noOfBranches*pc.hashSize)+8), 614 key: make([]byte, pc.hashSize), 615 index: int(nextLvlCount), 616 updatePending: false, 617 } 618 619 index := int64(0) 620 for i := startCount; i < endCount; i++ { 621 entry := pc.chunkLevel[lvl][i] 622 newEntry.subtreeSize += entry.subtreeSize 623 copy(newEntry.chunk[8+(index*pc.hashSize):8+((index+1)*pc.hashSize)], entry.key[:pc.hashSize]) 624 index++ 625 } 626 // 627 // 628 if lonelyChunkKey != nil { 629 // 630 copy(newEntry.chunk[int64(len(newEntry.chunk))-pc.hashSize:], lonelyChunkKey[:pc.hashSize]) 631 } 632 633 pc.enqueueTreeChunk(newEntry, chunkWG, last) 634 635 } 636 637 } 638 639 if !isAppend { 640 chunkWG.Wait() 641 if compress { 642 pc.chunkLevel[lvl] = nil 643 } 644 } 645 } 646 647 } 648 649 func (pc *PyramidChunker) enqueueTreeChunk(ent *TreeEntry, chunkWG *sync.WaitGroup, last bool) { 650 if ent != nil && ent.branchCount > 0 { 651 652 // 653 if last { 654 chunkWG.Wait() 655 } 656 657 binary.LittleEndian.PutUint64(ent.chunk[:8], ent.subtreeSize) 658 ent.key = make([]byte, pc.hashSize) 659 chunkWG.Add(1) 660 select { 661 case pc.jobC <- &chunkJob{ent.key, ent.chunk[:ent.branchCount*pc.hashSize+8], chunkWG}: 662 case <-pc.quitC: 663 } 664 665 // 666 if ent.updatePending { 667 chunkWG.Wait() 668 pc.chunkLevel[ent.level][ent.index] = ent 669 } else { 670 pc.chunkLevel[ent.level] = append(pc.chunkLevel[ent.level], ent) 671 } 672 673 } 674 } 675 676 func (pc *PyramidChunker) enqueueDataChunk(chunkData []byte, size uint64, parent *TreeEntry, chunkWG *sync.WaitGroup) Address { 677 binary.LittleEndian.PutUint64(chunkData[:8], size) 678 pkey := parent.chunk[8+parent.branchCount*pc.hashSize : 8+(parent.branchCount+1)*pc.hashSize] 679 680 chunkWG.Add(1) 681 select { 682 case pc.jobC <- &chunkJob{pkey, chunkData[:size+8], chunkWG}: 683 case <-pc.quitC: 684 } 685 686 return pkey 687 688 } 689 690 // 691 // 692 // 693 func (pc *PyramidChunker) depth() (d int) { 694 for _, l := range pc.chunkLevel { 695 if l == nil { 696 return 697 } 698 d++ 699 } 700 return 701 } 702 703 // 704 // 705 func (pc *PyramidChunker) cleanChunkLevels() { 706 for i, l := range pc.chunkLevel { 707 if l == nil { 708 pc.chunkLevel = append(pc.chunkLevel[:i], append(pc.chunkLevel[i+1:], nil)...) 709 } 710 } 711 }