Calico IndexAllocator

57 阅读1分钟

IndexAllocator用于将较复杂的区间简化为顺序线性空间。

// 区间定义 [上限,下限]
type IndexRange struct {
	Min, Max int
}

func (r IndexRange) contains(a int) bool {
	return r.Min <= a && r.Max >= a
}

// ByMaxIndex sorts collections of IndexRange structs in order of their starting/lower index
type ByMaxIndex []IndexRange

// Len is the number of indexranges in the collection
func (i ByMaxIndex) Len() int { return len(i) }

// Less reports whether the element with index a
// must sort before the element with index b.
func (i ByMaxIndex) Less(a, b int) bool { return i[a].Max < i[b].Max }

// Swap swaps the elements with indexes a and b.
func (i ByMaxIndex) Swap(a, b int) { i[a], i[b] = i[b], i[a] }

type IndexAllocator struct {
	indexStack *stack.Stack  // 存储合法的index
	exclusions []IndexRange
}

NewIndexAllocator会按照传入的区间范围,将合法的index从大到小压入栈中,并有能力处理重叠区间和不合法区间。

// NewIndexAllocator returns an index allocator from the provided indexRanges
// any indices falling within the specified exclusions will not be returned, even if designated by indexRanges
func NewIndexAllocator(indexRanges []IndexRange, exclusions []IndexRange) *IndexAllocator {
	// sort index ranges in descending order of their Max bound
	if len(indexRanges) > 1 {
		sort.Sort(sort.Reverse(ByMaxIndex(indexRanges)))
	}

	r := &IndexAllocator{
		indexStack: stack.New(),
		exclusions: exclusions,
	}
        // 从Max值最大的区间开始遍历
	var lowestIndex int
	for j, indexRange := range indexRanges {
		if j == 0 {
			// keep track of the most recent Min to prevent an overlapping range
			// from creating duplicate indices in the final index stack
			lowestIndex = indexRange.Max + 1
		}

		// Push in reverse order so that the lowest index will come out first.
	populating:
		for i := indexRange.Max; i >= indexRange.Min; i-- {
			// skip overlapping range indices
			if i >= lowestIndex {
				continue
			}
			// skip exclusions
			for _, excl := range exclusions {
				if excl.contains(i) {
					continue populating
				}
			}
			r.indexStack.Push(i)
			lowestIndex = i
		}
	}
	return r
}

func (r *IndexAllocator) GrabIndex() (int, error) {
	if r.indexStack.Len() == 0 {
		return 0, errors.New("no more indices available")
	}
	return r.indexStack.Pop().(int), nil
}

func (r *IndexAllocator) ReleaseIndex(index int) {
	r.indexStack.Push(index)
}