Skip to content

Commit

Permalink
RecurseVisitor: shallow visitation of hybrid nodes
Browse files Browse the repository at this point in the history
Summary: Implement shallow visitation of hybrid nodes for maps and lists.

Reviewed By: wilsonwinhi

Differential Revision: D65929681

fbshipit-source-id: b0d0f04da112fd711e88b602dc1e8b0ee2d9ba42
  • Loading branch information
Priyank Warkhede authored and facebook-github-bot committed Nov 15, 2024
1 parent 8cd4cce commit 3f8b2b5
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 29 deletions.
82 changes: 60 additions & 22 deletions fboss/thrift_cow/visitors/RecurseVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ struct RecurseVisitOptions {
RecurseVisitMode mode,
RecurseVisitOrder order,
bool outputIdPaths = false,
bool recurseIntoHybridNodes = false)
bool hybridNodeDeepTraversal = false)
: mode(mode),
order(order),
outputIdPaths(outputIdPaths),
recurseIntoHybridNodes(recurseIntoHybridNodes) {}
hybridNodeDeepTraversal(hybridNodeDeepTraversal) {}
RecurseVisitMode mode;
RecurseVisitOrder order;
bool outputIdPaths;
bool recurseIntoHybridNodes;
bool hybridNodeDeepTraversal;
};

template <typename>
Expand Down Expand Up @@ -160,12 +160,11 @@ struct RecurseVisitor<apache::thrift::type_class::set<ValueTypeClass>> {
typename folly::remove_cvref_t<NodePtr>::element_type::CowType,
HybridNodeType>)
{
if (!options.recurseIntoHybridNodes) {
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
} else {
if (options.hybridNodeDeepTraversal) {
throw std::runtime_error(folly::to<std::string>(
"RecurseVisitor support for recurseIntoHybridNode in Set not implemented"));
"RecurseVisitor support for hybridNodeDeepTraversal in Set not implemented"));
}
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
}

template <typename Fields, typename TraverseHelper, typename Func>
Expand Down Expand Up @@ -219,11 +218,27 @@ struct RecurseVisitor<apache::thrift::type_class::list<ValueTypeClass>> {
typename folly::remove_cvref_t<NodePtr>::element_type::CowType,
HybridNodeType>)
{
if (!options.recurseIntoHybridNodes) {
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
} else {
if (options.hybridNodeDeepTraversal) {
throw std::runtime_error(folly::to<std::string>(
"RecurseVisitor support for recurseIntoHybridNode in List not implemented"));
"RecurseVisitor support for hybridNodeDeepTraversal in List not implemented"));
}
auto& tObj = node->ref();
bool visitIntermediate = options.mode == RecurseVisitMode::FULL ||
options.mode == RecurseVisitMode::UNPUBLISHED || tObj.empty();
if (visitIntermediate &&
options.order == RecurseVisitOrder::PARENTS_FIRST) {
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
}
// visit list elements
for (int i = 0; i < tObj.size(); ++i) {
traverser.push(folly::to<std::string>(i), TCType<ValueTypeClass>);
rv_detail::invokeVisitorFnHelper(
traverser, &tObj.at(i), std::forward<Func>(f));
traverser.pop(TCType<ValueTypeClass>);
}
if (visitIntermediate &&
options.order == RecurseVisitOrder::CHILDREN_FIRST) {
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
}
}

Expand Down Expand Up @@ -278,11 +293,36 @@ struct RecurseVisitor<
typename folly::remove_cvref_t<NodePtr>::element_type::CowType,
HybridNodeType>)
{
if (!options.recurseIntoHybridNodes) {
if (options.hybridNodeDeepTraversal) {
throw std::runtime_error(folly::to<std::string>(
"RecurseVisitor support for hybridNodeDeepTraversal in Map not implemented"));
}
auto& tObj = node->ref();
bool visitIntermediate = options.mode == RecurseVisitMode::FULL ||
options.mode == RecurseVisitMode::UNPUBLISHED || tObj.empty();
if (visitIntermediate &&
options.order == RecurseVisitOrder::PARENTS_FIRST) {
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
}
// visit map entries
if constexpr (std::is_const_v<NodePtr>) {
for (const auto& [key, val] : tObj) {
traverser.push(folly::to<std::string>(key), TCType<MappedTypeClass>);
rv_detail::invokeVisitorFnHelper(
traverser, &val, std::forward<Func>(f));
traverser.pop(TCType<MappedTypeClass>);
}
} else {
throw std::runtime_error(folly::to<std::string>(
"RecurseVisitor support for recurseIntoHybridNode in Map not implemented"));
for (auto& [key, val] : tObj) {
traverser.push(folly::to<std::string>(key), TCType<MappedTypeClass>);
rv_detail::invokeVisitorFnHelper(
traverser, &val, std::forward<Func>(f));
traverser.pop(TCType<MappedTypeClass>);
}
}
if (visitIntermediate &&
options.order == RecurseVisitOrder::CHILDREN_FIRST) {
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
}
}

Expand Down Expand Up @@ -345,12 +385,11 @@ struct RecurseVisitor<apache::thrift::type_class::variant> {
typename folly::remove_cvref_t<NodePtr>::element_type::CowType,
HybridNodeType>)
{
if (!options.recurseIntoHybridNodes) {
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
} else {
if (options.hybridNodeDeepTraversal) {
throw std::runtime_error(folly::to<std::string>(
"RecurseVisitor support for recurseIntoHybridNode in Variant not implemented"));
"RecurseVisitor support for hybridNodeDeepTraversal in Variant not implemented"));
}
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
}

