Skip to content

Commit 2efe2dc

Browse files
committed
Implement a JSON::try_emplace_before method
Fixes: #1828 Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent 3a031c0 commit 2efe2dc

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

src/core/json/include/sourcemeta/core/json_object.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,39 @@ template <typename Key, typename Value, typename Hash> class FlatMap {
7878
return key_hash;
7979
}
8080

81+
auto try_emplace_before(const key_type &key, const mapped_type &value,
82+
const key_type &suffix) -> hash_type {
83+
const auto key_hash{this->hash(key)};
84+
const auto suffix_hash{this->hash(suffix)};
85+
86+
if (this->hasher.is_perfect(key_hash)) {
87+
for (auto iterator = this->data.begin(); iterator != this->data.end();
88+
++iterator) {
89+
if (iterator->hash == key_hash) {
90+
iterator->second = value;
91+
return key_hash;
92+
} else if (iterator->hash == suffix_hash && iterator->first == suffix) {
93+
this->data.insert(iterator, {key, value, key_hash});
94+
return key_hash;
95+
}
96+
}
97+
} else {
98+
for (auto iterator = this->data.begin(); iterator != this->data.end();
99+
++iterator) {
100+
if (iterator->hash == key_hash && iterator->first == key) {
101+
iterator->second = value;
102+
return key_hash;
103+
} else if (iterator->hash == suffix_hash && iterator->first == suffix) {
104+
this->data.insert(iterator, {key, value, key_hash});
105+
return key_hash;
106+
}
107+
}
108+
}
109+
110+
this->data.push_back({key, value, key_hash});
111+
return key_hash;
112+
}
113+
81114
auto emplace(const key_type &key, const mapped_type &value) -> hash_type {
82115
const auto key_hash{this->hash(key)};
83116

src/core/json/include/sourcemeta/core/json_value.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,22 @@ class SOURCEMETA_CORE_JSON_EXPORT JSON {
12911291
/// ```
12921292
auto assign(const String &key, JSON &&value) -> void;
12931293

1294+
/// This method sets or updates an object key. However, it will try to insert
1295+
/// the key _before_ the given one if possible.
1296+
///
1297+
/// ```cpp
1298+
/// #include <sourcemeta/core/json.h>
1299+
/// #include <cassert>
1300+
///
1301+
/// sourcemeta::core::JSON document =
1302+
/// sourcemeta::core::parse_json("{ \"foo\": true }");
1303+
/// const sourcemeta::core::JSON value{false};
1304+
/// document.try_assign_before("bar", value, "foo");
1305+
/// assert(document.as_object().cbegin()->first == "bar");
1306+
/// ```
1307+
auto try_assign_before(const String &key, const JSON &value,
1308+
const String &other) -> void;
1309+
12941310
/// This method sets an object key if it is not already defined. For example:
12951311
///
12961312
/// ```cpp

src/core/json/json_value.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,12 @@ auto JSON::assign(const JSON::String &key, JSON &&value) -> void {
809809
this->data_object.data.emplace(key, value);
810810
}
811811

812+
auto JSON::try_assign_before(const String &key, const JSON &value,
813+
const String &other) -> void {
814+
assert(this->is_object());
815+
this->data_object.data.try_emplace_before(key, value, other);
816+
}
817+
812818
auto JSON::assign_if_missing(const JSON::String &key, const JSON &value)
813819
-> void {
814820
assert(this->is_object());

test/json/json_object_test.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,3 +862,81 @@ TEST(JSON_object, at_or_not_defined_with_hash) {
862862
EXPECT_TRUE(result.is_integer());
863863
EXPECT_EQ(result.to_integer(), 99);
864864
}
865+
866+
TEST(JSON_object, try_assign_before_exists) {
867+
sourcemeta::core::JSON document{{"foo", sourcemeta::core::JSON{1}},
868+
{"bar", sourcemeta::core::JSON{2}}};
869+
870+
document.try_assign_before("baz", sourcemeta::core::JSON{99}, "bar");
871+
872+
std::vector<sourcemeta::core::JSON::String> properties;
873+
for (const auto &entry : document.as_object()) {
874+
properties.emplace_back(entry.first);
875+
}
876+
877+
EXPECT_EQ(properties.size(), 3);
878+
EXPECT_EQ(properties.at(0), "foo");
879+
EXPECT_EQ(properties.at(1), "baz");
880+
EXPECT_EQ(properties.at(2), "bar");
881+
882+
EXPECT_EQ(document.at("foo").to_integer(), 1);
883+
EXPECT_EQ(document.at("bar").to_integer(), 2);
884+
EXPECT_EQ(document.at("baz").to_integer(), 99);
885+
}
886+
887+
TEST(JSON_object, try_assign_before_not_exists) {
888+
sourcemeta::core::JSON document{{"foo", sourcemeta::core::JSON{1}},
889+
{"bar", sourcemeta::core::JSON{2}}};
890+
891+
document.try_assign_before("baz", sourcemeta::core::JSON{99}, "qux");
892+
893+
std::vector<sourcemeta::core::JSON::String> properties;
894+
for (const auto &entry : document.as_object()) {
895+
properties.emplace_back(entry.first);
896+
}
897+
898+
EXPECT_EQ(properties.size(), 3);
899+
EXPECT_EQ(properties.at(0), "foo");
900+
EXPECT_EQ(properties.at(1), "bar");
901+
EXPECT_EQ(properties.at(2), "baz");
902+
903+
EXPECT_EQ(document.at("foo").to_integer(), 1);
904+
EXPECT_EQ(document.at("bar").to_integer(), 2);
905+
EXPECT_EQ(document.at("baz").to_integer(), 99);
906+
}
907+
908+
TEST(JSON_object, try_assign_before_exists_front) {
909+
sourcemeta::core::JSON document{{"foo", sourcemeta::core::JSON{1}},
910+
{"bar", sourcemeta::core::JSON{2}}};
911+
912+
document.try_assign_before("baz", sourcemeta::core::JSON{99}, "foo");
913+
914+
std::vector<sourcemeta::core::JSON::String> properties;
915+
for (const auto &entry : document.as_object()) {
916+
properties.emplace_back(entry.first);
917+
}
918+
919+
EXPECT_EQ(properties.size(), 3);
920+
EXPECT_EQ(properties.at(0), "baz");
921+
EXPECT_EQ(properties.at(1), "foo");
922+
EXPECT_EQ(properties.at(2), "bar");
923+
924+
EXPECT_EQ(document.at("foo").to_integer(), 1);
925+
EXPECT_EQ(document.at("bar").to_integer(), 2);
926+
EXPECT_EQ(document.at("baz").to_integer(), 99);
927+
}
928+
929+
TEST(JSON_object, try_assign_before_empty) {
930+
sourcemeta::core::JSON document{sourcemeta::core::JSON::make_object()};
931+
932+
document.try_assign_before("baz", sourcemeta::core::JSON{99}, "foo");
933+
934+
std::vector<sourcemeta::core::JSON::String> properties;
935+
for (const auto &entry : document.as_object()) {
936+
properties.emplace_back(entry.first);
937+
}
938+
939+
EXPECT_EQ(properties.size(), 1);
940+
EXPECT_EQ(properties.at(0), "baz");
941+
EXPECT_EQ(document.at("baz").to_integer(), 99);
942+
}

0 commit comments

Comments
 (0)