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 }