template <typename Fields, typename TraverseHelper, typename Func>
Expand Down Expand Up @@ -437,12 +476,11 @@ struct RecurseVisitor<apache::thrift::type_class::structure> {
typename folly::remove_cvref_t<NodePtr>::element_type::CowType,
HybridNodeType>)
{
if (!options.recurseIntoHybridNodes) {
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
} else {
if (options.hybridNodeDeepTraversal) {
throw std::runtime_error(folly::to<std::string>(
"RecurseVisitor support for recurseIntoHybridNode in Struct not implemented"));
"RecurseVisitor support for hybridNodeDeepTraversal in Struct not implemented"));
}
rv_detail::invokeVisitorFnHelper(traverser, node, std::forward<Func>(f));
}

template <typename Fields, typename TraverseHelper, typename Func>
Expand Down
35 changes: 28 additions & 7 deletions fboss/thrift_cow/visitors/tests/RecurseVisitorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ TYPED_TEST(RecurseVisitorTests, TestFullRecurse) {
std::map<std::vector<std::string>, folly::dynamic> visited;
auto processPath = [&visited](
const std::vector<std::string>& path, auto&& node) {
visited.emplace(std::make_pair(path, node->toFollyDynamic()));
folly::dynamic dyn;
if constexpr (is_cow_type_v<decltype(*node)>) {
dyn = node->toFollyDynamic();
} else {
facebook::thrift::to_dynamic(
dyn, *node, facebook::thrift::dynamic_format::JSON_1);
}
visited.emplace(path, dyn);
};

RootRecurseVisitor::visit(
Expand Down Expand Up @@ -128,7 +135,10 @@ TYPED_TEST(RecurseVisitorTests, TestFullRecurse) {
{{"mapA"}, dynamic::object()},
{{"mapB"}, dynamic::object()}};

std::map<std::vector<std::string>, folly::dynamic> hybridLeaves = {
std::map<std::vector<std::string>, folly::dynamic> hybridNodes = {
{{"mapOfEnumToStruct", "3"}, testDyn["mapOfEnumToStruct"][3]}};

std::map<std::vector<std::string>, folly::dynamic> hybridDeepLeaves = {
{{"hybridStruct", "childMap"}, testDyn["hybridStruct"]["childMap"]},
{{"mapOfEnumToStruct"}, testDyn["mapOfEnumToStruct"]},
{{"mapOfEnumToStruct", "3"}, testDyn["mapOfEnumToStruct"][3]},
Expand All @@ -139,8 +149,12 @@ TYPED_TEST(RecurseVisitorTests, TestFullRecurse) {
{{"mapOfEnumToStruct", "3", "invert"},
testDyn["mapOfEnumToStruct"][3]["invert"]}};

if (!this->isHybridStorage()) {
for (const auto& entry : hybridLeaves) {
if (this->isHybridStorage()) {
for (const auto& entry : hybridNodes) {
expected.insert(entry);
}
} else {
for (const auto& entry : hybridDeepLeaves) {
expected.insert(entry);
}
}
Expand All @@ -160,7 +174,14 @@ TYPED_TEST(RecurseVisitorTests, TestLeafRecurse) {
std::map<std::vector<std::string>, folly::dynamic> visited;
auto processPath = [&visited](
const std::vector<std::string>& path, auto&& node) {
visited.emplace(std::make_pair(path, node->toFollyDynamic()));
folly::dynamic dyn;
if constexpr (is_cow_type_v<decltype(*node)>) {
dyn = node->toFollyDynamic();
} else {
facebook::thrift::to_dynamic(
dyn, *node, facebook::thrift::dynamic_format::JSON_1);
}
visited.emplace(path, dyn);
};

RootRecurseVisitor::visit(nodeA, RecurseVisitMode::LEAVES, processPath);
Expand Down Expand Up @@ -191,7 +212,7 @@ TYPED_TEST(RecurseVisitorTests, TestLeafRecurse) {
{{"mapOfI32ToI32"}, testDyn["mapOfI32ToI32"]},
// {{"mapOfI32ToListOfStructs"}, testDyn["mapOfI32ToListOfStructs"]},
// {{"mapOfI32ToSetOfString"}, testDyn["mapOfI32ToSetOfString"]},
{{"mapOfEnumToStruct"}, testDyn["mapOfEnumToStruct"]},
{{"mapOfEnumToStruct", "3"}, testDyn["mapOfEnumToStruct"][3]},
{{"hybridMap"}, testDyn["hybridMap"]},
{{"hybridMapOfI32ToStruct"}, testDyn["hybridMapOfI32ToStruct"]},
{{"hybridMapOfMap"}, testDyn["hybridMapOfMap"]},
Expand Down Expand Up @@ -244,7 +265,7 @@ TYPED_TEST(RecurseVisitorTests, TestLeafRecurse) {
{{"12"}, testDyn["mapOfEnumToI32"]},
{{"13"}, testDyn["mapOfStringToI32"]},
{{"14"}, testDyn["mapOfI32ToStruct"]},
{{"15"}, testDyn["mapOfEnumToStruct"]},
{{"15", "3"}, testDyn["mapOfEnumToStruct"][3]},
// {{"17"}, testDyn["mapOfI32ToListOfStructs"]},
{{"27"}, testDyn["hybridMap"]},
{{"28"}, testDyn["hybridList"]},
Expand Down

0 comments on commit 3f8b2b5

Please sign in to comment.