added prune() to table and array

This commit is contained in:
Mark Gillard
2021-11-12 19:37:02 +02:00
parent 64dd1dbac5
commit 32df34add2
9 changed files with 250 additions and 26 deletions

View File

@@ -37,6 +37,7 @@ code changes at callsites or in build systems are indicated with ⚠️.
- added `operator->` to `toml::value` for class types
- added `parse_benchmark` example
- added `toml::array::at()` (same semantics as `std::vector::at()`)
- added `toml::array::prune()`
- added `toml::array::replace()` (#109) (@LebJe)
- added `toml::array::resize()` param `default_init_flags`
- added `toml::format_flags::allow_binary_integers`
@@ -50,6 +51,7 @@ code changes at callsites or in build systems are indicated with ⚠️.
- added `toml::table::at()` (same semantics as `std::map::at()`)
- added `toml::table::emplace_hint()` (same semantics as `std::map::emplace_hint()`)
- added `toml::table::lower_bound()` (same semantics as `std::map::lower_bound()`)
- added `toml::table::prune()`
- added `toml::yaml_formatter`
- added `TOML_ENABLE_FORMATTERS` option
- added clang's enum annotation attributes to all enums

View File

@@ -1339,12 +1339,43 @@ TOML_NAMESPACE_START
/// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself (rvalue overload).
///
/// \returns An rvalue reference to the array.
TOML_API
array&& flatten() &&
{
return static_cast<toml::array&&>(this->flatten());
}
/// \brief Removes empty child arrays and tables.
///
/// \detail \cpp
///
/// auto arr = toml::array{ 1, 2, toml::array{ }, toml::array{ 3, toml::array{ } }, 4 };
/// std::cout << arr << "\n";
///
/// arr.prune();
/// std::cout << arr << "\n";
/// \ecpp
///
/// \out
/// [ 1, 2, [], [ 3, [] ], 4 ]
/// [ 1, 2, [ 3 ], 4 ]
/// \eout
///
/// \param recursive Should child arrays and tables themselves be pruned?
///
/// \returns A reference to the array.
TOML_API
array& prune(bool recursive = true) & noexcept;
/// \brief Removes empty child arrays and tables (rvalue overload).
///
/// \param recursive Should child arrays and tables themselves be pruned?
///
/// \returns An rvalue reference to the array.
array&& prune(bool recursive = true) && noexcept
{
return static_cast<toml::array&&>(this->prune(recursive));
}
/// @}
private:

View File

@@ -243,6 +243,33 @@ TOML_NAMESPACE_START
return *this;
}
TOML_EXTERNAL_LINKAGE
array& array::prune(bool recursive)& noexcept
{
if (elems_.empty())
return *this;
for (size_t i = elems_.size(); i-- > 0u;)
{
if (auto arr = elems_[i]->as_array())
{
if (recursive)
arr->prune(true);
if (arr->empty())
elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(i));
}
else if (auto tbl = elems_[i]->as_table())
{
if (recursive)
tbl->prune(true);
if (tbl->empty())
elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(i));
}
}
return *this;
}
}
TOML_NAMESPACE_END;

View File

@@ -98,7 +98,6 @@ TOML_NAMESPACE_START
class toml_formatter;
class json_formatter;
class yaml_formatter;
class xml_formatter;
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
#if TOML_EXCEPTIONS

View File

@@ -1555,6 +1555,38 @@ TOML_NAMESPACE_START
#endif // TOML_ENABLE_WINDOWS_COMPAT
/// \brief Removes empty child arrays and tables.
///
/// \detail \cpp
///
/// auto tbl = toml::table{ { "a", 1 }, { "b", toml::array{ } }, { "c", toml::array{ toml::table{}, toml::array{} } } };
/// std::cout << arr << "\n";
///
/// arr.prune();
/// std::cout << arr << "\n";
/// \ecpp
///
/// \out
/// { a = 1, b = [], c = [ {}, [] ] }
/// { a = 1 }
/// \eout
///
/// \param recursive Should child arrays and tables themselves be pruned?
///
/// \returns A reference to the table.
TOML_API
table& prune(bool recursive = true) & noexcept;
/// \brief Removes empty child arrays and tables (rvalue overload).
///
/// \param recursive Should child arrays and tables themselves be pruned?
///
/// \returns An rvalue reference to the table.
table&& prune(bool recursive = true) && noexcept
{
return static_cast<toml::table&&>(this->prune(recursive));
}
/// @}
private:

