blob: bf78cfec9c3133080c0942a9dd816ab1a0b1fee6 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "pdf/range_set.h"
#include <algorithm>
#include <sstream>
#include <vector>
namespace chrome_pdf {
namespace {
gfx::Range FixDirection(const gfx::Range& range) {
if (!range.IsValid() || !range.is_reversed())
return range;
return gfx::Range(range.end() + 1, range.start() + 1);
}
} // namespace
RangeSet::RangeSet() = default;
RangeSet::RangeSet(const gfx::Range& range) {
Union(range);
}
RangeSet::RangeSet(const RangeSet& range_set) = default;
RangeSet::RangeSet(RangeSet&& range_set)
: ranges_(std::move(range_set.ranges_)) {}
RangeSet& RangeSet::operator=(const RangeSet& other) = default;
RangeSet::~RangeSet() = default;
bool RangeSet::operator==(const RangeSet& other) const {
return other.ranges_ == ranges_;
}
bool RangeSet::operator!=(const RangeSet& other) const {
return other.ranges_ != ranges_;
}
void RangeSet::Union(const gfx::Range& range) {
if (range.is_empty())
return;
gfx::Range fixed_range = FixDirection(range);
if (IsEmpty()) {
ranges_.insert(fixed_range);
return;
}
auto start = ranges_.upper_bound(fixed_range);
if (start != ranges_.begin())
--start; // start now points to the key equal or lower than offset.
if (start->end() < fixed_range.start())
++start; // start element is entirely before current range, skip it.
auto end = ranges_.upper_bound(gfx::Range(fixed_range.end()));
if (start == end) { // No ranges to merge.
ranges_.insert(fixed_range);
return;
}
--end;
int new_start = std::min<size_t>(start->start(), fixed_range.start());
int new_end = std::max(end->end(), fixed_range.end());
ranges_.erase(start, ++end);
ranges_.insert(gfx::Range(new_start, new_end));
}
void RangeSet::Union(const RangeSet& range_set) {
if (&range_set == this)
return;
for (const auto& it : range_set.ranges()) {
Union(it);
}
}
bool RangeSet::Contains(uint32_t point) const {
return Contains(gfx::Range(point, point + 1));
}
bool RangeSet::Contains(const gfx::Range& range) const {
if (range.is_empty())
return false;
const gfx::Range fixed_range = FixDirection(range);
auto it = ranges().upper_bound(fixed_range);
if (it == ranges().begin())
return false; // No ranges includes range.start().
--it; // Now it starts equal or before range.start().
return it->end() >= fixed_range.end();
}
bool RangeSet::Contains(const RangeSet& range_set) const {
for (const auto& it : range_set.ranges()) {
if (!Contains(it))
return false;
}
return true;
}
bool RangeSet::Intersects(const gfx::Range& range) const {
if (IsEmpty() || range.is_empty())
return false;
const gfx::Range fixed_range = FixDirection(range);
auto start = ranges_.upper_bound(fixed_range);
if (start != ranges_.begin()) {
--start;
}
// start now points to the key equal or lower than range.start().
if (start->end() < range.start()) {
// start element is entirely before current range, skip it.
++start;
}
auto end = ranges_.upper_bound(gfx::Range(fixed_range.end()));
for (auto it = start; it != end; ++it) {
if (fixed_range.end() > it->start() && fixed_range.start() < it->end())
return true;
}
return false;
}
bool RangeSet::Intersects(const RangeSet& range_set) const {
for (const auto& it : range_set.ranges()) {
if (Intersects(it))
return true;
}
return false;
}
void RangeSet::Intersect(const gfx::Range& range) {
Intersect(RangeSet(range));
}
void RangeSet::Intersect(const RangeSet& range_set) {
if (IsEmpty())
return;
RangesContainer new_ranges;
for (const auto& range : range_set.ranges()) {
auto start = ranges_.upper_bound(range);
if (start != ranges_.begin())
--start; // start now points to the key equal or lower than
// range.start().
if (start->end() < range.start())
++start; // start element is entirely before current range, skip it.
auto end = ranges_.upper_bound(gfx::Range(range.end()));
if (start == end) { // No data in the current range available.
continue;
}
for (auto it = start; it != end; ++it) {
const gfx::Range new_range = range.Intersect(*it);
if (!new_range.is_empty()) {
new_ranges.insert(new_range);
}
}
}
new_ranges.swap(ranges_);
}
void RangeSet::Subtract(const gfx::Range& range) {
if (range.is_empty() || IsEmpty())
return;
const gfx::Range fixed_range = FixDirection(range);
auto start = ranges_.upper_bound(fixed_range);
if (start != ranges_.begin())
--start; // start now points to the key equal or lower than
// range.start().
if (start->end() < fixed_range.start())
++start; // start element is entirely before current range, skip it.
auto end = ranges_.upper_bound(gfx::Range(fixed_range.end()));
if (start == end) { // No data in the current range available.
return;
}
std::vector<gfx::Range> new_ranges;
for (auto it = start; it != end; ++it) {
const gfx::Range left(it->start(),
std::min(it->end(), fixed_range.start()));
const gfx::Range right(std::max(it->start(), fixed_range.end()), it->end());
if (!left.is_empty() && !left.is_reversed()) {
new_ranges.push_back(left);
}
if (!right.is_empty() && !right.is_reversed() && right != left) {
new_ranges.push_back(right);
}
}
ranges_.erase(start, end);
for (const auto& it : new_ranges) {
ranges_.insert(it);
}
}
void RangeSet::Subtract(const RangeSet& range_set) {
if (&range_set == this) {
ranges_.clear();
return;
}
for (const auto& range : range_set.ranges()) {
Subtract(range);
}
}
void RangeSet::Xor(const gfx::Range& range) {
Xor(RangeSet(range));
}
void RangeSet::Xor(const RangeSet& range_set) {
RangeSet tmp = *this;
tmp.Intersect(range_set);
Union(range_set);
Subtract(tmp);
}
bool RangeSet::IsEmpty() const {
return ranges().empty();
}
void RangeSet::Clear() {
ranges_.clear();
}
gfx::Range RangeSet::First() const {
return *ranges().begin();
}
gfx::Range RangeSet::Last() const {
return *ranges().rbegin();
}
std::string RangeSet::ToString() const {
std::stringstream ss;
ss << "{";
for (const auto& it : ranges()) {
ss << "[" << it.start() << "," << it.end() << ")";
}
ss << "}";
return ss.str();
}
} // namespace chrome_pdf
std::ostream& operator<<(std::ostream& os,
const chrome_pdf::RangeSet& range_set) {
return (os << range_set.ToString());
}