* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _RANGE_ARRAY_H
#define _RANGE_ARRAY_H
#include <algorithm>
#include <Array.h>
namespace BPrivate {
template<typename Value>
struct Range {
Value offset;
Value size;
Range(const Value& offset, const Value& size)
:
offset(offset),
size(size)
{
}
Value EndOffset() const
{
return offset + size;
}
};
template<typename Value>
class RangeArray {
public:
typedef Range<Value> RangeType;
public:
inline RangeArray();
inline RangeArray(const RangeArray<Value>& other);
inline int32 CountRanges() const
{ return fRanges.Count(); }
inline bool IsEmpty() const
{ return fRanges.IsEmpty(); }
inline const RangeType* Ranges() const
{ return fRanges.Elements(); }
inline bool AddRange(const RangeType& range);
bool AddRange(const Value& offset,
const Value& size);
inline bool RemoveRange(const RangeType& range);
bool RemoveRange(const Value& offset,
const Value& size);
inline bool RemoveRanges(int32 index, int32 count = 1);
inline void Clear() { fRanges.Clear(); }
inline void MakeEmpty() { fRanges.MakeEmpty(); }
inline bool IntersectsWith(const RangeType& range) const;
bool IntersectsWith(const Value& offset,
const Value& size) const;
int32 InsertionIndex(const Value& offset) const;
inline const RangeType& RangeAt(int32 index) const
{ return fRanges.ElementAt(index); }
inline const RangeType& operator[](int32 index) const
{ return fRanges[index]; }
inline RangeArray<Value>& operator=(const RangeArray<Value>& other);
private:
inline RangeType& _RangeAt(int32 index)
{ return fRanges.ElementAt(index); }
private:
Array<RangeType> fRanges;
};
template<typename Value>
inline
RangeArray<Value>::RangeArray()
{
}
template<typename Value>
inline
RangeArray<Value>::RangeArray(const RangeArray<Value>& other)
:
fRanges(other.fRanges)
{
}
template<typename Value>
inline bool
RangeArray<Value>::AddRange(const RangeType& range)
{
return AddRange(range.offset, range.size);
}
The range is automatically joined with ranges it adjoins or overlaps with.
\return \c true, if the range was added successfully, \c false, if a memory
allocation failed.
*/
template<typename Value>
bool
RangeArray<Value>::AddRange(const Value& offset, const Value& size)
{
if (size == 0)
return true;
int32 index = InsertionIndex(offset);
Value endOffset = offset + size;
int32 endIndex = index;
int32 count = CountRanges();
while (endIndex < count && RangeAt(endIndex).offset <= endOffset)
endIndex++;
if (index > 0 && offset == RangeAt(index - 1).EndOffset())
index--;
if (index == endIndex) {
return fRanges.Insert(RangeType(offset, size), index);
}
endOffset = std::max(endOffset, RangeAt(endIndex - 1).EndOffset());
RangeType& firstRange = _RangeAt(index);
firstRange.offset = std::min(firstRange.offset, offset);
firstRange.size = endOffset - firstRange.offset;
if (index + 1 < endIndex)
RemoveRanges(index + 1, endIndex - index - 1);
return true;
}
template<typename Value>
inline bool
RangeArray<Value>::RemoveRange(const RangeType& range)
{
return RemoveRange(range.offset, range.size);
}
Ranges that are completely covered by the given range are removed. Ranges
that partially intersect with it are cut accordingly.
If a range is split into two ranges by the operation, a memory allocation
might be necessary and the method can fail, if the memory allocation fails.
\return \c true, if the range was removed successfully, \c false, if a
memory allocation failed.
*/
template<typename Value>
bool
RangeArray<Value>::RemoveRange(const Value& offset, const Value& size)
{
if (size == 0)
return true;
int32 index = InsertionIndex(offset);
Value endOffset = offset + size;
int32 endIndex = index;
int32 count = CountRanges();
while (endIndex < count && RangeAt(endIndex).offset < endOffset)
endIndex++;
if (index == endIndex) {
return true;
}
RangeType& firstRange = _RangeAt(index);
RangeType& lastRange = _RangeAt(endIndex - 1);
int32 firstRemoveIndex = firstRange.offset >= offset ? index : index + 1;
int32 endRemoveIndex = lastRange.EndOffset() <= endOffset
? endIndex : endIndex - 1;
if (firstRemoveIndex > endRemoveIndex) {
RangeType newRange(endOffset, firstRange.EndOffset() - endOffset);
if (!fRanges.Insert(newRange, index + 1))
return false;
firstRange.size = offset - firstRange.offset;
return true;
}
if (index < firstRemoveIndex)
firstRange.size = offset - firstRange.offset;
if (endOffset > endRemoveIndex) {
lastRange.size = lastRange.EndOffset() - endOffset;
lastRange.offset = endOffset;
}
if (firstRemoveIndex < endRemoveIndex)
RemoveRanges(firstRemoveIndex, endRemoveIndex - firstRemoveIndex);
return true;
}
template<typename Value>
inline bool
RangeArray<Value>::RemoveRanges(int32 index, int32 count)
{
return fRanges.Remove(index, count);
}
template<typename Value>
inline bool
RangeArray<Value>::IntersectsWith(const RangeType& range) const
{
return IntersectsWith(range.offset, range.size);
}
template<typename Value>
bool
RangeArray<Value>::IntersectsWith(const Value& offset, const Value& size) const
{
int32 index = InsertionIndex(offset);
return index < CountRanges() && RangeAt(index).offset < offset + size;
}
If the array contains a range that starts at, includes (but doesn't end at)
\a offset, the index of that range is returned. If \a offset lies in between
two ranges or before the first range, the index of the range following
\a offset is returned. Otherwise \c CountRanges() is returned.
\return The insertion index for a range starting at \a offset.
*/
template<typename Value>
int32
RangeArray<Value>::InsertionIndex(const Value& offset) const
{
int32 lower = 0;
int32 upper = CountRanges();
while (lower < upper) {
int32 mid = (lower + upper) / 2;
const RangeType& range = RangeAt(mid);
if (offset >= range.EndOffset())
lower = mid + 1;
else
upper = mid;
}
return lower;
}
template<typename Value>
inline RangeArray<Value>&
RangeArray<Value>::operator=(const RangeArray<Value>& other)
{
fRanges = other.fRanges;
return *this;
}
}
using BPrivate::Range;
using BPrivate::RangeArray;
#endif