2015-08-31 11:20:20 -07:00
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Copyright 2015 Realm Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "index_set.hpp"
|
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
#include <realm/util/assert.hpp>
|
|
|
|
|
2015-08-31 11:20:20 -07:00
|
|
|
using namespace realm;
|
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
const size_t IndexSet::npos;
|
|
|
|
|
|
|
|
IndexSet::IndexSet(std::initializer_list<size_t> values)
|
|
|
|
{
|
|
|
|
for (size_t v : values)
|
|
|
|
add(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IndexSet::contains(size_t index) const
|
|
|
|
{
|
|
|
|
auto it = const_cast<IndexSet*>(this)->find(index);
|
|
|
|
return it != m_ranges.end() && it->first <= index;
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:00:31 -08:00
|
|
|
size_t IndexSet::count(size_t start_index, size_t end_index) const
|
|
|
|
{
|
|
|
|
auto it = const_cast<IndexSet*>(this)->find(start_index);
|
2016-03-11 16:51:35 -08:00
|
|
|
const auto end = m_ranges.end();
|
|
|
|
if (it == end || it->first >= end_index) {
|
|
|
|
return 0;
|
2016-03-08 19:00:31 -08:00
|
|
|
}
|
2016-03-11 16:51:35 -08:00
|
|
|
if (it->second >= end_index)
|
|
|
|
return std::min(it->second, end_index) - std::max(it->first, start_index);
|
|
|
|
|
|
|
|
// These checks are somewhat redundant, but this loop is hot so pulling instructions out of it helps
|
|
|
|
size_t ret = it->second - std::max(it->first, start_index);
|
|
|
|
for (++it; it != end && it->second < end_index; ++it) {
|
|
|
|
ret += it->second - it->first;
|
|
|
|
}
|
|
|
|
if (it != end && it->first < end_index)
|
|
|
|
ret += end_index - it->first;
|
2016-03-08 19:00:31 -08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-09-02 13:51:28 -07:00
|
|
|
IndexSet::iterator IndexSet::find(size_t index)
|
2015-08-31 11:20:20 -07:00
|
|
|
{
|
2016-01-21 10:11:19 -08:00
|
|
|
return find(index, m_ranges.begin());
|
|
|
|
}
|
|
|
|
|
|
|
|
IndexSet::iterator IndexSet::find(size_t index, iterator it)
|
|
|
|
{
|
2016-03-14 15:13:19 -07:00
|
|
|
return std::lower_bound(it, m_ranges.end(), std::make_pair(size_t(0), index + 1),
|
|
|
|
[&](auto const& a, auto const& b) { return a.second < b.second; });
|
2015-08-31 11:20:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::add(size_t index)
|
|
|
|
{
|
|
|
|
do_add(find(index), index);
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
void IndexSet::add(IndexSet const& other)
|
2015-08-31 11:20:20 -07:00
|
|
|
{
|
2016-01-21 10:11:19 -08:00
|
|
|
auto it = m_ranges.begin();
|
|
|
|
for (size_t index : other.as_indexes()) {
|
|
|
|
it = do_add(find(index, it), index);
|
2015-08-31 11:20:20 -07:00
|
|
|
}
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
2016-01-21 16:15:25 -08:00
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
size_t IndexSet::add_shifted(size_t index)
|
|
|
|
{
|
|
|
|
auto it = m_ranges.begin();
|
|
|
|
for (auto end = m_ranges.end(); it != end && it->first <= index; ++it) {
|
|
|
|
index += it->second - it->first;
|
2015-08-31 11:20:20 -07:00
|
|
|
}
|
2016-01-21 10:11:19 -08:00
|
|
|
do_add(it, index);
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::add_shifted_by(IndexSet const& shifted_by, IndexSet const& values)
|
|
|
|
{
|
2016-03-15 14:04:21 -07:00
|
|
|
if (values.empty())
|
|
|
|
return;
|
|
|
|
|
2016-03-10 16:58:43 -08:00
|
|
|
#ifdef REALM_DEBUG
|
|
|
|
size_t expected = std::distance(as_indexes().begin(), as_indexes().end());
|
|
|
|
for (auto index : values.as_indexes()) {
|
|
|
|
if (!shifted_by.contains(index))
|
|
|
|
++expected;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-15 14:04:21 -07:00
|
|
|
auto old_ranges = move(m_ranges);
|
|
|
|
m_ranges.reserve(std::max(old_ranges.size(), values.size()));
|
|
|
|
|
|
|
|
auto old_it = old_ranges.cbegin(), old_end = old_ranges.cend();
|
|
|
|
auto shift_it = shifted_by.m_ranges.cbegin(), shift_end = shifted_by.m_ranges.cend();
|
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
size_t skip_until = 0;
|
2016-03-15 14:04:21 -07:00
|
|
|
size_t old_shift = 0;
|
|
|
|
size_t new_shift = 0;
|
2016-01-21 10:11:19 -08:00
|
|
|
for (size_t index : values.as_indexes()) {
|
2016-03-15 14:04:21 -07:00
|
|
|
for (; shift_it != shift_end && shift_it->first <= index; ++shift_it) {
|
|
|
|
new_shift += shift_it->second - shift_it->first;
|
|
|
|
skip_until = shift_it->second;
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
2016-03-15 14:04:21 -07:00
|
|
|
if (index < skip_until)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (; old_it != old_end && old_it->first <= index - new_shift + old_shift; ++old_it) {
|
|
|
|
for (size_t i = old_it->first; i < old_it->second; ++i)
|
|
|
|
add_back(i);
|
|
|
|
old_shift += old_it->second - old_it->first;
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
2016-03-15 14:04:21 -07:00
|
|
|
|
|
|
|
REALM_ASSERT(index >= new_shift);
|
|
|
|
add_back(index - new_shift + old_shift);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_it != old_end) {
|
|
|
|
if (!empty() && old_it->first == m_ranges.back().second) {
|
|
|
|
m_ranges.back().second = old_it->second;
|
|
|
|
++old_it;
|
|
|
|
}
|
|
|
|
copy(old_it, old_end, back_inserter(m_ranges));
|
2015-08-31 11:20:20 -07:00
|
|
|
}
|
2016-03-10 16:58:43 -08:00
|
|
|
|
|
|
|
REALM_ASSERT_DEBUG(std::distance(as_indexes().begin(), as_indexes().end()) == expected);
|
2015-08-31 11:20:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::set(size_t len)
|
|
|
|
{
|
|
|
|
m_ranges.clear();
|
|
|
|
if (len) {
|
|
|
|
m_ranges.push_back({0, len});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
void IndexSet::insert_at(size_t index, size_t count)
|
2015-08-31 11:20:20 -07:00
|
|
|
{
|
2016-01-21 10:11:19 -08:00
|
|
|
REALM_ASSERT(count > 0);
|
|
|
|
|
2015-08-31 11:20:20 -07:00
|
|
|
auto pos = find(index);
|
2016-01-21 10:11:19 -08:00
|
|
|
bool in_existing = false;
|
2015-08-31 11:20:20 -07:00
|
|
|
if (pos != m_ranges.end()) {
|
2016-01-21 10:11:19 -08:00
|
|
|
if (pos->first <= index)
|
|
|
|
in_existing = true;
|
|
|
|
else
|
|
|
|
pos->first += count;
|
|
|
|
pos->second += count;
|
2015-08-31 11:20:20 -07:00
|
|
|
for (auto it = pos + 1; it != m_ranges.end(); ++it) {
|
2016-01-21 10:11:19 -08:00
|
|
|
it->first += count;
|
|
|
|
it->second += count;
|
2015-08-31 11:20:20 -07:00
|
|
|
}
|
|
|
|
}
|
2016-01-21 10:11:19 -08:00
|
|
|
if (!in_existing) {
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
|
|
pos = do_add(pos, index + i) + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::insert_at(IndexSet const& positions)
|
|
|
|
{
|
2016-03-15 14:04:21 -07:00
|
|
|
if (positions.empty())
|
|
|
|
return;
|
|
|
|
if (empty()) {
|
|
|
|
m_ranges = positions.m_ranges;
|
|
|
|
return;
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
2016-03-15 14:04:21 -07:00
|
|
|
|
|
|
|
auto old_ranges = move(m_ranges);
|
|
|
|
m_ranges.reserve(std::max(m_ranges.size(), positions.m_ranges.size()));
|
|
|
|
|
|
|
|
IndexIterator begin1 = old_ranges.cbegin(), begin2 = positions.m_ranges.cbegin();
|
|
|
|
IndexIterator end1 = old_ranges.cend(), end2 = positions.m_ranges.cend();
|
|
|
|
|
|
|
|
size_t shift = 0;
|
|
|
|
while (begin1 != end1 && begin2 != end2) {
|
|
|
|
if (*begin1 + shift < *begin2) {
|
|
|
|
add_back(*begin1++ + shift);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++shift;
|
|
|
|
add_back(*begin2++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (; begin1 != end1; ++begin1)
|
|
|
|
add_back(*begin1 + shift);
|
|
|
|
for (; begin2 != end2; ++begin2)
|
|
|
|
add_back(*begin2);
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::shift_for_insert_at(size_t index, size_t count)
|
|
|
|
{
|
|
|
|
REALM_ASSERT(count > 0);
|
|
|
|
|
|
|
|
auto it = find(index);
|
|
|
|
if (it == m_ranges.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (it->first < index) {
|
|
|
|
// split the range so that we can exclude `index`
|
|
|
|
auto old_second = it->second;
|
|
|
|
it->second = index;
|
|
|
|
it = m_ranges.insert(it + 1, {index, old_second});
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; it != m_ranges.end(); ++it) {
|
|
|
|
it->first += count;
|
|
|
|
it->second += count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::shift_for_insert_at(realm::IndexSet const& values)
|
|
|
|
{
|
2016-03-15 14:04:21 -07:00
|
|
|
if (values.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
size_t shift = 0;
|
|
|
|
auto it = find(values.begin()->first);
|
|
|
|
for (auto range : values) {
|
|
|
|
for (; it != m_ranges.end() && it->second + shift <= range.first; ++it) {
|
|
|
|
it->first += shift;
|
|
|
|
it->second += shift;
|
|
|
|
}
|
|
|
|
if (it == m_ranges.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (it->first + shift < range.first) {
|
|
|
|
// split the range so that we can exclude `index`
|
|
|
|
auto old_second = it->second;
|
|
|
|
it->first += shift;
|
|
|
|
it->second = range.first;
|
|
|
|
it = m_ranges.insert(it + 1, {range.first - shift, old_second});
|
|
|
|
}
|
|
|
|
|
|
|
|
shift += range.second - range.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; it != m_ranges.end(); ++it) {
|
|
|
|
it->first += shift;
|
|
|
|
it->second += shift;
|
|
|
|
}
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::erase_at(size_t index)
|
|
|
|
{
|
|
|
|
auto it = find(index);
|
|
|
|
if (it != m_ranges.end())
|
|
|
|
do_erase(it, index);
|
|
|
|
}
|
|
|
|
|
2016-03-15 14:04:21 -07:00
|
|
|
void IndexSet::erase_at(IndexSet const& positions)
|
2016-01-21 10:11:19 -08:00
|
|
|
{
|
2016-03-15 14:04:21 -07:00
|
|
|
if (empty() || positions.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto old_ranges = move(m_ranges);
|
|
|
|
m_ranges.reserve(std::max(m_ranges.size(), positions.m_ranges.size()));
|
|
|
|
|
|
|
|
IndexIterator begin1 = old_ranges.cbegin(), begin2 = positions.m_ranges.cbegin();
|
|
|
|
IndexIterator end1 = old_ranges.cend(), end2 = positions.m_ranges.cend();
|
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
size_t shift = 0;
|
2016-03-15 14:04:21 -07:00
|
|
|
while (begin1 != end1 && begin2 != end2) {
|
|
|
|
if (*begin1 < *begin2) {
|
|
|
|
add_back(*begin1++ - shift);
|
|
|
|
}
|
|
|
|
else if (*begin1 == *begin2) {
|
|
|
|
++shift;
|
|
|
|
++begin1;
|
|
|
|
++begin2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++shift;
|
|
|
|
++begin2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (; begin1 != end1; ++begin1)
|
|
|
|
add_back(*begin1 - shift);
|
2015-08-31 11:20:20 -07:00
|
|
|
}
|
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
size_t IndexSet::erase_and_unshift(size_t index)
|
|
|
|
{
|
|
|
|
auto shifted = index;
|
|
|
|
auto it = m_ranges.begin(), end = m_ranges.end();
|
|
|
|
for (; it != end && it->second <= index; ++it) {
|
|
|
|
shifted -= it->second - it->first;
|
|
|
|
}
|
|
|
|
if (it == end)
|
|
|
|
return shifted;
|
|
|
|
|
|
|
|
if (it->first <= index)
|
|
|
|
shifted = npos;
|
|
|
|
|
|
|
|
do_erase(it, index);
|
|
|
|
|
|
|
|
return shifted;
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::do_erase(iterator it, size_t index)
|
|
|
|
{
|
|
|
|
if (it->first <= index) {
|
|
|
|
--it->second;
|
|
|
|
if (it->first == it->second) {
|
|
|
|
it = m_ranges.erase(it);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (it != m_ranges.begin() && (it - 1)->second + 1 == it->first) {
|
|
|
|
(it - 1)->second = it->second - 1;
|
|
|
|
it = m_ranges.erase(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; it != m_ranges.end(); ++it) {
|
|
|
|
--it->first;
|
|
|
|
--it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-07 15:02:17 -08:00
|
|
|
IndexSet::iterator IndexSet::do_remove(iterator it, size_t begin, size_t end)
|
2016-01-21 10:11:19 -08:00
|
|
|
{
|
2016-03-07 15:02:17 -08:00
|
|
|
for (it = find(begin, it); it != m_ranges.end() && it->first < end; it = find(begin, it)) {
|
|
|
|
// Trim off any part of the range to remove that's before the matching range
|
|
|
|
begin = std::max(it->first, begin);
|
|
|
|
|
|
|
|
// If the matching range extends to both sides of the range to remove,
|
|
|
|
// split it on the range to remove
|
|
|
|
if (it->first < begin && it->second > end) {
|
|
|
|
it = m_ranges.insert(it + 1, {end, it->second}) - 1;
|
|
|
|
it->second = begin;
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
|
|
|
|
2016-03-07 15:02:17 -08:00
|
|
|
// Range to delete now coverages (at least) one end of the matching range
|
|
|
|
if (begin == it->first && end >= it->second)
|
|
|
|
it = m_ranges.erase(it);
|
|
|
|
else if (begin == it->first)
|
|
|
|
it->first = end;
|
|
|
|
else
|
|
|
|
it->second = begin;
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
2016-03-07 15:02:17 -08:00
|
|
|
return it;
|
|
|
|
}
|
2016-01-21 10:11:19 -08:00
|
|
|
|
2016-03-07 15:02:17 -08:00
|
|
|
void IndexSet::remove(size_t index, size_t count)
|
|
|
|
{
|
|
|
|
do_remove(find(index), index, index + count);
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::remove(realm::IndexSet const& values)
|
2015-09-02 11:04:01 -07:00
|
|
|
{
|
|
|
|
auto it = m_ranges.begin();
|
2016-03-07 15:02:17 -08:00
|
|
|
for (auto range : values) {
|
|
|
|
it = do_remove(it, range.first, range.second);
|
2016-01-21 10:11:19 -08:00
|
|
|
if (it == m_ranges.end())
|
|
|
|
return;
|
2015-09-02 11:04:01 -07:00
|
|
|
}
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t IndexSet::shift(size_t index) const
|
|
|
|
{
|
|
|
|
for (auto range : m_ranges) {
|
|
|
|
if (range.first > index)
|
|
|
|
break;
|
|
|
|
index += range.second - range.first;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t IndexSet::unshift(size_t index) const
|
|
|
|
{
|
|
|
|
REALM_ASSERT_DEBUG(!contains(index));
|
2016-03-16 11:18:59 -07:00
|
|
|
return index - count(0, index);
|
2016-01-21 10:11:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::clear()
|
|
|
|
{
|
|
|
|
m_ranges.clear();
|
|
|
|
}
|
|
|
|
|
2016-03-15 14:04:21 -07:00
|
|
|
void IndexSet::add_back(size_t index)
|
|
|
|
{
|
|
|
|
if (m_ranges.empty())
|
|
|
|
m_ranges.push_back({index, index + 1});
|
|
|
|
else if (m_ranges.back().second == index)
|
|
|
|
++m_ranges.back().second;
|
|
|
|
else {
|
|
|
|
REALM_ASSERT_DEBUG(m_ranges.back().second < index);
|
|
|
|
m_ranges.push_back({index, index + 1});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:11:19 -08:00
|
|
|
IndexSet::iterator IndexSet::do_add(iterator it, size_t index)
|
|
|
|
{
|
|
|
|
verify();
|
|
|
|
bool more_before = it != m_ranges.begin(), valid = it != m_ranges.end();
|
|
|
|
REALM_ASSERT(!more_before || index >= (it - 1)->second);
|
|
|
|
if (valid && it->first <= index && it->second > index) {
|
|
|
|
// index is already in set
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
if (more_before && (it - 1)->second == index) {
|
|
|
|
// index is immediately after an existing range
|
|
|
|
++(it - 1)->second;
|
|
|
|
|
|
|
|
if (valid && (it - 1)->second == it->first) {
|
|
|
|
// index joins two existing ranges
|
|
|
|
(it - 1)->second = it->second;
|
|
|
|
return m_ranges.erase(it) - 1;
|
|
|
|
}
|
|
|
|
return it - 1;
|
|
|
|
}
|
|
|
|
if (valid && it->first == index + 1) {
|
|
|
|
// index is immediately before an existing range
|
|
|
|
--it->first;
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
// index is not next to an existing range
|
|
|
|
return m_ranges.insert(it, {index, index + 1});
|
|
|
|
}
|
|
|
|
|
|
|
|
void IndexSet::verify() const noexcept
|
|
|
|
{
|
|
|
|
#ifdef REALM_DEBUG
|
|
|
|
size_t prev_end = -1;
|
|
|
|
for (auto range : m_ranges) {
|
|
|
|
REALM_ASSERT(range.first < range.second);
|
|
|
|
REALM_ASSERT(prev_end == size_t(-1) || range.first > prev_end);
|
|
|
|
prev_end = range.second;
|
|
|
|
}
|
|
|
|
#endif
|
2015-09-02 11:04:01 -07:00
|
|
|
}
|