termdash/attrrange/attrrange.go

104 lines
2.7 KiB
Go

// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package attrrange simplifies tracking of attributes that apply to a range of
// items.
// Refer to the examples in the test file for details on usage.
package attrrange
import (
"fmt"
"sort"
)
// AttrRange is a range of items that share the same attributes.
type AttrRange struct {
// Low is the first position where these attributes apply.
Low int
// High is the end of the range. The attributes apply to all items in range
// Low <= b < high.
High int
// AttrIdx is the index of the attributes that apply to this range.
AttrIdx int
}
// newAttrRange returns a new AttrRange instance.
func newAttrRange(low, high, attrIdx int) *AttrRange {
return &AttrRange{
Low: low,
High: high,
AttrIdx: attrIdx,
}
}
// Tracker tracks attributes that apply to a range of items.
// This object is not thread safe.
type Tracker struct {
// ranges maps low indices of ranges to the attribute ranges.
ranges map[int]*AttrRange
}
// NewTracker returns a new tracker of ranges that share the same attributes.
func NewTracker() *Tracker {
return &Tracker{
ranges: map[int]*AttrRange{},
}
}
// Add adds a new range of items that share attributes with the specified
// index.
// The low position of the range must not overlap with low position of any
// existing range.
func (t *Tracker) Add(low, high, attrIdx int) error {
ar := newAttrRange(low, high, attrIdx)
if ar, ok := t.ranges[low]; ok {
return fmt.Errorf("already have range starting on low:%d, existing:%+v", low, ar)
}
t.ranges[low] = ar
return nil
}
// ForPosition returns attribute index that apply to the specified position.
// Returns ErrNotFound when the requested position wasn't found in any of the
// known ranges.
func (t *Tracker) ForPosition(pos int) (*AttrRange, error) {
if ar, ok := t.ranges[pos]; ok {
return ar, nil
}
var keys []int
for k := range t.ranges {
keys = append(keys, k)
}
sort.Ints(keys)
var res *AttrRange
for _, k := range keys {
ar := t.ranges[k]
if ar.Low > pos {
break
}
if ar.High > pos {
res = ar
}
}
if res == nil {
return nil, fmt.Errorf("did not find attribute range for position %d", pos)
}
return res, nil
}