github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/container/queue/iterator.go (about)

     1  // Copyright 2022 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 queue
    15  
    16  // ChunkQueueIterator is the iterator type of ChunkQueue. Iterating ChunkQueue
    17  // by iterators is not thread-safe. Manipulating invalid iterators may incur
    18  // panics. Don't use an iterator of an element that has already been dequeued.
    19  // Instead, use with checks and in loop. E.g.
    20  //
    21  //	for it := someQueue.First(); it.Valid(); it.Next() {
    22  //			... // operations cannot pop element
    23  //	}
    24  //
    25  // Note: Begin() and First() are interchangeable
    26  // for it := someQueue.Begin(); it.Valid(); { 			// forwards
    27  //
    28  //			it.Next()
    29  //			q.Pop() // can pop element
    30  //	}
    31  //
    32  // for it := someQueue.Last(); it.Valid(); it.Next() {	// backwards
    33  //
    34  //			...
    35  //	}
    36  //
    37  // for it := someQueue.End(); it.Prev(); {				// backwards
    38  //
    39  //			...
    40  //	}
    41  type ChunkQueueIterator[T any] struct {
    42  	idxInChunk int
    43  	chunk      *chunk[T]
    44  }
    45  
    46  // First returns the iterator of the first element. The iterator is valid for a
    47  // non-empty queue, and invalid otherwise.
    48  func (q *ChunkQueue[T]) First() *ChunkQueueIterator[T] {
    49  	return &ChunkQueueIterator[T]{
    50  		chunk:      q.firstChunk(),
    51  		idxInChunk: q.firstChunk().l,
    52  	}
    53  }
    54  
    55  // Last returns the iterator of the last element. The iterator is valid for a
    56  // non-empty queue, and invalid otherwise.
    57  func (q *ChunkQueue[T]) Last() *ChunkQueueIterator[T] {
    58  	return &ChunkQueueIterator[T]{
    59  		chunk:      q.lastChunk(),
    60  		idxInChunk: q.lastChunk().r - 1,
    61  	}
    62  }
    63  
    64  // Begin is an alias of First(), for convenient
    65  func (q *ChunkQueue[T]) Begin() *ChunkQueueIterator[T] {
    66  	return q.First()
    67  }
    68  
    69  // End returns a special iterator of the queue representing the end. The end
    70  // iterator is not valid since it's not in the queue. Its predecessor is Last()
    71  func (q *ChunkQueue[T]) End() *ChunkQueueIterator[T] {
    72  	return &ChunkQueueIterator[T]{
    73  		chunk:      q.lastChunk(),
    74  		idxInChunk: q.chunkLength,
    75  	}
    76  }
    77  
    78  // GetIterator returns an iterator of a given index. Nil for invalid indices
    79  func (q *ChunkQueue[T]) GetIterator(idx int) *ChunkQueueIterator[T] {
    80  	if idx < 0 || idx >= q.size {
    81  		return nil
    82  	}
    83  	idx += q.chunks[q.head].l
    84  	return &ChunkQueueIterator[T]{
    85  		chunk:      q.chunks[q.head+idx/q.chunkLength],
    86  		idxInChunk: idx % q.chunkLength,
    87  	}
    88  }
    89  
    90  // Valid indicates if the element of the iterator is in queue
    91  func (it *ChunkQueueIterator[T]) Valid() bool {
    92  	return it.chunk != nil && it.idxInChunk >= it.chunk.l && it.idxInChunk < it.chunk.r
    93  }
    94  
    95  // Value returns the element value of a valid iterator which is in queue.
    96  // It's meaningless and may panic otherwise.
    97  func (it *ChunkQueueIterator[T]) Value() T {
    98  	return it.chunk.data[it.idxInChunk]
    99  }
   100  
   101  // Set replaces the element of the valid iterator. Panic for invalid iterators
   102  func (it *ChunkQueueIterator[T]) Set(v T) {
   103  	it.chunk.data[it.idxInChunk] = v
   104  }
   105  
   106  // Index returns the index of a valid iterator, and -1 otherwise.
   107  // Attention: The time complexity is O(N). Please avoid using this method
   108  func (it *ChunkQueueIterator[T]) Index() int {
   109  	if !it.Valid() {
   110  		return -1
   111  	}
   112  	q := it.chunk.queue
   113  	idx := 0
   114  	for i := q.head; i < q.tail; i++ {
   115  		if q.chunks[i] != it.chunk {
   116  			idx += q.chunks[i].len()
   117  		} else {
   118  			idx += it.idxInChunk - it.chunk.l
   119  			break
   120  		}
   121  	}
   122  	return idx
   123  }
   124  
   125  // Next updates the current iterator to its next iterator. It returns true if
   126  // the next iterator is still in queue, and false otherwise. Calling Next for
   127  // an invalid iterator is meaningless, and using invalid iterators may panic.
   128  func (it *ChunkQueueIterator[T]) Next() bool {
   129  	if it.chunk == nil {
   130  		return false
   131  	}
   132  
   133  	it.idxInChunk++
   134  	if it.idxInChunk < it.chunk.r {
   135  		return true
   136  	}
   137  
   138  	c, q := it.chunk, it.chunk.queue
   139  	if it.idxInChunk == q.chunkLength && c.next != nil && !c.empty() {
   140  		it.idxInChunk, it.chunk = 0, c.next
   141  		return true
   142  	}
   143  
   144  	it.idxInChunk = q.chunkLength
   145  	return false
   146  }
   147  
   148  // Prev updates the current to its previous iterator. It returns true if the
   149  // next iterator is in queue, and false otherwise. The Prev of an end iterator
   150  // points to the last element of the queue if the queue is not empty.
   151  // The return boolean value is useful for backwards iteration. E.g.
   152  // `for it := someQueue.Last(); it.Valid(); it.Next() {...}`
   153  // `for it := someQueue.End(); it.Prev; {...} `
   154  func (it *ChunkQueueIterator[T]) Prev() bool {
   155  	if !it.Valid() {
   156  		if c := it.chunk; c != nil && c.queue != nil && it.idxInChunk == len(c.data) {
   157  			lc := c.queue.lastChunk()
   158  			it.chunk, it.idxInChunk = lc, lc.r-1
   159  			return it.Valid()
   160  		}
   161  		return false
   162  	}
   163  
   164  	it.idxInChunk--
   165  	if it.idxInChunk >= it.chunk.l {
   166  		return true
   167  	}
   168  
   169  	c := it.chunk
   170  	if c.prev != nil {
   171  		it.chunk, it.idxInChunk = c.prev, c.prev.r-1
   172  		return true
   173  	}
   174  	it.idxInChunk = -1
   175  	return false
   176  }