github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/sqlparse/tidbparser/dependency/util/chunk/list.go (about)

     1  // Copyright 2017 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package chunk
    15  
    16  import (
    17  	"github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/types"
    18  	"github.com/bingoohuang/gg/pkg/sqlparse/tidbparser/dependency/util/memory"
    19  	"github.com/juju/errors"
    20  )
    21  
    22  // List holds a slice of chunks, use to append rows with max chunk size properly handled.
    23  type List struct {
    24  	fieldTypes   []*types.FieldType
    25  	maxChunkSize int
    26  	length       int
    27  	chunks       []*Chunk
    28  	freelist     []*Chunk
    29  
    30  	memTracker  *memory.Tracker // track memory usage.
    31  	consumedIdx int             // chunk index in "chunks", has been consumed.
    32  }
    33  
    34  // RowPtr is used to get a row from a list.
    35  // It is only valid for the list that returns it.
    36  type RowPtr struct {
    37  	ChkIdx uint32
    38  	RowIdx uint32
    39  }
    40  
    41  // NewList creates a new List with field types and max chunk size.
    42  func NewList(fieldTypes []*types.FieldType, maxChunkSize int) *List {
    43  	l := &List{
    44  		fieldTypes:   fieldTypes,
    45  		maxChunkSize: maxChunkSize,
    46  		memTracker:   memory.NewTracker("chunk.List", -1),
    47  		consumedIdx:  -1,
    48  	}
    49  	return l
    50  }
    51  
    52  // GetMemTracker returns the memory tracker of this List.
    53  func (l *List) GetMemTracker() *memory.Tracker {
    54  	return l.memTracker
    55  }
    56  
    57  // Len returns the length of the List.
    58  func (l *List) Len() int {
    59  	return l.length
    60  }
    61  
    62  // NumChunks returns the number of chunks in the List.
    63  func (l *List) NumChunks() int {
    64  	return len(l.chunks)
    65  }
    66  
    67  // GetChunk gets the Chunk by ChkIdx.
    68  func (l *List) GetChunk(chkIdx int) *Chunk {
    69  	return l.chunks[chkIdx]
    70  }
    71  
    72  // AppendRow appends a row to the List, the row is copied to the List.
    73  func (l *List) AppendRow(row Row) RowPtr {
    74  	chkIdx := len(l.chunks) - 1
    75  	if chkIdx == -1 || l.chunks[chkIdx].NumRows() >= l.maxChunkSize || chkIdx == l.consumedIdx {
    76  		newChk := l.allocChunk()
    77  		l.chunks = append(l.chunks, newChk)
    78  		if chkIdx != l.consumedIdx {
    79  			l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage())
    80  			l.consumedIdx = chkIdx
    81  		}
    82  		chkIdx++
    83  	}
    84  	chk := l.chunks[chkIdx]
    85  	rowIdx := chk.NumRows()
    86  	chk.AppendRow(row)
    87  	l.length++
    88  	return RowPtr{ChkIdx: uint32(chkIdx), RowIdx: uint32(rowIdx)}
    89  }
    90  
    91  // Add adds a chunk to the List, the chunk may be modified later by the list.
    92  // Caller must make sure the input chk is not empty and not used any more and has the same field types.
    93  func (l *List) Add(chk *Chunk) {
    94  	// FixMe: we should avoid add a Chunk that chk.NumRows() > list.maxChunkSize.
    95  	if chk.NumRows() == 0 {
    96  		panic("chunk appended to List should have at least 1 row")
    97  	}
    98  	if chkIdx := len(l.chunks) - 1; l.consumedIdx != chkIdx {
    99  		l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage())
   100  		l.consumedIdx = chkIdx
   101  	}
   102  	l.memTracker.Consume(chk.MemoryUsage())
   103  	l.consumedIdx++
   104  	l.chunks = append(l.chunks, chk)
   105  	l.length += chk.NumRows()
   106  	return
   107  }
   108  
   109  func (l *List) allocChunk() (chk *Chunk) {
   110  	if len(l.freelist) > 0 {
   111  		lastIdx := len(l.freelist) - 1
   112  		chk = l.freelist[lastIdx]
   113  		l.freelist = l.freelist[:lastIdx]
   114  		l.memTracker.Consume(-chk.MemoryUsage())
   115  		chk.Reset()
   116  		return
   117  	}
   118  	return NewChunk(l.fieldTypes)
   119  }
   120  
   121  // GetRow gets a Row from the list by RowPtr.
   122  func (l *List) GetRow(ptr RowPtr) Row {
   123  	chk := l.chunks[ptr.ChkIdx]
   124  	return chk.GetRow(int(ptr.RowIdx))
   125  }
   126  
   127  // Reset resets the List.
   128  func (l *List) Reset() {
   129  	if lastIdx := len(l.chunks) - 1; lastIdx != l.consumedIdx {
   130  		l.memTracker.Consume(l.chunks[lastIdx].MemoryUsage())
   131  	}
   132  	l.freelist = append(l.freelist, l.chunks...)
   133  	l.chunks = l.chunks[:0]
   134  	l.length = 0
   135  	l.consumedIdx = -1
   136  }
   137  
   138  // ListWalkFunc is used to walk the list.
   139  // If error is returned, it will stop walking.
   140  type ListWalkFunc = func(row Row) error
   141  
   142  // Walk iterate the list and call walkFunc for each row.
   143  func (l *List) Walk(walkFunc ListWalkFunc) error {
   144  	for i := 0; i < len(l.chunks); i++ {
   145  		chk := l.chunks[i]
   146  		for j := 0; j < chk.NumRows(); j++ {
   147  			err := walkFunc(chk.GetRow(j))
   148  			if err != nil {
   149  				return errors.Trace(err)
   150  			}
   151  		}
   152  	}
   153  	return nil
   154  }