/*
|
* Copyright (c) 2016, Google Inc.
|
* All rights reserved.
|
* Use of this source code is governed by a BSD-style license that can be
|
* found in the LICENSE file.
|
*/
|
|
#ifndef PERFTOOLS_INTERVALMAP_H_
|
#define PERFTOOLS_INTERVALMAP_H_
|
|
#include <cstdlib>
|
#include <iostream>
|
#include <iterator>
|
#include <map>
|
#include <sstream>
|
|
#include "int_compat.h"
|
|
namespace perftools {
|
|
template <class V>
|
class IntervalMap {
|
public:
|
IntervalMap();
|
|
// Set [start, limit) to value. If this interval overlaps one currently in the
|
// map, the overlapping section will be overwritten by the new interval.
|
void Set(uint64 start, uint64 limit, const V& value);
|
|
// Finds the value associated with the interval containing key. Returns false
|
// if no interval contains key.
|
bool Lookup(uint64 key, V* value) const;
|
|
// Find the interval containing key, or the next interval containing
|
// something greater than key. Returns false if one is not found, otherwise
|
// it sets start, limit, and value to the corresponding values from the
|
// interval.
|
bool FindNext(uint64 key, uint64* start, uint64* limit, V* value) const;
|
|
// Remove all entries from the map.
|
void Clear();
|
|
// Clears everything in the interval map from [clear_start,
|
// clear_limit). This may cut off sections or entire intervals in
|
// the map. This will invalidate iterators to intervals which have a
|
// start value residing in [clear_start, clear_limit).
|
void ClearInterval(uint64 clear_start, uint64 clear_limit);
|
|
uint64 Size() const;
|
|
private:
|
struct Value {
|
uint64 limit;
|
V value;
|
};
|
|
using MapIter = typename std::map<uint64, Value>::iterator;
|
using ConstMapIter = typename std::map<uint64, Value>::const_iterator;
|
|
// For an interval to be valid, start must be strictly less than limit.
|
void AssertValidInterval(uint64 start, uint64 limit) const;
|
|
// Returns an iterator pointing to the interval containing the given key, or
|
// end() if one was not found.
|
ConstMapIter GetContainingInterval(uint64 point) const {
|
auto bound = interval_start_.upper_bound(point);
|
if (!Decrement(&bound)) {
|
return interval_start_.end();
|
}
|
if (bound->second.limit <= point) {
|
return interval_start_.end();
|
}
|
return bound;
|
}
|
|
MapIter GetContainingInterval(uint64 point) {
|
auto bound = interval_start_.upper_bound(point);
|
if (!Decrement(&bound)) {
|
return interval_start_.end();
|
}
|
if (bound->second.limit <= point) {
|
return interval_start_.end();
|
}
|
return bound;
|
}
|
|
// Decrements the provided iterator to interval_start_, or returns false if
|
// iter == begin().
|
bool Decrement(ConstMapIter* iter) const;
|
bool Decrement(MapIter* iter);
|
|
// Removes everything in the interval map from [remove_start,
|
// remove_limit). This may cut off sections or entire intervals in
|
// the map. This will invalidate iterators to intervals which have a
|
// start value residing in [remove_start, remove_limit).
|
void RemoveInterval(uint64 remove_start, uint64 remove_limit);
|
|
void Insert(uint64 start, uint64 limit, const V& value);
|
|
// Split an interval into two intervals, [iter.start, point) and
|
// [point, iter.limit). If point is not within (iter.start, point) or iter is
|
// end(), it is a noop.
|
void SplitInterval(MapIter iter, uint64 point);
|
|
// Map from the start of the interval to the limit of the interval and the
|
// corresponding value.
|
std::map<uint64, Value> interval_start_;
|
};
|
|
template <class V>
|
IntervalMap<V>::IntervalMap() {}
|
|
template <class V>
|
void IntervalMap<V>::Set(uint64 start, uint64 limit, const V& value) {
|
AssertValidInterval(start, limit);
|
RemoveInterval(start, limit);
|
Insert(start, limit, value);
|
}
|
|
template <class V>
|
bool IntervalMap<V>::Lookup(uint64 key, V* value) const {
|
const auto contain = GetContainingInterval(key);
|
if (contain == interval_start_.end()) {
|
return false;
|
}
|
*value = contain->second.value;
|
return true;
|
}
|
|
template <class V>
|
bool IntervalMap<V>::FindNext(uint64 key, uint64* start, uint64* limit,
|
V* value) const {
|
auto iter = interval_start_.upper_bound(key);
|
if (iter == interval_start_.end()) {
|
return false;
|
}
|
*start = iter->first;
|
*limit = iter->second.limit;
|
*value = iter->second.value;
|
return true;
|
}
|
|
template <class V>
|
void IntervalMap<V>::Clear() {
|
interval_start_.clear();
|
}
|
|
template <class V>
|
void IntervalMap<V>::ClearInterval(uint64 clear_start, uint64 clear_limit) {
|
AssertValidInterval(clear_start, clear_limit);
|
RemoveInterval(clear_start, clear_limit);
|
}
|
|
template <class V>
|
uint64 IntervalMap<V>::Size() const {
|
return interval_start_.size();
|
}
|
|
template <class V>
|
void IntervalMap<V>::RemoveInterval(uint64 remove_start, uint64 remove_limit) {
|
if (remove_start >= remove_limit) {
|
return;
|
}
|
// It starts by splitting intervals that will only be partly cleared into two,
|
// where one of those will be fully cleared and the other will not be cleared.
|
SplitInterval(GetContainingInterval(remove_limit), remove_limit);
|
SplitInterval(GetContainingInterval(remove_start), remove_start);
|
|
auto remove_interval_start = interval_start_.lower_bound(remove_start);
|
auto remove_interval_end = interval_start_.lower_bound(remove_limit);
|
// Note that if there are no intervals to be cleared, then
|
// clear_interval_start == clear_interval_end and the erase will be a noop.
|
interval_start_.erase(remove_interval_start, remove_interval_end);
|
}
|
|
template <class V>
|
void IntervalMap<V>::SplitInterval(MapIter iter, uint64 point) {
|
if (iter == interval_start_.end() || point <= iter->first ||
|
point >= iter->second.limit) {
|
return;
|
}
|
const auto larger_limit = iter->second.limit;
|
iter->second.limit = point;
|
Insert(point, larger_limit, iter->second.value);
|
}
|
|
template <class V>
|
bool IntervalMap<V>::Decrement(ConstMapIter* iter) const {
|
if ((*iter) == interval_start_.begin()) {
|
return false;
|
}
|
--(*iter);
|
return true;
|
}
|
|
template <class V>
|
bool IntervalMap<V>::Decrement(MapIter* iter) {
|
if ((*iter) == interval_start_.begin()) {
|
return false;
|
}
|
--(*iter);
|
return true;
|
}
|
|
template <class V>
|
void IntervalMap<V>::Insert(uint64 start, uint64 limit, const V& value) {
|
interval_start_.emplace(std::pair<uint64, Value>{start, {limit, value}});
|
}
|
|
template <class V>
|
void IntervalMap<V>::AssertValidInterval(uint64 start, uint64 limit) const {
|
if (start >= limit) {
|
std::cerr << "Invalid interval. Start may not be >= limit." << std::endl
|
<< "Start: " << start << std::endl
|
<< "Limit: " << limit << std::endl;
|
abort();
|
}
|
}
|
|
} // namespace perftools
|
|
#endif // PERFTOOLS_INTERVALMAP_H_
|