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)
}