github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/storage/pyramid.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:45</date>
    10  //</624450121341341696>
    11  
    12  
    13  package storage
    14  
    15  import (
    16  	"context"
    17  	"encoding/binary"
    18  	"errors"
    19  	"io"
    20  	"io/ioutil"
    21  	"sync"
    22  	"time"
    23  
    24  	ch "github.com/ethereum/go-ethereum/swarm/chunk"
    25  	"github.com/ethereum/go-ethereum/swarm/log"
    26  )
    27  
    28  /*
    29     金字塔块的主要思想是在不知道整个大小的前提下处理输入数据。
    30     为了实现这一点,chunker树是从地面建立的,直到数据耗尽。
    31     这就打开了新的Aveneus,比如容易附加和对树进行其他类型的修改,从而避免了
    32     重复数据块。
    33  
    34  
    35     下面是一个两级块树的例子。叶块称为数据块,以上都称为
    36     块称为树块。数据块上方的树块为0级,以此类推,直到达到
    37     根目录树块。
    38  
    39  
    40  
    41                                              t10<-树块Lvl1
    42                                              γ
    43                    _uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    44                   /_
    45                  /\ \
    46              _uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    47             //\//\//\////\
    48            //\//\//\////\
    49           D1 D2…d128 d1 d2…d128 d1 d2…d128 d1 d2…D128<-数据块
    50  
    51  
    52      split函数连续读取数据并创建数据块并将其发送到存储器。
    53      当创建一定数量的数据块(默认分支)时,会发送一个信号来创建树。
    54      条目。当0级树条目达到某个阈值(默认分支)时,另一个信号
    55      发送到一级以上的树条目。等等…直到只有一个数据用尽
    56      树条目存在于某个级别。树条目的键作为文件的根地址给出。
    57  
    58  **/
    59  
    60  
    61  var (
    62  	errLoadingTreeRootChunk = errors.New("LoadTree Error: Could not load root chunk")
    63  	errLoadingTreeChunk     = errors.New("LoadTree Error: Could not load chunk")
    64  )
    65  
    66  const (
    67  	ChunkProcessors = 8
    68  	splitTimeout    = time.Minute * 5
    69  )
    70  
    71  type PyramidSplitterParams struct {
    72  	SplitterParams
    73  	getter Getter
    74  }
    75  
    76  func NewPyramidSplitterParams(addr Address, reader io.Reader, putter Putter, getter Getter, chunkSize int64) *PyramidSplitterParams {
    77  	hashSize := putter.RefSize()
    78  	return &PyramidSplitterParams{
    79  		SplitterParams: SplitterParams{
    80  			ChunkerParams: ChunkerParams{
    81  				chunkSize: chunkSize,
    82  				hashSize:  hashSize,
    83  			},
    84  			reader: reader,
    85  			putter: putter,
    86  			addr:   addr,
    87  		},
    88  		getter: getter,
    89  	}
    90  }
    91  
    92  /*
    93   拆分时,数据作为一个节阅读器提供,键是一个hashsize长字节片(地址),一旦处理完成,整个内容的根散列将填充此内容。
    94   要存储的新块是使用调用方提供的推杆存储的。
    95  **/
    96  
    97  func PyramidSplit(ctx context.Context, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) {
    98  	return NewPyramidSplitter(NewPyramidSplitterParams(nil, reader, putter, getter, ch.DefaultSize)).Split(ctx)
    99  }
   100  
   101  func PyramidAppend(ctx context.Context, addr Address, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) {
   102  	return NewPyramidSplitter(NewPyramidSplitterParams(addr, reader, putter, getter, ch.DefaultSize)).Append(ctx)
   103  }
   104  
   105  //创建树节点的条目
   106  type TreeEntry struct {
   107  	level         int
   108  	branchCount   int64
   109  	subtreeSize   uint64
   110  	chunk         []byte
   111  	key           []byte
   112  index         int  //在append中用于指示现有树条目的索引
   113  updatePending bool //指示是否从现有树加载项
   114  }
   115  
   116  func NewTreeEntry(pyramid *PyramidChunker) *TreeEntry {
   117  	return &TreeEntry{
   118  		level:         0,
   119  		branchCount:   0,
   120  		subtreeSize:   0,
   121  		chunk:         make([]byte, pyramid.chunkSize+8),
   122  		key:           make([]byte, pyramid.hashSize),
   123  		index:         0,
   124  		updatePending: false,
   125  	}
   126  }
   127  
   128  //哈希处理器用于创建数据/树块并发送到存储
   129  type chunkJob struct {
   130  	key      Address
   131  	chunk    []byte
   132  	parentWg *sync.WaitGroup
   133  }
   134  
   135  type PyramidChunker struct {
   136  	chunkSize   int64
   137  	hashSize    int64
   138  	branches    int64
   139  	reader      io.Reader
   140  	putter      Putter
   141  	getter      Getter
   142  	key         Address
   143  	workerCount int64
   144  	workerLock  sync.RWMutex
   145  	jobC        chan *chunkJob
   146  	wg          *sync.WaitGroup
   147  	errC        chan error
   148  	quitC       chan bool
   149  	rootAddress []byte
   150  	chunkLevel  [][]*TreeEntry
   151  }
   152  
   153  func NewPyramidSplitter(params *PyramidSplitterParams) (pc *PyramidChunker) {
   154  	pc = &PyramidChunker{}
   155  	pc.reader = params.reader
   156  	pc.hashSize = params.hashSize
   157  	pc.branches = params.chunkSize / pc.hashSize
   158  	pc.chunkSize = pc.hashSize * pc.branches
   159  	pc.putter = params.putter
   160  	pc.getter = params.getter
   161  	pc.key = params.addr
   162  	pc.workerCount = 0
   163  	pc.jobC = make(chan *chunkJob, 2*ChunkProcessors)
   164  	pc.wg = &sync.WaitGroup{}
   165  	pc.errC = make(chan error)
   166  	pc.quitC = make(chan bool)
   167  	pc.rootAddress = make([]byte, pc.hashSize)
   168  	pc.chunkLevel = make([][]*TreeEntry, pc.branches)
   169  	return
   170  }
   171  
   172  func (pc *PyramidChunker) Join(addr Address, getter Getter, depth int) LazySectionReader {
   173  	return &LazyChunkReader{
   174  		addr:      addr,
   175  		depth:     depth,
   176  		chunkSize: pc.chunkSize,
   177  		branches:  pc.branches,
   178  		hashSize:  pc.hashSize,
   179  		getter:    getter,
   180  	}
   181  }
   182  
   183  func (pc *PyramidChunker) incrementWorkerCount() {
   184  	pc.workerLock.Lock()
   185  	defer pc.workerLock.Unlock()
   186  	pc.workerCount += 1
   187  }
   188  
   189  func (pc *PyramidChunker) getWorkerCount() int64 {
   190  	pc.workerLock.Lock()
   191  	defer pc.workerLock.Unlock()
   192  	return pc.workerCount
   193  }
   194  
   195  func (pc *PyramidChunker) decrementWorkerCount() {
   196  	pc.workerLock.Lock()
   197  	defer pc.workerLock.Unlock()
   198  	pc.workerCount -= 1
   199  }
   200  
   201  func (pc *PyramidChunker) Split(ctx context.Context) (k Address, wait func(context.Context) error, err error) {
   202  	log.Debug("pyramid.chunker: Split()")
   203  
   204  	pc.wg.Add(1)
   205  	pc.prepareChunks(ctx, false)
   206  
   207  //如果工作组中的所有子进程都已完成,则关闭内部错误通道
   208  	go func() {
   209  
   210  //等待所有块完成
   211  		pc.wg.Wait()
   212  
   213  //我们在这里关闭errc,因为它被传递到下面的8个并行例程中。
   214  //如果其中一个发生错误…那个特定的程序会引起错误…
   215  //一旦它们都成功完成,控制权就回来了,我们可以在这里安全地关闭它。
   216  		close(pc.errC)
   217  	}()
   218  
   219  	defer close(pc.quitC)
   220  	defer pc.putter.Close()
   221  
   222  	select {
   223  	case err := <-pc.errC:
   224  		if err != nil {
   225  			return nil, nil, err
   226  		}
   227  	case <-ctx.Done():
   228  _ = pc.putter.Wait(ctx) //????
   229  		return nil, nil, ctx.Err()
   230  	}
   231  	return pc.rootAddress, pc.putter.Wait, nil
   232  
   233  }
   234  
   235  func (pc *PyramidChunker) Append(ctx context.Context) (k Address, wait func(context.Context) error, err error) {
   236  	log.Debug("pyramid.chunker: Append()")
   237  //加载每个级别中最右侧的未完成树块
   238  	pc.loadTree(ctx)
   239  
   240  	pc.wg.Add(1)
   241  	pc.prepareChunks(ctx, true)
   242  
   243  //如果工作组中的所有子进程都已完成,则关闭内部错误通道
   244  	go func() {
   245  
   246  //等待所有块完成
   247  		pc.wg.Wait()
   248  
   249  		close(pc.errC)
   250  	}()
   251  
   252  	defer close(pc.quitC)
   253  	defer pc.putter.Close()
   254  
   255  	select {
   256  	case err := <-pc.errC:
   257  		if err != nil {
   258  			return nil, nil, err
   259  		}
   260  	case <-time.NewTimer(splitTimeout).C:
   261  	}
   262  
   263  	return pc.rootAddress, pc.putter.Wait, nil
   264  
   265  }
   266  
   267  func (pc *PyramidChunker) processor(ctx context.Context, id int64) {
   268  	defer pc.decrementWorkerCount()
   269  	for {
   270  		select {
   271  
   272  		case job, ok := <-pc.jobC:
   273  			if !ok {
   274  				return
   275  			}
   276  			pc.processChunk(ctx, id, job)
   277  		case <-pc.quitC:
   278  			return
   279  		}
   280  	}
   281  }
   282  
   283  func (pc *PyramidChunker) processChunk(ctx context.Context, id int64, job *chunkJob) {
   284  	log.Debug("pyramid.chunker: processChunk()", "id", id)
   285  
   286  	ref, err := pc.putter.Put(ctx, job.chunk)
   287  	if err != nil {
   288  		select {
   289  		case pc.errC <- err:
   290  		case <-pc.quitC:
   291  		}
   292  	}
   293  
   294  //向上一级报告此块的哈希(键对应于父块的正确子块)
   295  	copy(job.key, ref)
   296  
   297  //将新块发送到存储
   298  	job.parentWg.Done()
   299  }
   300  
   301  func (pc *PyramidChunker) loadTree(ctx context.Context) error {
   302  	log.Debug("pyramid.chunker: loadTree()")
   303  //获取根块以获取总大小
   304  	chunkData, err := pc.getter.Get(ctx, Reference(pc.key))
   305  	if err != nil {
   306  		return errLoadingTreeRootChunk
   307  	}
   308  	chunkSize := int64(chunkData.Size())
   309  	log.Trace("pyramid.chunker: root chunk", "chunk.Size", chunkSize, "pc.chunkSize", pc.chunkSize)
   310  
   311  //如果数据大小小于块…添加更新为挂起的父级
   312  	if chunkSize <= pc.chunkSize {
   313  		newEntry := &TreeEntry{
   314  			level:         0,
   315  			branchCount:   1,
   316  			subtreeSize:   uint64(chunkSize),
   317  			chunk:         make([]byte, pc.chunkSize+8),
   318  			key:           make([]byte, pc.hashSize),
   319  			index:         0,
   320  			updatePending: true,
   321  		}
   322  		copy(newEntry.chunk[8:], pc.key)
   323  		pc.chunkLevel[0] = append(pc.chunkLevel[0], newEntry)
   324  		return nil
   325  	}
   326  
   327  	var treeSize int64
   328  	var depth int
   329  	treeSize = pc.chunkSize
   330  	for ; treeSize < chunkSize; treeSize *= pc.branches {
   331  		depth++
   332  	}
   333  	log.Trace("pyramid.chunker", "depth", depth)
   334  
   335  //添加根块条目
   336  	branchCount := int64(len(chunkData)-8) / pc.hashSize
   337  	newEntry := &TreeEntry{
   338  		level:         depth - 1,
   339  		branchCount:   branchCount,
   340  		subtreeSize:   uint64(chunkSize),
   341  		chunk:         chunkData,
   342  		key:           pc.key,
   343  		index:         0,
   344  		updatePending: true,
   345  	}
   346  	pc.chunkLevel[depth-1] = append(pc.chunkLevel[depth-1], newEntry)
   347  
   348  //添加树的其余部分
   349  	for lvl := depth - 1; lvl >= 1; lvl-- {
   350  
   351  //todo(jmozah):不是加载完成的分支,然后在末端修剪,
   352  //首先避免装载它们
   353  		for _, ent := range pc.chunkLevel[lvl] {
   354  			branchCount = int64(len(ent.chunk)-8) / pc.hashSize
   355  			for i := int64(0); i < branchCount; i++ {
   356  				key := ent.chunk[8+(i*pc.hashSize) : 8+((i+1)*pc.hashSize)]
   357  				newChunkData, err := pc.getter.Get(ctx, Reference(key))
   358  				if err != nil {
   359  					return errLoadingTreeChunk
   360  				}
   361  				newChunkSize := newChunkData.Size()
   362  				bewBranchCount := int64(len(newChunkData)-8) / pc.hashSize
   363  				newEntry := &TreeEntry{
   364  					level:         lvl - 1,
   365  					branchCount:   bewBranchCount,
   366  					subtreeSize:   newChunkSize,
   367  					chunk:         newChunkData,
   368  					key:           key,
   369  					index:         0,
   370  					updatePending: true,
   371  				}
   372  				pc.chunkLevel[lvl-1] = append(pc.chunkLevel[lvl-1], newEntry)
   373  
   374  			}
   375  
   376  //我们只需要得到最右边未完成的分支。所以修剪所有完成的树枝
   377  			if int64(len(pc.chunkLevel[lvl-1])) >= pc.branches {
   378  				pc.chunkLevel[lvl-1] = nil
   379  			}
   380  		}
   381  	}
   382  
   383  	return nil
   384  }
   385  
   386  func (pc *PyramidChunker) prepareChunks(ctx context.Context, isAppend bool) {
   387  	log.Debug("pyramid.chunker: prepareChunks", "isAppend", isAppend)
   388  	defer pc.wg.Done()
   389  
   390  	chunkWG := &sync.WaitGroup{}
   391  
   392  	pc.incrementWorkerCount()
   393  
   394  	go pc.processor(ctx, pc.workerCount)
   395  
   396  	parent := NewTreeEntry(pc)
   397  	var unfinishedChunkData ChunkData
   398  	var unfinishedChunkSize uint64
   399  
   400  	if isAppend && len(pc.chunkLevel[0]) != 0 {
   401  		lastIndex := len(pc.chunkLevel[0]) - 1
   402  		ent := pc.chunkLevel[0][lastIndex]
   403  
   404  		if ent.branchCount < pc.branches {
   405  			parent = &TreeEntry{
   406  				level:         0,
   407  				branchCount:   ent.branchCount,
   408  				subtreeSize:   ent.subtreeSize,
   409  				chunk:         ent.chunk,
   410  				key:           ent.key,
   411  				index:         lastIndex,
   412  				updatePending: true,
   413  			}
   414  
   415  			lastBranch := parent.branchCount - 1
   416  			lastAddress := parent.chunk[8+lastBranch*pc.hashSize : 8+(lastBranch+1)*pc.hashSize]
   417  
   418  			var err error
   419  			unfinishedChunkData, err = pc.getter.Get(ctx, lastAddress)
   420  			if err != nil {
   421  				pc.errC <- err
   422  			}
   423  			unfinishedChunkSize = unfinishedChunkData.Size()
   424  			if unfinishedChunkSize < uint64(pc.chunkSize) {
   425  				parent.subtreeSize = parent.subtreeSize - unfinishedChunkSize
   426  				parent.branchCount = parent.branchCount - 1
   427  			} else {
   428  				unfinishedChunkData = nil
   429  			}
   430  		}
   431  	}
   432  
   433  	for index := 0; ; index++ {
   434  		var err error
   435  		chunkData := make([]byte, pc.chunkSize+8)
   436  
   437  		var readBytes int
   438  
   439  		if unfinishedChunkData != nil {
   440  			copy(chunkData, unfinishedChunkData)
   441  			readBytes += int(unfinishedChunkSize)
   442  			unfinishedChunkData = nil
   443  			log.Trace("pyramid.chunker: found unfinished chunk", "readBytes", readBytes)
   444  		}
   445  
   446  		var res []byte
   447  		res, err = ioutil.ReadAll(io.LimitReader(pc.reader, int64(len(chunkData)-(8+readBytes))))
   448  
   449  //ioutil.readall的黑客:
   450  //对ioutil.readall的成功调用返回err==nil,not err==eof,而我们
   451  //要传播IO.EOF错误
   452  		if len(res) == 0 && err == nil {
   453  			err = io.EOF
   454  		}
   455  		copy(chunkData[8+readBytes:], res)
   456  
   457  		readBytes += len(res)
   458  		log.Trace("pyramid.chunker: copied all data", "readBytes", readBytes)
   459  
   460  		if err != nil {
   461  			if err == io.EOF || err == io.ErrUnexpectedEOF {
   462  
   463  				pc.cleanChunkLevels()
   464  
   465  //检查是否追加,或者块是唯一的。
   466  				if parent.branchCount == 1 && (pc.depth() == 0 || isAppend) {
   467  //数据正好是一个块。选取最后一个区块键作为根
   468  					chunkWG.Wait()
   469  					lastChunksAddress := parent.chunk[8 : 8+pc.hashSize]
   470  					copy(pc.rootAddress, lastChunksAddress)
   471  					break
   472  				}
   473  			} else {
   474  				close(pc.quitC)
   475  				break
   476  			}
   477  		}
   478  
   479  //数据以块边界结尾。只需发出信号,开始建造树木
   480  		if readBytes == 0 {
   481  			pc.buildTree(isAppend, parent, chunkWG, true, nil)
   482  			break
   483  		} else {
   484  			pkey := pc.enqueueDataChunk(chunkData, uint64(readBytes), parent, chunkWG)
   485  
   486  //更新与树相关的父数据结构
   487  			parent.subtreeSize += uint64(readBytes)
   488  			parent.branchCount++
   489  
   490  //数据耗尽…发送任何与父树相关的块的信号
   491  			if int64(readBytes) < pc.chunkSize {
   492  
   493  				pc.cleanChunkLevels()
   494  
   495  //只有一个数据块..所以不要添加任何父块
   496  				if parent.branchCount <= 1 {
   497  					chunkWG.Wait()
   498  
   499  					if isAppend || pc.depth() == 0 {
   500  //如果深度为0,则无需构建树
   501  //或者我们正在追加。
   502  //只用最后一把钥匙。
   503  						copy(pc.rootAddress, pkey)
   504  					} else {
   505  //我们需要建造树和提供孤独
   506  //chunk键替换最后一个树chunk键。
   507  						pc.buildTree(isAppend, parent, chunkWG, true, pkey)
   508  					}
   509  					break
   510  				}
   511  
   512  				pc.buildTree(isAppend, parent, chunkWG, true, nil)
   513  				break
   514  			}
   515  
   516  			if parent.branchCount == pc.branches {
   517  				pc.buildTree(isAppend, parent, chunkWG, false, nil)
   518  				parent = NewTreeEntry(pc)
   519  			}
   520  
   521  		}
   522  
   523  		workers := pc.getWorkerCount()
   524  		if int64(len(pc.jobC)) > workers && workers < ChunkProcessors {
   525  			pc.incrementWorkerCount()
   526  			go pc.processor(ctx, pc.workerCount)
   527  		}
   528  
   529  	}
   530  
   531  }
   532  
   533  func (pc *PyramidChunker) buildTree(isAppend bool, ent *TreeEntry, chunkWG *sync.WaitGroup, last bool, lonelyChunkKey []byte) {
   534  	chunkWG.Wait()
   535  	pc.enqueueTreeChunk(ent, chunkWG, last)
   536  
   537  	compress := false
   538  	endLvl := pc.branches
   539  	for lvl := int64(0); lvl < pc.branches; lvl++ {
   540  		lvlCount := int64(len(pc.chunkLevel[lvl]))
   541  		if lvlCount >= pc.branches {
   542  			endLvl = lvl + 1
   543  			compress = true
   544  			break
   545  		}
   546  	}
   547  
   548  	if !compress && !last {
   549  		return
   550  	}
   551  
   552  //在压缩树之前,请等待所有要处理的键
   553  	chunkWG.Wait()
   554  
   555  	for lvl := int64(ent.level); lvl < endLvl; lvl++ {
   556  
   557  		lvlCount := int64(len(pc.chunkLevel[lvl]))
   558  		if lvlCount == 1 && last {
   559  			copy(pc.rootAddress, pc.chunkLevel[lvl][0].key)
   560  			return
   561  		}
   562  
   563  		for startCount := int64(0); startCount < lvlCount; startCount += pc.branches {
   564  
   565  			endCount := startCount + pc.branches
   566  			if endCount > lvlCount {
   567  				endCount = lvlCount
   568  			}
   569  
   570  			var nextLvlCount int64
   571  			var tempEntry *TreeEntry
   572  			if len(pc.chunkLevel[lvl+1]) > 0 {
   573  				nextLvlCount = int64(len(pc.chunkLevel[lvl+1]) - 1)
   574  				tempEntry = pc.chunkLevel[lvl+1][nextLvlCount]
   575  			}
   576  			if isAppend && tempEntry != nil && tempEntry.updatePending {
   577  				updateEntry := &TreeEntry{
   578  					level:         int(lvl + 1),
   579  					branchCount:   0,
   580  					subtreeSize:   0,
   581  					chunk:         make([]byte, pc.chunkSize+8),
   582  					key:           make([]byte, pc.hashSize),
   583  					index:         int(nextLvlCount),
   584  					updatePending: true,
   585  				}
   586  				for index := int64(0); index < lvlCount; index++ {
   587  					updateEntry.branchCount++
   588  					updateEntry.subtreeSize += pc.chunkLevel[lvl][index].subtreeSize
   589  					copy(updateEntry.chunk[8+(index*pc.hashSize):8+((index+1)*pc.hashSize)], pc.chunkLevel[lvl][index].key[:pc.hashSize])
   590  				}
   591  
   592  				pc.enqueueTreeChunk(updateEntry, chunkWG, last)
   593  
   594  			} else {
   595  
   596  				noOfBranches := endCount - startCount
   597  				newEntry := &TreeEntry{
   598  					level:         int(lvl + 1),
   599  					branchCount:   noOfBranches,
   600  					subtreeSize:   0,
   601  					chunk:         make([]byte, (noOfBranches*pc.hashSize)+8),
   602  					key:           make([]byte, pc.hashSize),
   603  					index:         int(nextLvlCount),
   604  					updatePending: false,
   605  				}
   606  
   607  				index := int64(0)
   608  				for i := startCount; i < endCount; i++ {
   609  					entry := pc.chunkLevel[lvl][i]
   610  					newEntry.subtreeSize += entry.subtreeSize
   611  					copy(newEntry.chunk[8+(index*pc.hashSize):8+((index+1)*pc.hashSize)], entry.key[:pc.hashSize])
   612  					index++
   613  				}
   614  //孤独区块键是最后一个区块的键,在最后一个分支上只有一个区块。
   615  //在这种情况下,忽略ITS树块键并将其替换为孤独块键。
   616  				if lonelyChunkKey != nil {
   617  //用Lonely数据块键覆盖最后一个树块键。
   618  					copy(newEntry.chunk[int64(len(newEntry.chunk))-pc.hashSize:], lonelyChunkKey[:pc.hashSize])
   619  				}
   620  
   621  				pc.enqueueTreeChunk(newEntry, chunkWG, last)
   622  
   623  			}
   624  
   625  		}
   626  
   627  		if !isAppend {
   628  			chunkWG.Wait()
   629  			if compress {
   630  				pc.chunkLevel[lvl] = nil
   631  			}
   632  		}
   633  	}
   634  
   635  }
   636  
   637  func (pc *PyramidChunker) enqueueTreeChunk(ent *TreeEntry, chunkWG *sync.WaitGroup, last bool) {
   638  	if ent != nil && ent.branchCount > 0 {
   639  
   640  //在处理树块之前,请等待数据块通过。
   641  		if last {
   642  			chunkWG.Wait()
   643  		}
   644  
   645  		binary.LittleEndian.PutUint64(ent.chunk[:8], ent.subtreeSize)
   646  		ent.key = make([]byte, pc.hashSize)
   647  		chunkWG.Add(1)
   648  		select {
   649  		case pc.jobC <- &chunkJob{ent.key, ent.chunk[:ent.branchCount*pc.hashSize+8], chunkWG}:
   650  		case <-pc.quitC:
   651  		}
   652  
   653  //根据天气情况更新或附加它是一个新条目或被重用
   654  		if ent.updatePending {
   655  			chunkWG.Wait()
   656  			pc.chunkLevel[ent.level][ent.index] = ent
   657  		} else {
   658  			pc.chunkLevel[ent.level] = append(pc.chunkLevel[ent.level], ent)
   659  		}
   660  
   661  	}
   662  }
   663  
   664  func (pc *PyramidChunker) enqueueDataChunk(chunkData []byte, size uint64, parent *TreeEntry, chunkWG *sync.WaitGroup) Address {
   665  	binary.LittleEndian.PutUint64(chunkData[:8], size)
   666  	pkey := parent.chunk[8+parent.branchCount*pc.hashSize : 8+(parent.branchCount+1)*pc.hashSize]
   667  
   668  	chunkWG.Add(1)
   669  	select {
   670  	case pc.jobC <- &chunkJob{pkey, chunkData[:size+8], chunkWG}:
   671  	case <-pc.quitC:
   672  	}
   673  
   674  	return pkey
   675  
   676  }
   677  
   678  //深度返回块级别的数目。
   679  //它用于检测是否只有一个数据块
   680  //最后一个分支。
   681  func (pc *PyramidChunker) depth() (d int) {
   682  	for _, l := range pc.chunkLevel {
   683  		if l == nil {
   684  			return
   685  		}
   686  		d++
   687  	}
   688  	return
   689  }
   690  
   691  //cleanchunklevels删除块级别之间的间隙(零级别)
   692  //这不是零。
   693  func (pc *PyramidChunker) cleanChunkLevels() {
   694  	for i, l := range pc.chunkLevel {
   695  		if l == nil {
   696  			pc.chunkLevel = append(pc.chunkLevel[:i], append(pc.chunkLevel[i+1:], nil)...)
   697  		}
   698  	}
   699  }
   700