Skip to content

Commit fa4a972

Browse files
Improve processYamlIncludeDirective peformance
1 parent ff450d3 commit fa4a972

File tree

6 files changed

+78
-46
lines changed

6 files changed

+78
-46
lines changed

tesseract_collision/core/src/contact_managers_plugin_factory.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ ContactManagersPluginFactory::ContactManagersPluginFactory(YAML::Node config,
7878
const tesseract_common::ResourceLocator& locator)
7979
: ContactManagersPluginFactory()
8080
{
81-
config = tesseract_common::processYamlIncludeDirective(config, locator);
81+
tesseract_common::processYamlIncludeDirective(config, locator);
8282
loadConfig(config);
8383
}
8484

tesseract_collision/test/contact_managers_factory_unit.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ void runContactManagersFactoryTest(const std::filesystem::path& config_path)
4141
{
4242
tesseract_common::GeneralResourceLocator locator;
4343
ContactManagersPluginFactory factory(config_path, locator);
44-
YAML::Node plugin_config =
45-
tesseract_common::processYamlIncludeDirective(YAML::LoadFile(config_path.string()), locator);
44+
YAML::Node plugin_config = YAML::LoadFile(config_path.string());
45+
tesseract_common::processYamlIncludeDirective(plugin_config, locator);
4646

4747
const YAML::Node& plugin_info = plugin_config["contact_manager_plugins"];
4848
const YAML::Node& search_paths = plugin_info["search_paths"];

tesseract_common/include/tesseract_common/yaml_utils.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,30 @@ namespace tesseract_common
4141
class ResourceLocator;
4242

4343
/**
44-
* @brief Recursively processes a YAML node to resolve `!include` directives.
44+
* @brief Recursively expands `!include` directives in a YAML node, in-place.
4545
*
46-
* This function replaces any node tagged with `!include` with the content of the
47-
* specified file. It also recursively processes maps and sequences to handle nested
48-
* `!include` directives.
46+
* This function walks the given node and:
47+
* - When it encounters a node tagged `!include`, it loads the referenced file
48+
* (via the provided ResourceLocator), replaces the tagged node with the parsed
49+
* content of that file, clears the `!include` tag, and then continues processing
50+
* the newly loaded subtree.
51+
* - When it encounters a mapping, it recurses into each value in-place.
52+
* - When it encounters a sequence, it recurses into each element in-place.
4953
*
50-
* @param node The YAML node to process.
51-
* @param locator The locator used to resolve urls and relative file paths.
52-
* @return A YAML::Node with all `!include` directives resolved.
54+
* After this call returns, `node` and its entire subtree will have had all
55+
* `!include` directives resolved and removed. Subsequent calls on the same
56+
* expanded tree are no-ops.
5357
*
54-
* @throws std::runtime_error if an `!include` tag is used improperly (e.g., not scalar),
55-
* or if a file specified in an `!include` directive cannot be loaded.
58+
* @param node The YAML node to process. Must be non-const so it can
59+
* be mutated in-place as includes are expanded.
60+
* @param locator The locator used to resolve file paths or URLs for
61+
* `!include` directives. May maintain internal state
62+
* (e.g. cache) between calls.
63+
*
64+
* @throws std::runtime_error if an `!include` tag is not a scalar string,
65+
* or if the specified file cannot be located or loaded.
5666
*/
57-
YAML::Node processYamlIncludeDirective(const YAML::Node& node, const ResourceLocator& locator);
67+
void processYamlIncludeDirective(YAML::Node& node, const ResourceLocator& locator);
5868

5969
/**
6070
* @brief Loads a YAML file and processes `!include` directives recursively.

tesseract_common/src/yaml_utils.cpp

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,9 @@ TESSERACT_COMMON_IGNORE_WARNINGS_POP
3535

3636
namespace tesseract_common
3737
{
38-
/**
39-
* @brief Recursively processes a YAML node to resolve `!include` directives.
40-
*
41-
* This function replaces any node tagged with `!include` with the content of the
42-
* specified file. It also recursively processes maps and sequences to handle nested
43-
* `!include` directives.
44-
*
45-
* @param node The YAML node to process.
46-
* @param locator The locator used to resolve urls and relative file paths.
47-
* @return A YAML::Node with all `!include` directives resolved.
48-
*
49-
* @throws std::runtime_error if an `!include` tag is used improperly (e.g., not scalar),
50-
* or if a file specified in an `!include` directive cannot be loaded.
51-
*/
52-
YAML::Node processYamlIncludeDirective(const YAML::Node& node, const ResourceLocator& locator)
38+
void processYamlIncludeDirective(YAML::Node& node, const ResourceLocator& locator)
5339
{
40+
// Case 1: this node *is* an include → replace it with the loaded file
5441
if (node.Tag() == "!include")
5542
{
5643
// Ensure the node is scalar and contains a file path
@@ -63,44 +50,76 @@ YAML::Node processYamlIncludeDirective(const YAML::Node& node, const ResourceLoc
6350
if (resource == nullptr)
6451
throw std::runtime_error("Unable to locate resource: " + included_file);
6552

66-
return processYamlIncludeDirective(YAML::LoadFile(resource->getFilePath()), *resource);
53+
// Parse once
54+
YAML::Node loaded = YAML::LoadFile(resource->getFilePath());
55+
56+
// Take over this node
57+
node = loaded;
58+
59+
// Clear the old tag so we don't re‐process it
60+
node.SetTag("");
61+
62+
// Recurse into what we just loaded
63+
processYamlIncludeDirective(node, *resource);
64+
return;
6765
}
6866

67+
// Case 2: map → mutate each value in‐place
6968
if (node.IsMap())
7069
{
71-
// Create a new map and process each key-value pair
72-
YAML::Node processed_map = YAML::Node(YAML::NodeType::Map);
7370
for (auto it = node.begin(); it != node.end(); ++it)
74-
processed_map[it->first] = processYamlIncludeDirective(it->second, locator);
71+
{
72+
// 1) pull out the key
73+
const std::string key = it->first.Scalar();
7574

76-
return processed_map;
75+
// 2) copy the child handle (this is cheap)
76+
YAML::Node child = it->second;
77+
78+
// 3) recurse & mutate the child
79+
processYamlIncludeDirective(child, locator);
80+
81+
// 4) write it back into the map
82+
node[key] = child;
83+
}
84+
85+
return;
7786
}
7887

88+
// Case 3: sequence → mutate each element in‐place
7989
if (node.IsSequence())
8090
{
81-
// Create a new sequence and process each element
82-
YAML::Node processed_sequence = YAML::Node(YAML::NodeType::Sequence);
83-
for (const auto& child : node)
84-
processed_sequence.push_back(processYamlIncludeDirective(child, locator));
91+
// NOLINTNEXTLINE(modernize-loop-convert)
92+
for (std::size_t i = 0; i < node.size(); ++i)
93+
{
94+
// 1) pull out the element (this is cheap—just a handle copy)
95+
YAML::Node child = node[i];
96+
97+
// 2) recurse & mutate that subtree
98+
processYamlIncludeDirective(child, locator);
99+
100+
// 3) write it back into the sequence
101+
node[i] = child;
102+
}
85103

86-
return processed_sequence;
104+
return;
87105
}
88106

89-
// Return the node as-is for scalars and unsupported types
90-
return node;
107+
// Case 4: scalar or anything else → nothing to do
91108
}
92109

93110
YAML::Node loadYamlFile(const std::string& file_path, const ResourceLocator& locator)
94111
{
95112
auto resource = locator.locateResource(file_path);
96113
YAML::Node root = YAML::LoadFile(resource->getFilePath());
97-
return processYamlIncludeDirective(root, *resource);
114+
processYamlIncludeDirective(root, *resource);
115+
return root;
98116
}
99117

100118
YAML::Node loadYamlString(const std::string& yaml_string, const ResourceLocator& locator)
101119
{
102120
YAML::Node root = YAML::Load(yaml_string);
103-
return processYamlIncludeDirective(root, locator);
121+
processYamlIncludeDirective(root, locator);
122+
return root;
104123
}
105124

106125
void writeYamlToFile(const YAML::Node& node, const std::string& file_path)

tesseract_kinematics/core/src/kinematics_plugin_factory.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void KinematicsPluginFactory::loadConfig(const YAML::Node& config)
7676
KinematicsPluginFactory::KinematicsPluginFactory(YAML::Node config, const tesseract_common::ResourceLocator& locator)
7777
: KinematicsPluginFactory()
7878
{
79-
config = tesseract_common::processYamlIncludeDirective(config, locator);
79+
tesseract_common::processYamlIncludeDirective(config, locator);
8080
loadConfig(config);
8181
}
8282

tesseract_srdf/src/configs.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ tesseract_common::CalibrationInfo parseCalibrationConfig(const tesseract_scene_g
7171
YAML::Node config;
7272
try
7373
{
74-
config = tesseract_common::processYamlIncludeDirective(YAML::LoadFile(cal_config_file_path.string()), locator);
74+
config = YAML::LoadFile(cal_config_file_path.string());
75+
tesseract_common::processYamlIncludeDirective(config, locator);
7576
}
7677
// LCOV_EXCL_START
7778
catch (...)
@@ -103,7 +104,8 @@ tesseract_common::KinematicsPluginInfo parseKinematicsPluginConfig(const tessera
103104
YAML::Node config;
104105
try
105106
{
106-
config = tesseract_common::processYamlIncludeDirective(YAML::LoadFile(kin_plugin_file_path.string()), locator);
107+
config = YAML::LoadFile(kin_plugin_file_path.string());
108+
tesseract_common::processYamlIncludeDirective(config, locator);
107109
}
108110
// LCOV_EXCL_START
109111
catch (...)
@@ -128,7 +130,8 @@ parseContactManagersPluginConfig(const tesseract_common::ResourceLocator& locato
128130
YAML::Node config;
129131
try
130132
{
131-
config = tesseract_common::processYamlIncludeDirective(YAML::LoadFile(cm_plugin_file_path.string()), locator);
133+
config = YAML::LoadFile(cm_plugin_file_path.string());
134+
tesseract_common::processYamlIncludeDirective(config, locator);
132135
}
133136
// LCOV_EXCL_START
134137
catch (...)

0 commit comments

Comments
 (0)