View File

@@ -204,6 +204,42 @@ TOML_NAMESPACE_START
{
return const_iterator{ map_.lower_bound(key) };
}
TOML_EXTERNAL_LINKAGE
table& table::prune(bool recursive)& noexcept
{
if (map_.empty())
return *this;
for (auto it = map_.begin(); it != map_.end();)
{
if (auto arr = it->second->as_array())
{
if (recursive)
arr->prune(true);
if (arr->empty())
{
it = map_.erase(it);
continue;
}
}
else if (auto tbl = it->second->as_table())
{
if (recursive)
tbl->prune(true);
if (tbl->empty())
{
it = map_.erase(it);
continue;
}
}
it++;
}
return *this;
}
}
TOML_NAMESPACE_END;

View File

@@ -34,6 +34,7 @@ TOML_DISABLE_SUGGEST_ATTR_WARNINGS;
#include "impl/value.h"
#include "impl/make_node.h"
#include "impl/array.h"
#include "impl/key.h"
#include "impl/table.h"
#include "impl/utf8.h"
#include "impl/parse_error.h"

View File

@@ -441,16 +441,16 @@ TEST_CASE("arrays - insertion and erasure")
arr.clear();
it = arr.insert(arr.cbegin(), L"test");
REQUIRE(*arr.get_as<std::string>(0u) == "test"sv);
CHECK(*arr.get_as<std::string>(0u) == "test"sv);
it = arr.emplace<std::string>(arr.cbegin(), L"test2"sv);
REQUIRE(*arr.get_as<std::string>(0u) == "test2"sv);
CHECK(*arr.get_as<std::string>(0u) == "test2"sv);
arr.push_back(L"test3"s);
REQUIRE(*arr.back().as_string() == "test3"sv);
CHECK(*arr.back().as_string() == "test3"sv);
arr.emplace_back<std::string>(L"test4");
REQUIRE(*arr.back().as_string() == "test4"sv);
CHECK(*arr.back().as_string() == "test4"sv);
#endif // TOML_ENABLE_WINDOWS_COMPAT
}
@@ -475,7 +475,7 @@ TEST_CASE("arrays - flattening")
11 },
};
arr.flatten();
REQUIRE(arr == array{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 });
CHECK(arr == array{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 });
}
{
@@ -484,37 +484,53 @@ TEST_CASE("arrays - flattening")
array{ array{}, array{ array{}, array{} }, array{} },
array{ array{ array{ array{ array{ array{ 1 } } } } } } };
arr.flatten();
REQUIRE(arr == array{ 1 });
CHECK(arr == array{ 1 });
}
}
TEST_CASE("arrays - pruning")
{
// [ 1, [ 2, [], 3 ], { 4 = 5, 6 = 7 }, [], 8, [{}], 9, 10 ]
const auto arr =
array{ 1, array{ 2, array{}, 3 }, table{ { "4", 5 }, { "6", array{} } }, array{}, 8, array{ table{} }, 9, 10 };
// [ 1, [ 2, 3 ], { 4 = 5, 6 = 7 }, 8, 9, 10 ]
const auto pruned_recursive = array{ 1, array{ 2, 3 }, table{ { "4", 5 } }, 8, 9, 10 };
CHECK(array{ arr }.prune(true) == pruned_recursive);
// [ 1, [ 2, [], 3 ], { 4 = 5, 6 = 7 }, [], 8, [{}], 9, 10 ]
const auto pruned_flat =
array{ 1, array{ 2, array{}, 3 }, table{ { "4", 5 }, { "6", array{} } }, 8, array{ table{} }, 9, 10 };
CHECK(array{ arr }.prune(false) == pruned_flat);
}
TEST_CASE("arrays - resizing and truncation")
{
array arr{ 1, 2, 3, 4, 5 };
REQUIRE(arr.size() == 5u);
CHECK(arr.size() == 5u);
// truncate with no change
arr.truncate(5u);
REQUIRE(arr.size() == 5u);
REQUIRE(arr == array{ 1, 2, 3, 4, 5 });
CHECK(arr.size() == 5u);
CHECK(arr == array{ 1, 2, 3, 4, 5 });
// truncate down to three elements
arr.truncate(3u);
REQUIRE(arr.size() == 3u);
REQUIRE(arr == array{ 1, 2, 3 });
CHECK(arr.size() == 3u);
CHECK(arr == array{ 1, 2, 3 });
// resize down to two elements
arr.resize(2u, 42);
REQUIRE(arr.size() == 2u);
REQUIRE(arr == array{ 1, 2 });
CHECK(arr.size() == 2u);
CHECK(arr == array{ 1, 2 });
// resize with no change
arr.resize(2u, 42);
REQUIRE(arr.size() == 2u);
REQUIRE(arr == array{ 1, 2 });
CHECK(arr.size() == 2u);
CHECK(arr == array{ 1, 2 });
// resize up to six elements
arr.resize(6u, 42);
REQUIRE(arr.size() == 6u);
REQUIRE(arr == array{ 1, 2, 42, 42, 42, 42 });
CHECK(arr.size() == 6u);
CHECK(arr == array{ 1, 2, 42, 42, 42, 42 });
}

