blob: e51e65f9507e6d9e1822e7b9e2c306c498750b55 [file] [log] [blame]
package find_breaks
// slice is a struct used for identifying regions of a parent array/slice. Two
// slice instances may be combined into a new slice instance representing the
// region where the two overlap, if any.
type slice struct {
start int
end int
}
// newSlice returns a slice instance.
func newSlice(start, end int) slice {
return slice{
start: start,
end: end,
}
}
// Valid returns true iff the slice is valid. A slice is valid if it is empty
// (start and end are both < 0), or if both start and end are nonnegative with
// end >= start.
func (s slice) Valid() bool {
if s.start < 0 && s.end < 0 {
return true // Empty slices are valid.
}
if s.start < 0 || s.end < 0 {
return false
}
if s.start > s.end {
return false
}
return true
}
// Overlap returns a new slice representing the overlapping region of the two
// slices.
func (s slice) Overlap(other slice) slice {
if !s.Valid() || !other.Valid() {
return newSlice(-1, -1)
}
rv := newSlice(s.start, s.end)
if other.start > rv.start {
rv.start = other.start
}
if other.end < rv.end {
rv.end = other.end
}
if rv.start >= rv.end {
rv.start = -1
rv.end = -1
}
return rv
}
// Len returns the length of the slice.
func (s slice) Len() int {
if !s.Valid() {
return 0
}
return s.end - s.start
}
// Empty returns true iff the slice has zero length.
func (s slice) Empty() bool {
return s.Len() == 0
}
// Copy returns a copy of this slice.
func (s slice) Copy() slice {
return newSlice(s.start, s.end)
}
// makeSlice creates a slice from the given slice of strings as a subslice of
// the given parent slice of strings.
func makeSlice(s []string, parent []string) slice {
if len(s) == 0 {
return newSlice(-1, -1)
}
// We can't guarantee the order of db.Task.Commits, and it's not
// convenient to sort chronologically before this point, so rather than
// iterating through the parent slice to find the sub-slice, we use a map,
// realizing that the commits will be unique and so the order within the
// sub-slice is not important.
m := make(map[string]bool, len(s))
for _, str := range s {
m[str] = true
}
startIdx := -1
endIdx := -1
for idx, c := range parent {
_, ok := m[c]
if ok {
if startIdx == -1 {
startIdx = idx
}
delete(m, c)
} else {
if startIdx >= 0 {
// Some tasks will have blamelists which extend
// outside of the time period. Therefore, we
// have to handle sub-slices which aren't
// contained within the parent slice.
// Unfortunately, this prevents us from handling
// the case where the sub-slice does not form a
// contiguous sub-slice of the parent, ie:
//
// if len(m) != 0 {
// // Non-contiguous slice!
// return newSlice(-1, -1)
// }
// We've reached the end of the sub-slice.
return newSlice(startIdx, idx)
}
}
}
if startIdx >= 0 {
endIdx = len(parent)
}
return newSlice(startIdx, endIdx)
}
// Resolve creates an actual slice of strings from the given slice instance.
func (s slice) Resolve(strs []string) []string {
if !s.Valid() || s.Empty() {
return []string{}
}
if s.start >= len(strs) || s.end > len(strs) || s.start > s.end {
return []string{}
}
return strs[s.start:s.end]
}