|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interface Range<T> { |
|
start: number; |
|
end: number; |
|
refCount: number; |
|
data: T | null; |
|
} |
|
|
|
export class RangeList<T> { |
|
private ranges: Range<T>[] = []; |
|
|
|
|
|
|
|
|
|
|
|
add(start: number, end: number): void { |
|
if (end <= start) { |
|
throw new TypeError("End must be greater than start"); |
|
} |
|
|
|
|
|
const overlappingRanges: { index: number; range: Range<T> }[] = []; |
|
for (let i = 0; i < this.ranges.length; i++) { |
|
const range = this.ranges[i]; |
|
if (start < range.end && end > range.start) { |
|
overlappingRanges.push({ index: i, range }); |
|
} |
|
if (range.data !== null) { |
|
throw new Error("Overlapping range already has data"); |
|
} |
|
} |
|
|
|
if (overlappingRanges.length === 0) { |
|
|
|
this.ranges.push({ start, end, refCount: 1, data: null }); |
|
this.ranges.sort((a, b) => a.start - b.start); |
|
return; |
|
} |
|
|
|
|
|
const newRanges: Range<T>[] = []; |
|
let currentPos = start; |
|
|
|
for (let i = 0; i < overlappingRanges.length; i++) { |
|
const { range } = overlappingRanges[i]; |
|
|
|
|
|
if (currentPos < range.start) { |
|
newRanges.push({ |
|
start: currentPos, |
|
end: range.start, |
|
refCount: 1, |
|
data: null, |
|
}); |
|
} else if (range.start < currentPos) { |
|
newRanges.push({ |
|
start: range.start, |
|
end: currentPos, |
|
refCount: range.refCount, |
|
data: null, |
|
}); |
|
} |
|
|
|
|
|
newRanges.push({ |
|
start: Math.max(currentPos, range.start), |
|
end: Math.min(end, range.end), |
|
refCount: range.refCount + 1, |
|
data: null, |
|
}); |
|
|
|
|
|
if (range.end > end) { |
|
newRanges.push({ |
|
start: end, |
|
end: range.end, |
|
refCount: range.refCount, |
|
data: null, |
|
}); |
|
} |
|
|
|
currentPos = Math.max(currentPos, range.end); |
|
} |
|
|
|
|
|
if (currentPos < end) { |
|
newRanges.push({ |
|
start: currentPos, |
|
end, |
|
refCount: 1, |
|
data: null, |
|
}); |
|
} |
|
|
|
|
|
const firstIndex = overlappingRanges[0].index; |
|
const lastIndex = overlappingRanges[overlappingRanges.length - 1].index; |
|
this.ranges.splice(firstIndex, lastIndex - firstIndex + 1, ...newRanges); |
|
this.ranges.sort((a, b) => a.start - b.start); |
|
} |
|
|
|
|
|
|
|
|
|
remove(start: number, end: number): void { |
|
if (end <= start) { |
|
throw new TypeError("End must be greater than start"); |
|
} |
|
|
|
|
|
const affectedRanges: { index: number; range: Range<T> }[] = []; |
|
for (let i = 0; i < this.ranges.length; i++) { |
|
const range = this.ranges[i]; |
|
if (start < range.end && end > range.start) { |
|
affectedRanges.push({ index: i, range }); |
|
} |
|
} |
|
|
|
if (affectedRanges.length === 0) { |
|
throw new Error("No ranges found to remove"); |
|
} |
|
|
|
|
|
if (start !== affectedRanges[0].range.start || end !== affectedRanges[affectedRanges.length - 1].range.end) { |
|
throw new Error("Range boundaries must match existing boundaries"); |
|
} |
|
|
|
|
|
|
|
for (let i = 0; i < affectedRanges.length; i++) { |
|
const { range } = affectedRanges[i]; |
|
|
|
range.refCount--; |
|
} |
|
|
|
this.ranges = this.ranges.filter((range) => range.refCount > 0); |
|
} |
|
|
|
|
|
|
|
|
|
getRanges(start: number, end: number): Range<T>[] { |
|
if (end <= start) { |
|
throw new TypeError("End must be greater than start"); |
|
} |
|
|
|
return this.ranges.filter((range) => start < range.end && end > range.start); |
|
} |
|
|
|
|
|
|
|
|
|
getAllRanges(): Range<T>[] { |
|
return [...this.ranges]; |
|
} |
|
} |
|
|