blob: 1fc95b0f900acddb48493eb2119ed1a14a720a7a [file] [log] [blame]
// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
import java.util.Map;
import java.util.TreeMap;
/**
* Implementation of a discrete segment tree where intervals are specified by their start end and
* point. Both points are considered part of the interval.
*/
public class SegmentTree<V> {
private final TreeMap<Integer, V> internalTree = new TreeMap<>();
private final boolean allowIntervalOverwrites;
private int size = 0;
public SegmentTree(boolean allowIntervalOverwrites) {
this.allowIntervalOverwrites = allowIntervalOverwrites;
}
public V find(int point) {
Map.Entry<Integer, V> entry = findEntry(point);
return entry != null ? entry.getValue() : null;
}
public Map.Entry<Integer, V> findEntry(Integer point) {
Map.Entry<Integer, V> kvEntry = internalTree.floorEntry(point);
return (kvEntry == null || kvEntry.getValue() == null) ? null : kvEntry;
}
public SegmentTree<V> add(int start, int end, V value) {
Map.Entry<Integer, V> existingEndRange = findEntry(end);
Box<Integer> removedIntervals = new Box<>(0);
boolean removedKeys =
internalTree
.navigableKeySet()
.removeIf(
key -> {
if (start < key && key <= end) {
assert allowIntervalOverwrites;
if (internalTree.get(key) != null) {
removedIntervals.set(removedIntervals.get() + 1);
}
return true;
}
return false;
});
if (existingEndRange != null) {
assert allowIntervalOverwrites;
if (removedKeys) {
// We have counted a removed interval where it was actually just shortened:
// I1 |----------------------------------|
// I2 |--------|
// R |--------||---------------------------|
// I1.start has been removed and counted, but it is actually just moved, so we decrease the
// removed counter by 1.
removedIntervals.set(removedIntervals.get() - 1);
}
}
internalTree.put(start, value);
if (!internalTree.containsKey(end + 1)) {
internalTree.put(end + 1, existingEndRange == null ? null : existingEndRange.getValue());
}
// We only count unique intervals, thus the following is two and not three intervals when
// adding I2:
// I1 |-------------------------------|
// I2 |------------|
// R |------||------------||---------|
// However, if the order was reversed, we should remove one from the size count since I1
// completely shadows I2.
size = (size - removedIntervals.get()) + 1;
return this;
}
public int size() {
return size;
}
}