Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object::stable_erase #749

Merged
merged 5 commits into from Sep 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
119 changes: 89 additions & 30 deletions include/boost/json/impl/object.ipp
Expand Up @@ -508,40 +508,17 @@ object::
erase(const_iterator pos) noexcept ->
iterator
{
auto p = begin() + (pos - begin());
if(t_->is_small())
{
p->~value_type();
--t_->size;
auto const pb = end();
if(p != end())
{
return do_erase(pos,
[this](iterator p) {
// the casts silence warnings
std::memcpy(
static_cast<void*>(p),
static_cast<void const*>(pb),
static_cast<void const*>(end()),
sizeof(*p));
}
return p;
}
remove(t_->bucket(p->key()), *p);
p->~value_type();
--t_->size;
auto const pb = end();
if(p != end())
{
auto& head = t_->bucket(pb->key());
remove(head, *pb);
// the casts silence warnings
std::memcpy(
static_cast<void*>(p),
static_cast<void const*>(pb),
sizeof(*p));
access::next(*p) = head;
head = static_cast<
index_t>(p - begin());
}
return p;
},
[this](iterator p) {
reindex_relocate(end(), p);
});
}

auto
Expand All @@ -556,6 +533,39 @@ erase(string_view key) noexcept ->
return 1;
}

auto
object::
stable_erase(const_iterator pos) noexcept ->
grisumbras marked this conversation as resolved.
Show resolved Hide resolved
iterator
{
return do_erase(pos,
[this](iterator p) {
// the casts silence warnings
std::memmove(
static_cast<void*>(p),
static_cast<void const*>(p + 1),
sizeof(*p) * (end() - p));
},
[this](iterator p) {
for (; p != end(); ++p)
{
reindex_relocate(p + 1, p);
}
});
}

auto
object::
stable_erase(string_view key) noexcept ->
std::size_t
{
auto it = find(key);
if(it == end())
return 0;
stable_erase(it);
return 1;
}

void
object::
swap(object& other)
Expand Down Expand Up @@ -830,6 +840,55 @@ destroy(
(--last)->~key_value_pair();
}

template<class FS, class FB>
auto
object::
do_erase(
const_iterator pos,
FS small_reloc,
FB big_reloc) noexcept
-> iterator
{
auto p = begin() + (pos - begin());
if(t_->is_small())
{
p->~value_type();
--t_->size;
if(p != end())
{
small_reloc(p);
}
return p;
}
remove(t_->bucket(p->key()), *p);
p->~value_type();
--t_->size;
if(p != end())
{
big_reloc(p);
}
return p;
}

void
object::
reindex_relocate(
key_value_pair* src,
key_value_pair* dst) noexcept
{
BOOST_ASSERT(! t_->is_small());
auto& head = t_->bucket(src->key());
remove(head, *src);
// the casts silence warnings
std::memcpy(
static_cast<void*>(dst),
static_cast<void const*>(src),
sizeof(*dst));
access::next(*dst) = head;
head = static_cast<
index_t>(dst - begin());
}

BOOST_JSON_NS_END

//----------------------------------------------------------
Expand Down
75 changes: 71 additions & 4 deletions include/boost/json/object.hpp
Expand Up @@ -1122,20 +1122,23 @@ class object
/** Erase an element

Remove the element pointed to by `pos`, which must
be valid and dereferenceable. Thus the @ref end()
iterator (which is valid but cannot be dereferenced)
cannot be used as a value for `pos`.
be valid and dereferenceable.
References and iterators to the erased element are
invalidated. Other iterators and references are not
invalidated.

@note

The @ref end() iterator (which is valid but cannot be
dereferenced) cannot be used as a value for `pos`.

@par Complexity
Constant on average, worst case linear in @ref size().

@par Exception Safety
No-throw guarantee.

@return An iterator following the last removed element.
@return An iterator following the removed element.

@param pos An iterator pointing to the element to be
removed.
Expand Down Expand Up @@ -1166,6 +1169,56 @@ class object
std::size_t
erase(string_view key) noexcept;

/** Erase an element preserving order

Remove the element pointed to by `pos`, which must
be valid and dereferenceable.
References and iterators from `pos` to `end()`, both
included, are invalidated. Other iterators and references
are not invalidated.
The relative order of remaining elements is preserved.

@note

The @ref end() iterator (which is valid but cannot be
dereferenced) cannot be used as a value for `pos`.

@par Complexity
Linear in @ref size().

@par Exception Safety
No-throw guarantee.

@return An iterator following the removed element.

@param pos An iterator pointing to the element to be
removed.
*/
BOOST_JSON_DECL
iterator
stable_erase(const_iterator pos) noexcept;

/** Erase an element preserving order

Remove the element which matches `key`, if it exists.
All references and iterators are invalidated.
The relative order of remaining elements is preserved.

@par Complexity
Linear in @ref size().

@par Exception Safety
No-throw guarantee.

@return The number of elements removed, which will
be either 0 or 1.

@param key The key to match.
*/
BOOST_JSON_DECL
std::size_t
stable_erase(string_view key) noexcept;

/** Swap two objects.

Exchanges the contents of this object with another
Expand Down Expand Up @@ -1567,6 +1620,20 @@ class object
destroy(
key_value_pair* first,
key_value_pair* last) noexcept;

template<class FS, class FB>
auto
do_erase(
const_iterator pos,
FS small_reloc,
FB big_reloc) noexcept
-> iterator;

inline
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this use a little javadoc even though it's internal?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh..

by the way docca supports @param first, last The range to destroy now

void
reindex_relocate(
key_value_pair* src,
key_value_pair* dst) noexcept;
};

BOOST_JSON_NS_END
Expand Down
56 changes: 56 additions & 0 deletions test/object.cpp
Expand Up @@ -1204,6 +1204,62 @@ class object_test
}
}

// stable_erase(pos)
{
// small
{
object o(i0_);
auto it = o.stable_erase(o.find("10"));
BOOST_TEST(it->key() == "11");
BOOST_TEST(
it->value().as_int64() == 11);
BOOST_TEST(serialize(o) ==
R"({"0":0,"1":1,"2":2,"3":3,"4":4,)"
R"("5":5,"6":6,"7":7,"8":8,"9":9,)"
R"("11":11,"12":12,"13":13,"14":14,"15":15})");
BOOST_TEST(o.find("11") == it);
BOOST_TEST(o.find("14") == o.end() - 2);
}

// large
{
object o(i1_);
auto it = o.stable_erase(o.find("10"));
BOOST_TEST(it->key() == "11");
BOOST_TEST(
it->value().as_int64() == 11);
BOOST_TEST(serialize(o) ==
R"({"0":0,"1":1,"2":2,"3":3,"4":4,)"
R"("5":5,"6":6,"7":7,"8":8,"9":9,)"
R"("11":11,"12":12,"13":13,"14":14,"15":15,)"
R"("16":16,"17":17,"18":18,"19":19})");
BOOST_TEST(o.find("11") == it);
BOOST_TEST(o.find("18") == o.end() - 2);
}
}

// stable_erase(key)
{
{
object o({
{"a", 1},
{"b", true},
{"c", "hello"}});
BOOST_TEST(o.stable_erase("b2") == 0);
check(o, 3);
}

{
object o({
{"a", 1},
{"b", true},
{"b2", 2},
{"c", "hello"}});
BOOST_TEST(o.stable_erase("b2") == 1);
check(o, 4);
}
}

// swap(object&)
{
{
Expand Down