/*
* 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_