View File

@@ -1112,7 +1112,6 @@ TOML_NAMESPACE_START
class toml_formatter;
class json_formatter;
class yaml_formatter;
class xml_formatter;
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
#if TOML_EXCEPTIONS
@@ -5540,12 +5539,19 @@ TOML_NAMESPACE_START
TOML_API
array& flatten() &;
TOML_API
array&& flatten() &&
{
return static_cast<toml::array&&>(this->flatten());
}
TOML_API
array& prune(bool recursive = true) & noexcept;
array&& prune(bool recursive = true) && noexcept
{
return static_cast<toml::array&&>(this->prune(recursive));
}
private:
TOML_NODISCARD
@@ -5625,12 +5631,8 @@ TOML_NAMESPACE_END;
#endif
TOML_POP_WARNINGS;
//******** impl/table.h **********************************************************************************************
//******** impl/key.h ************************************************************************************************
TOML_DISABLE_WARNINGS;
#include <map>
#include <iterator>
TOML_ENABLE_WARNINGS;
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#pragma push_macro("min")
@@ -5863,6 +5865,13 @@ TOML_IMPL_NAMESPACE_END;
#pragma pop_macro("max")
#endif
TOML_POP_WARNINGS;
//******** impl/table.h **********************************************************************************************
TOML_DISABLE_WARNINGS;
#include <map>
#include <iterator>
TOML_ENABLE_WARNINGS;
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#pragma push_macro("min")
@@ -6779,6 +6788,14 @@ TOML_NAMESPACE_START
#endif // TOML_ENABLE_WINDOWS_COMPAT
TOML_API
table& prune(bool recursive = true) & noexcept;
table&& prune(bool recursive = true) && noexcept
{
return static_cast<toml::table&&>(this->prune(recursive));
}
private:
TOML_NODISCARD
@@ -9770,6 +9787,33 @@ TOML_NAMESPACE_START
return *this;
}
TOML_EXTERNAL_LINKAGE
array& array::prune(bool recursive)& noexcept
{
if (elems_.empty())
return *this;
for (size_t i = elems_.size(); i-- > 0u;)
{
if (auto arr = elems_[i]->as_array())
{
if (recursive)
arr->prune(true);
if (arr->empty())
elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(i));
}
else if (auto tbl = elems_[i]->as_table())
{
if (recursive)
tbl->prune(true);
if (tbl->empty())
elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(i));
}
}
return *this;
}
}
TOML_NAMESPACE_END;
@@ -9978,6 +10022,42 @@ TOML_NAMESPACE_START
{
return const_iterator{ map_.lower_bound(key) };
}
TOML_EXTERNAL_LINKAGE
table& table::prune(bool recursive)& noexcept
{
if (map_.empty())
return *this;
for (auto it = map_.begin(); it != map_.end();)
{
if (auto arr = it->second->as_array())
{
if (recursive)
arr->prune(true);
if (arr->empty())
{
it = map_.erase(it);
continue;
}
}
else if (auto tbl = it->second->as_table())
{
if (recursive)
tbl->prune(true);
if (tbl->empty())
{
it = map_.erase(it);
continue;
}
}
it++;
}
return *this;
}
}
TOML_NAMESPACE_END;