Skip to content

Commit

Permalink
Add two stage IR xml parse to handle corner cases
Browse files Browse the repository at this point in the history
  • Loading branch information
t-jankowski committed Sep 19, 2024
1 parent b368c31 commit 0cb901c
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 14 deletions.
41 changes: 27 additions & 14 deletions src/frontends/ir/src/frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,54 @@ namespace frontend {
namespace ir {
namespace {

inline size_t get_ir_version(pugi::xml_node& root) {
return static_cast<size_t>(ov::util::pugixml::get_uint64_attr(root, "version", 0));
size_t get_ir_version(const pugi::xml_document& doc) {
pugi::xml_node root = doc.document_element();
std::string root_name = root.name();
std::transform(root_name.begin(), root_name.end(), root_name.begin(), ::tolower);

if (root_name == "net")
return static_cast<size_t>(ov::util::pugixml::get_uint64_attr(root, "version", 0));

return 0;
}

/**
* @brief Extracts IR version from model stream
* @param model Models stream
* @param model Model's stream
* @return IR version, 0 if model does represent IR
*/
size_t get_ir_version(std::istream& model) {
// IR version is a value of root tag attribuite thought not need to parse the whole stream.
std::array<char, 512> header{};

model.seekg(0, model.beg);
model.read(header.data(), header.size());
model.clear();
model.seekg(0, model.beg);

pugi::xml_document doc;
auto res =

// For dominant number of IRs `load_buffer' in this case returns parsing-error as 512 is not enough for the whole
// root tag. Basing on Pugi manual "If parsing failed because the source data was not a valid XML, the resulting
// tree is not destroyed - despite the fact that load function returns error, you can use the part of the tree that
// was successfully parsed." root tag is processed as typically it's enough to read model version. However if IR is
// small enough to fit 512 bytes ok-status is returned. Thus ignoring returned value.
std::ignore =
doc.load_buffer(header.data(), header.size(), pugi::parse_default | pugi::parse_fragment, pugi::encoding_utf8);

if (res == pugi::status_ok) {
pugi::xml_node root = doc.document_element();
auto ir_version = get_ir_version(doc);

std::string node_name = root.name();
std::transform(node_name.begin(), node_name.end(), node_name.begin(), ::tolower);
// In case attribute name is very long and placed before version attribute the latter is not accesible within first
// 512 bytes, so read the whole stream and try to obtain version value.
if (ir_version == 0) {
if (doc.load(model))
ir_version = get_ir_version(doc);

if (node_name == "net") {
return get_ir_version(root);
}
model.clear();
model.seekg(0, model.beg);
}

return 0;
return ir_version;
}

} // namespace

bool FrontEnd::supported_impl(const std::vector<ov::Any>& variants) const {
Expand Down
89 changes: 89 additions & 0 deletions src/frontends/ir/tests/frontend_test_basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1523,3 +1523,92 @@ TEST_F(IRFrontendTests, load_model_weights_not_exist_at_path) {

std::remove(model_file_path.c_str());
}

TEST_F(IRFrontendTests, VeryLongModelName) {
std::string testModel = R"V0G0N(
<?xml version="1.0"?>
<net name="VeryLongModelName....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................." version="11">
<layers>
<layer id="0" name="Parameter_1" type="Parameter" version="opset1">
<data shape="2" element_type="f16" />
<output>
<port id="0" precision="FP16">
<dim>2</dim>
</port>
</output>
</layer>
<layer id="1" name="Convert_2" type="Convert" version="opset1">
<data destination_type="f32" />
<input>
<port id="0" precision="FP16">
<dim>2</dim>
</port>
</input>
<output>
<port id="1" precision="FP32">
<dim>2</dim>
</port>
</output>
</layer>
<layer id="2" name="Result_3" type="Result" version="opset1">
<input>
<port id="0" precision="FP32">
<dim>2</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="0" />
<edge from-layer="1" from-port="1" to-layer="2" to-port="0" />
</edges>
<rt_info />
</net>
)V0G0N";

std::shared_ptr<ov::Model> model;
ov::RTMap rtInfo;
int64_t version = 0;

OV_ASSERT_NO_THROW(model = getWithIRFrontend(testModel));
ASSERT_TRUE(!!model);
OV_ASSERT_NO_THROW(version = model->get_rt_info().at("version").as<int64_t>());
ASSERT_EQ(11, version);
}

TEST_F(IRFrontendTests, VeryShortValidModel) {
std::string testModel = R"V0G0N(
<?xml version="1.0"?>
<net name="A" version="11">
<layers>
<layer id="0" name="P" type="Parameter" version="opset1">
<data shape="2" element_type="f16" />
<output>
<port id="0" precision="FP16">
<dim>2</dim>
</port>
</output>
</layer>
<layer id="1" name="R" type="Result" version="opset1">
<input>
<port id="0" precision="FP32">
<dim>2</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="0" />
</edges>
<rt_info />
</net>
)V0G0N";

std::shared_ptr<ov::Model> model;
int64_t version = 0;

OV_ASSERT_NO_THROW(model = getWithIRFrontend(testModel));
ASSERT_TRUE(!!model);
OV_ASSERT_NO_THROW(version = model->get_rt_info().at("version").as<int64_t>());
ASSERT_EQ(11, version);
}

0 comments on commit 0cb901c

Please sign in to comment.