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  }