From c46554551191a7a67439cd156cdfe563f1de3d6c Mon Sep 17 00:00:00 2001 From: wgodoy <wgodoy@wgodoy-desktop> Date: Mon, 19 Jun 2017 00:23:28 -0400 Subject: [PATCH] Testing XML implementation with MPI Initial implementation of XML running with helloBPWriter example Corrected bug in ADIOS.cpp when detecting IO in ConfigFile --- .../runtimeconfig/grandSchema.xml | 6 +- examples/hello/bpWriter/CMakeLists.txt | 6 + examples/hello/bpWriter/helloBPWriter.xml | 17 +- examples/hello/bpWriter/helloBPWriterXML.cpp | 82 ++++ source/adios2/core/ADIOS.cpp | 14 +- source/adios2/helper/adiosString.cpp | 4 +- source/adios2/helper/adiosSystem.cpp | 21 +- source/adios2/helper/adiosSystem.h | 3 +- source/adios2/helper/adiosXML.cpp | 372 +++++++++--------- source/adios2/helper/adiosXML.h | 59 +-- 10 files changed, 336 insertions(+), 248 deletions(-) create mode 100644 examples/hello/bpWriter/helloBPWriterXML.cpp diff --git a/examples/experimental/runtimeconfig/grandSchema.xml b/examples/experimental/runtimeconfig/grandSchema.xml index e069a7764..71d80bd30 100644 --- a/examples/experimental/runtimeconfig/grandSchema.xml +++ b/examples/experimental/runtimeconfig/grandSchema.xml @@ -4,9 +4,9 @@ <io name="Output"> <var name="myArray"> <!-- provide a sequence of transforms --> - <transform type="BZip2"> <!-- not implemented yet --> + <transform type='BZip2'> <!-- not implemented yet --> BlockSize100K=3; <!-- must be a number between 0 and 9--> - </transform> + </transform> <transform type="Zfp"> <!-- not implemented yet, mutually exclusive options --> Tolerance=1e-3; <!-- must be a number between 0 and 9--> <!-- Rate=1; --> <!-- must be a number between 0 and 9--> @@ -58,7 +58,7 @@ CacheSize=8192; <!-- not to be confused with ADIOS heap buffer, this is transport library cache, always in bytes --> </transport> - <transport type="RDMA"> <!-- Staging gets more complex depending on library requirements--> + <transport type="RDMA"> <!-- Staging gets more complex depending on library requirements--> ProfileUnits=Microseconds; <!-- Microseconds, Milliseconds, Seconds, Minutes, Hours --> Library=ibverbs; <!-- SST (default), Flexpath, Dataspaces, ibverbs --> Port=18515; <!-- ibverbs --> diff --git a/examples/hello/bpWriter/CMakeLists.txt b/examples/hello/bpWriter/CMakeLists.txt index 56c647c17..17fc96c85 100644 --- a/examples/hello/bpWriter/CMakeLists.txt +++ b/examples/hello/bpWriter/CMakeLists.txt @@ -9,6 +9,12 @@ if(ADIOS_HAVE_MPI) add_executable(hello_bpWriter helloBPWriter.cpp) target_include_directories(hello_bpWriter PRIVATE ${MPI_C_INCLUDE_PATH}) target_link_libraries(hello_bpWriter ${MPI_C_LIBRARIES}) + + add_executable(hello_bpWriterXML helloBPWriterXML.cpp) + target_include_directories(hello_bpWriterXML PRIVATE ${MPI_C_INCLUDE_PATH}) + target_link_libraries(hello_bpWriterXML ${MPI_C_LIBRARIES}) + target_link_libraries(hello_bpWriterXML adios2) + else() add_executable(hello_bpWriter helloBPWriter_nompi.cpp) endif() diff --git a/examples/hello/bpWriter/helloBPWriter.xml b/examples/hello/bpWriter/helloBPWriter.xml index 00afc9126..bda56e4c1 100644 --- a/examples/hello/bpWriter/helloBPWriter.xml +++ b/examples/hello/bpWriter/helloBPWriter.xml @@ -4,12 +4,17 @@ <io name="BPFile_N2N"> <engine type="BPFileWriter"> Threads=1; <!-- for vectorized memory operations and asynchronous tasks --> - ProfileUnits=Microseconds; <!-- Microseconds (default), Milliseconds, Seconds, Minutes, Hours --> - MaxBufferSize=20Mb; <!-- XXKb, XXMb, or XXXGb supported, 16Mb (default should depend on system) --> - InitialBufferSize=1Mb; <!-- XXKb, XXMb, or XXXGb supported, 16Kb (default shoudl depende on system) --> - BufferGrowthFactor=2; <!-- exponential growth factor > 1, 1.5 (default), for this case: 1, 2, 4, 8, 16, 20 Mb--> + ProfileUnits=Microseconds; <!-- Microseconds (default), Milliseconds, Seconds, Minutes, Hours --> + MaxBufferSize=20Mb; <!-- XXKb, XXMb, or XXXGb supported, 16Mb (default should depend on system) --> + InitialBufferSize=1Mb; <!-- XXKb, XXMb, or XXXGb supported, 16Kb (default should depend on system) --> + BufferGrowthFactor=2; <!-- exponential growth factor > 1, 1.5 (default, e.g. STL default=2), for this case: 1, 2, 4, 8, 16, 20 Mb--> </engine> - </io> -</adios-config> + <transport type="File"> + Library=POSIX; + ProfileUnits=Milliseconds; + </transport> + + </io> +</adios-config> \ No newline at end of file diff --git a/examples/hello/bpWriter/helloBPWriterXML.cpp b/examples/hello/bpWriter/helloBPWriterXML.cpp new file mode 100644 index 000000000..807b0c9c3 --- /dev/null +++ b/examples/hello/bpWriter/helloBPWriterXML.cpp @@ -0,0 +1,82 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * helloBPWriter.cpp: Simple self-descriptive example of how to write a variable + * to a BP File that lives in several MPI processes. + * + * Created on: Feb 16, 2017 + * Author: William F Godoy godoywf@ornl.gov + */ + +#include <ios> //std::ios_base::failure +#include <iostream> //std::cout +#include <mpi.h> +#include <stdexcept> //std::invalid_argument std::exception +#include <vector> + +#include <adios2.h> + +int main(int argc, char *argv[]) +{ + MPI_Init(&argc, &argv); + int rank, size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + /** Application variable */ + std::vector<float> myFloats = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + const std::size_t Nx = myFloats.size(); + + try + { + /** ADIOS class factory of IO class objects, DebugON is recommended */ + adios::ADIOS adios("helloBPWriter.xml", MPI_COMM_WORLD, adios::DebugON); + + /*** IO class object: settings and factory of Settings: Variables, + * Parameters, Transports, and Execution: Engines */ + adios::IO &bpIO = adios.DeclareIO("BPFile_N2N"); + + /** global array : name, { shape (total) }, { start (local) }, { count + * (local) }, all are constant dimensions */ + adios::Variable<float> &bpFloats = bpIO.DefineVariable<float>( + "bpFloats", {size * Nx}, {rank * Nx}, {Nx}, adios::ConstantDims); + + /** Engine derived class, spawned to start IO operations */ + auto bpWriter = bpIO.Open("myVector.bp", adios::OpenMode::Write); + + if (!bpWriter) + { + throw std::ios_base::failure( + "ERROR: bpWriter not created at Open\n"); + } + + /** Write variable for buffering */ + bpWriter->Write<float>(bpFloats, myFloats.data()); + + /** Create bp file, engine becomes unreachable after this*/ + bpWriter->Close(); + } + catch (std::invalid_argument &e) + { + std::cout << "Invalid argument exception, STOPPING PROGRAM from rank " + << rank << "\n"; + std::cout << e.what() << "\n"; + } + catch (std::ios_base::failure &e) + { + std::cout + << "IO System base failure exception, STOPPING PROGRAM from rank " + << rank << "\n"; + std::cout << e.what() << "\n"; + } + catch (std::exception &e) + { + std::cout << "Exception, STOPPING PROGRAM from rank " << rank << "\n"; + std::cout << e.what() << "\n"; + } + + MPI_Finalize(); + + return 0; +} diff --git a/source/adios2/core/ADIOS.cpp b/source/adios2/core/ADIOS.cpp index ee373fb86..194dbdddb 100644 --- a/source/adios2/core/ADIOS.cpp +++ b/source/adios2/core/ADIOS.cpp @@ -19,6 +19,7 @@ /// \endcond #include "adios2/ADIOSMPI.h" +#include "adios2/helper/adiosFunctions.h" namespace adios { @@ -31,9 +32,14 @@ ADIOS::ADIOS(const std::string configFile, MPI_Comm mpiComm, { CheckMPI(); } - // XML to be implemented later - // InitXML( m_XMLConfigFile, m_MPIComm, m_DebugMode, m_HostLanguage, - // m_Transforms, m_Groups ); + if (!configFile.empty()) + { + if (configFile.substr(configFile.size() - 3) == "xml") + { + InitXML(configFile, m_MPIComm, m_DebugMode, m_Transforms, m_IOs); + } + // TODO expand for other formats + } } ADIOS::ADIOS(const std::string configFile, const bool debugMode) @@ -56,7 +62,7 @@ IO &ADIOS::DeclareIO(const std::string ioName) { if (m_DebugMode) { - if (itIO->second.InConfigFile()) + if (!itIO->second.InConfigFile()) { throw std::invalid_argument( "ERROR: IO class object with name " + ioName + diff --git a/source/adios2/helper/adiosString.cpp b/source/adios2/helper/adiosString.cpp index 90b6399b1..bc7841276 100644 --- a/source/adios2/helper/adiosString.cpp +++ b/source/adios2/helper/adiosString.cpp @@ -24,9 +24,9 @@ std::string FileToString(const std::string &fileName) { std::ifstream fileStream(fileName); - if (!fileStream.good()) + if (!fileStream) { - throw std::ios_base::failure( + throw std::invalid_argument( "ERROR: file " + fileName + " could not be opened. Check permissions or existence\n"); } diff --git a/source/adios2/helper/adiosSystem.cpp b/source/adios2/helper/adiosSystem.cpp index 0dd5896d8..bc8acdcfb 100644 --- a/source/adios2/helper/adiosSystem.cpp +++ b/source/adios2/helper/adiosSystem.cpp @@ -79,10 +79,9 @@ std::string LocalTimeDate() noexcept return std::string(std::ctime(&now)); } -std::string BroadcastFileContents(const std::string &fileName, - MPI_Comm mpiComm) noexcept +std::string BroadcastString(const std::string &input, MPI_Comm mpiComm) { - std::string fileContents; + std::string receivedInput; size_t characterCount = 0; int rank; @@ -90,15 +89,16 @@ std::string BroadcastFileContents(const std::string &fileName, if (rank == 0) // sender { - fileContents = FileToString(fileName); - characterCount = fileContents.size(); + characterCount = input.size(); // broadcast size for allocation MPI_Bcast(&characterCount, 1, ADIOS2_MPI_SIZE_T, 0, mpiComm); // broadcast contents - MPI_Bcast(const_cast<char *>(fileContents.c_str()), + MPI_Bcast(const_cast<char *>(input.c_str()), static_cast<int>(characterCount), MPI_CHAR, 0, mpiComm); + + return input; } else // receivers { @@ -106,15 +106,14 @@ std::string BroadcastFileContents(const std::string &fileName, MPI_Bcast(&characterCount, 1, ADIOS2_MPI_SIZE_T, 0, mpiComm); // allocate receiver - std::vector<char> fileContentsReceiver(characterCount); - MPI_Bcast(fileContentsReceiver.data(), static_cast<int>(characterCount), + std::vector<char> stringReceiver(characterCount); + MPI_Bcast(stringReceiver.data(), static_cast<int>(characterCount), MPI_CHAR, 0, mpiComm); - fileContents.assign(fileContentsReceiver.begin(), - fileContentsReceiver.end()); + receivedInput.assign(stringReceiver.begin(), stringReceiver.end()); } - return fileContents; + return receivedInput; } } // end namespace adios diff --git a/source/adios2/helper/adiosSystem.h b/source/adios2/helper/adiosSystem.h index acbae391d..389e095dd 100644 --- a/source/adios2/helper/adiosSystem.h +++ b/source/adios2/helper/adiosSystem.h @@ -50,8 +50,7 @@ std::string LocalTimeDate() noexcept; * @param mpiComm * @return fileContents as a single string */ -std::string BroadcastFileContents(const std::string &fileName, - MPI_Comm mpiComm) noexcept; +std::string BroadcastString(const std::string &input, MPI_Comm mpiComm); } // end namespace adios diff --git a/source/adios2/helper/adiosXML.cpp b/source/adios2/helper/adiosXML.cpp index 8107a4104..d52968e1e 100644 --- a/source/adios2/helper/adiosXML.cpp +++ b/source/adios2/helper/adiosXML.cpp @@ -16,145 +16,163 @@ /// \endcond #include "adios2/ADIOSTypes.h" -#include "adios2/helper/adiosSystem.h" +#include "adios2/helper/adiosString.h" namespace adios { -std::string GetSubString(const std::string initialTag, - const std::string finalTag, const std::string &content, - std::string::size_type ¤tPosition) +void RemoveCommentsXML(std::string ¤tContent) noexcept { - auto lf_Wipe = [](std::string &subString, - std::string::size_type ¤tPosition) { - subString.clear(); - currentPosition = std::string::npos; - }; + std::string::size_type startComment(currentContent.find("<!--")); + + while (startComment != currentContent.npos) + { + std::string::size_type endComment(currentContent.find("-->")); + currentContent.erase(startComment, endComment - startComment + 3); + startComment = currentContent.find("<!--"); + } +} +TagXML GetTagXML(const std::string tagName, const std::string &content, + std::string::size_type &position) +{ auto lf_SetPositions = - [](const char quote, const std::string::size_type quotePosition, - const std::string &content, std::string::size_type ¤tPosition, - std::string::size_type &closingQuotePosition) { - currentPosition = quotePosition; - closingQuotePosition = content.find(quote, currentPosition + 1); - }; - - // BODY OF FUNCTION STARTS HERE - std::string subString; - - std::string::size_type start(content.find(initialTag, currentPosition)); - if (start == content.npos) + [](const std::string input, const std::string &content, + std::string::size_type &position) -> std::string::size_type { + + const std::string::size_type inputPosition = + GetStringPositionXML(input, content, position); + + if (inputPosition != std::string::npos) + { + position = inputPosition + input.size(); + } + return inputPosition; + }; + + TagXML tagXML; + + std::string name(tagName); + if (name.back() == ' ') + { + name.pop_back(); + } + + auto openingStart = lf_SetPositions("<" + name, content, position); + auto openingEnd = lf_SetPositions(">", content, position); + + if (openingStart == std::string::npos || openingEnd == std::string::npos) + { + tagXML.IsFull = false; + return tagXML; + } + + tagXML.Header = content.substr(openingStart, openingEnd + 1 - openingStart); + + auto closingStart = + GetStringPositionXML("</" + name + ">", content, position); + + if (closingStart == std::string::npos) { - lf_Wipe(subString, currentPosition); - return subString; + throw std::invalid_argument( + "ERROR: could not find closing tag </" + name + + "> in XML config file, in call to ADIOS constructor\n"); } - currentPosition = start; + tagXML.IsFull = true; + tagXML.Elements = + content.substr(openingEnd + 1, closingStart - (openingEnd + 1)); + + // std::cout << "START..." << tagXML.Header << "..."; + // std::cout << tagXML.Elements << "...END\n"; + + return tagXML; +} - std::string::size_type end(content.find(finalTag, currentPosition)); - if (end == content.npos) +std::string::size_type +GetStringPositionXML(const std::string input, const std::string &content, + const std::string::size_type &startPosition) noexcept +{ + std::string::size_type foundPosition(content.find(input, startPosition)); + if (foundPosition == content.npos) { - lf_Wipe(subString, currentPosition); - return subString; + return foundPosition; } - // make sure the finalTag is not a value surrounded by " " or ' ', - // if so find next - bool isValue = true; + // check if it is not inside " " or ' ' + std::string::size_type currentPosition(startPosition); - while (isValue) + while (foundPosition != content.npos) { - std::string::size_type singleQuotePosition = - content.find('\'', currentPosition); - std::string::size_type doubleQuotePosition = - content.find('\"', currentPosition); + const std::string::size_type singleQuotePosition( + content.find('\'', currentPosition)); + const std::string::size_type doubleQuotePosition( + content.find('\"', currentPosition)); if ((singleQuotePosition == content.npos && doubleQuotePosition == content.npos) || (singleQuotePosition == content.npos && - end < doubleQuotePosition) || + foundPosition < doubleQuotePosition) || (doubleQuotePosition == content.npos && - end < singleQuotePosition) || - (end < singleQuotePosition && end < doubleQuotePosition)) + foundPosition < singleQuotePosition) || + (foundPosition < singleQuotePosition && + foundPosition < doubleQuotePosition)) { break; } - // find the closing corresponding quote std::string::size_type closingQuotePosition; - if (singleQuotePosition == content.npos) - { // no ' anywhere - lf_SetPositions('\"', doubleQuotePosition, content, currentPosition, - closingQuotePosition); + if (singleQuotePosition != content.npos && + doubleQuotePosition == content.npos) + { + currentPosition = singleQuotePosition; + closingQuotePosition = content.find('\'', currentPosition + 1); } - else if (doubleQuotePosition == content.npos) - { // no " anywhere - lf_SetPositions('\'', singleQuotePosition, content, currentPosition, - closingQuotePosition); + else if (singleQuotePosition == content.npos && + doubleQuotePosition != content.npos) + { + currentPosition = doubleQuotePosition; + closingQuotePosition = content.find('\"', currentPosition + 1); } else { if (singleQuotePosition < doubleQuotePosition) { - lf_SetPositions('\'', singleQuotePosition, content, - currentPosition, closingQuotePosition); + currentPosition = singleQuotePosition; + closingQuotePosition = content.find('\'', currentPosition + 1); } else - { // find the closing " - lf_SetPositions('\"', doubleQuotePosition, content, - currentPosition, closingQuotePosition); + { + currentPosition = doubleQuotePosition; + closingQuotePosition = content.find('\"', currentPosition + 1); } } - // if can't find closing it's open until the end if (closingQuotePosition == content.npos) { - lf_Wipe(subString, currentPosition); - return subString; + currentPosition == content.npos; + break; } currentPosition = closingQuotePosition + 1; - if (closingQuotePosition < end) + if (closingQuotePosition < foundPosition) { continue; } - - // if this point is reached it means it's a value inside " " or ' ', - // move to the next end - end = content.find(finalTag, currentPosition); - } - - currentPosition = end; - subString = content.substr(start, end - start + finalTag.size()); - return subString; -} - -std::string GetOpeningTag(const std::string tagName, - const std::string &tagContents, - std::string::size_type &position, - const bool debugMode) -{ - std::string openingTag( - GetSubString("<" + tagName + " ", ">", tagContents, position)); - - const std::string::size_type elementsStartPosition(position); - - if (debugMode) - { - if (openingTag.size() < 2) + else { - throw std::invalid_argument("ERROR: wrong XML tag <" + tagName + - ", in call to ADIOS constructor\n"); + // if this point is reached it means it's a value inside " " or ' ', + // iterate + foundPosition = content.find(input, currentPosition); + currentPosition = foundPosition; } } - // eliminate < > - openingTag = openingTag.substr(1, openingTag.size() - 2); - return openingTag; + return foundPosition; } -Params GetTagAttributes(const std::string &fileContent, const std::string &tag) +Params GetTagAttributesXML(const std::string &tagHeader) { auto lf_GetQuotedValue = [](const char quote, const std::string::size_type "ePosition, @@ -170,8 +188,9 @@ Params GetTagAttributes(const std::string &fileContent, const std::string &tag) "...check XML file, in call to ADIOS constructor\n"); } + const std::string value(currentTag.substr(0, nextQuotePosition)); currentTag = currentTag.substr(nextQuotePosition + 1); - return currentTag.substr(0, nextQuotePosition); + return value; }; auto lf_GetAttributes = [&](const std::string &tag) -> Params { @@ -199,6 +218,10 @@ Params GetTagAttributes(const std::string &fileContent, const std::string &tag) { value = lf_GetQuotedValue(quote, equalPosition + 1, currentTag); } + else + { + // throw exception here? + } attributes.emplace(key, value); } @@ -207,105 +230,80 @@ Params GetTagAttributes(const std::string &fileContent, const std::string &tag) // BODY of function starts here Params attributes; + // eliminate < > + std::string openingTag = tagHeader.substr(1, tagHeader.size() - 2); - if (tag.back() == '/') // last char is / --> "XML empty tag" + if (tagHeader.back() == '/') // last char is / --> "XML empty tag" { - attributes = lf_GetAttributes(tag); + // attributes = lf_GetAttributes(openingTag); + // throw exception here, ADIOS2 doesn't allow XML empty tags } - else if (tag[0] == '/') // first char is / ---> closing tag + else if (tagHeader[0] == '/') // first char is / ---> closing tag { - attributes = lf_GetAttributes(tag); + attributes = lf_GetAttributes(openingTag); if (attributes.size() > 0) { throw std::invalid_argument( - "ERROR: closing tag " + tag + + "ERROR: closing tag " + tagHeader + " can't have attributes, in call to ADIOS constructor\n"); } } else // opening tag { - const std::string tagName(tag.substr(0, tag.find_first_of(" \t\n\r"))); - // look for closing tagName - const std::string closingTagName("</" + tagName + ">"); - - if (fileContent.find(closingTagName) == fileContent.npos) - { - throw std::invalid_argument( - "ERROR: closing tag missing for " + closingTagName + - " check XML config file, in call to ADIOS constructor\n"); - } - - attributes = lf_GetAttributes(tag); + attributes = lf_GetAttributes(openingTag); } return attributes; } -void RemoveXMLComments(std::string ¤tContent) noexcept -{ - std::string::size_type startComment(currentContent.find("<!--")); - - while (startComment != currentContent.npos) - { - std::string::size_type endComment(currentContent.find("-->")); - currentContent.erase(startComment, endComment - startComment + 3); - startComment = currentContent.find("<!--"); - } -} - void InitXML(const std::string configXML, const MPI_Comm mpiComm, const bool debugMode, std::vector<std::shared_ptr<Transform>> &transforms, std::map<std::string, IO> &ios) { - // if using collective IO only? - std::string fileContents = BroadcastFileContents(configXML, mpiComm); + // independent IO + std::string fileContents(FileToString(configXML)); + RemoveCommentsXML(fileContents); // adios-config - std::string::size_type currentPosition(0); - std::string currentContent(GetSubString("<adios-config ", "</adios-config>", - fileContents, currentPosition)); - RemoveXMLComments(currentContent); + std::string::size_type position(0); + const TagXML adiosConfigXML( + GetTagXML("adios-config", fileContents, position)); // process transforms, not yet implemented - while (currentPosition != std::string::npos) + + while (position != std::string::npos) { - std::string transformTag(GetSubString("<transform ", "</transform>", - currentContent, currentPosition)); + const TagXML transformXML( + GetTagXML("transform ", adiosConfigXML.Elements, position)); - if (transformTag.empty()) // no more transforms + if (transformXML.Header.empty()) { break; } // InitTransform(transformTag, debugMode, transforms); } - currentPosition = 0; + position = 0; // process IOs - while (currentPosition != std::string::npos) + while (position != std::string::npos) { // io - std::string ioTag( - GetSubString("<io ", "</io>", currentContent, currentPosition)); + const TagXML ioXML(GetTagXML("io ", adiosConfigXML.Elements, position)); - if (ioTag.empty()) // no more groups to find + if (ioXML.Header.empty()) // no more groups to find { break; } - InitIOXML(ioTag, mpiComm, debugMode, transforms, ios); + InitIOXML(ioXML, mpiComm, debugMode, transforms, ios); } } -void InitIOXML(const std::string &ioTag, const MPI_Comm mpiComm, +void InitIOXML(const TagXML &ioXML, const MPI_Comm mpiComm, const bool debugMode, std::vector<std::shared_ptr<Transform>> &transforms, std::map<std::string, IO> &ios) { - std::string::size_type position(0); - const std::string openingTag( - GetOpeningTag("io", ioTag, position, debugMode)); - const std::string::size_type elementsStart(position); - - Params ioAttributes = GetTagAttributes(ioTag, openingTag); + const Params ioAttributes(GetTagAttributesXML(ioXML.Header)); std::string ioName; for (const auto &ioAttribute : ioAttributes) @@ -323,7 +321,7 @@ void InitIOXML(const std::string &ioTag, const MPI_Comm mpiComm, throw std::invalid_argument( "ERROR: io name=\"value\" attribute not found in opening XML " "tag " + - openingTag + + ioXML.Header + ", check XML config file, in call to ADIOS constructor\n"); } @@ -339,49 +337,45 @@ void InitIOXML(const std::string &ioTag, const MPI_Comm mpiComm, auto itIO = ios.emplace(ioName, IO(ioName, mpiComm, true, debugMode)); // process engine - std::string engineTag( - GetSubString("<engine ", "</engine>", ioTag, position)); + std::string::size_type position(0); - if (!engineTag.empty()) // no more groups to find + TagXML engineXML(GetTagXML("engine ", ioXML.Elements, position)); + if (!engineXML.Header.empty()) // found first one { - InitEngineXML(engineTag, debugMode, itIO.first->second); + InitEngineXML(engineXML, debugMode, itIO.first->second); } if (debugMode) { - // try finding a 2nd one - std::string engineTag( - GetSubString("<engine ", "</engine>", ioTag, position)); - if (!engineTag.empty()) + // try finding a 2nd one from current position + TagXML engineXML(GetTagXML("engine ", ioXML.Elements, position)); + if (!engineXML.Header.empty()) // found first one { - throw std::invalid_argument("ERROR: two engine tags found in IO " + - ioName + ", only one is allowed in XML " - "config file, in call to " - "ADIOS constructor\n"); + throw std::invalid_argument( + "ERROR: more than one engine found in <io name=" + ioName + + "...>, only one per io tag is allowed in XML " + "config file, in call to " + "ADIOS constructor\n"); } } - position = elementsStart; + position = 0; // process transports while (position != std::string::npos) { - std::string transportTag( - GetSubString("<transport ", "</transport>", ioTag, position)); + TagXML transportXML(GetTagXML("transport", ioXML.Elements, position)); - if (transportTag.empty()) // no more groups to find + if (transportXML.Header.empty()) // no more groups to find { break; } + InitTransportXML(transportXML, debugMode, itIO.first->second); } } -void InitEngineXML(const std::string &engineTag, const bool debugMode, IO &io) +void InitEngineXML(const TagXML &engineXML, const bool debugMode, IO &io) { - std::string::size_type position(0); - const std::string openingTag( - GetOpeningTag("engine", engineTag, position, debugMode)); - const std::string::size_type elementsStartPosition(position); - Params attributes = GetTagAttributes(engineTag, openingTag); + const Params attributes = GetTagAttributesXML(engineXML.Header); std::string type; for (const auto &attribute : attributes) @@ -389,22 +383,21 @@ void InitEngineXML(const std::string &engineTag, const bool debugMode, IO &io) if (attribute.first == "type") { type = attribute.second; + break; } } - io.SetEngine(type); - io.SetParameters( - ParseParamsXML(engineTag, elementsStartPosition, debugMode)); + if (!type.empty()) + { + io.SetEngine(type); + } + + io.SetParameters(ParseParamsXML(engineXML.Elements, debugMode)); } -void InitTransportXML(const std::string &transportTag, const bool debugMode, - IO &io) +void InitTransportXML(const TagXML &transportXML, const bool debugMode, IO &io) { - std::string::size_type position(0); - const std::string openingTag( - GetOpeningTag("transport", transportTag, position, debugMode)); - const std::string::size_type elementsStartPosition(position); - Params attributes = GetTagAttributes(transportTag, openingTag); + const Params attributes = GetTagAttributesXML(transportXML.Header); std::string type; for (const auto &attribute : attributes) @@ -412,24 +405,44 @@ void InitTransportXML(const std::string &transportTag, const bool debugMode, if (attribute.first == "type") { type = attribute.second; + break; } } - io.AddTransport( - type, ParseParamsXML(transportTag, elementsStartPosition, debugMode)); + if (type.empty()) + { + throw std::invalid_argument( + "ERROR: missing transport type in " + transportXML.Header + + ", in XML config file, in call to ADIOS constructor\n"); + } + + io.AddTransport(type, ParseParamsXML(transportXML.Elements, debugMode)); } -Params ParseParamsXML(const std::string &tag, - const std::string::size_type elementsStartPosition, - const bool debugMode) +Params ParseParamsXML(const std::string &tagElements, const bool debugMode) { - std::istringstream parametersSS(tag.substr(elementsStartPosition)); + auto start = tagElements.find_first_not_of(" \t\n"); + auto end = tagElements.find_last_not_of(" \t\n"); + + std::string parametersString(tagElements.substr(start, end - start + 1)); + if (debugMode) + { + if (parametersString.back() != ';') + { + throw std::invalid_argument( + "ERROR: parameters in config XML file must end with a ; " + + tagElements + ", in call to ADIOS constructor\n"); + } + } + + std::istringstream parametersSS(parametersString); std::string pair; Params parameters; while (std::getline(parametersSS, pair, ';')) { + pair = pair.substr(pair.find_first_not_of(" \t\n")); auto equalPosition = pair.find("="); if (debugMode) @@ -443,6 +456,7 @@ Params ParseParamsXML(const std::string &tag, "call to ADIOS constructor\n"); } } + const std::string key(pair.substr(0, equalPosition)); const std::string value(pair.substr(equalPosition + 1)); parameters.emplace(key, value); diff --git a/source/adios2/helper/adiosXML.h b/source/adios2/helper/adiosXML.h index 30ae76222..22d6abd89 100644 --- a/source/adios2/helper/adiosXML.h +++ b/source/adios2/helper/adiosXML.h @@ -25,43 +25,23 @@ namespace adios { -/** - * Extracts a substring between two tags from content - * @param initialTag - * @param finalTag - * @param content full string - * @param subString if found return substring between initialTag and finalTag, - * otherwise returns empty - * @param currentPosition to start the search, moved forward to finalTag - * position - */ -std::string GetSubString(const std::string initialTag, - const std::string finalTag, const std::string &content, - std::string::size_type ¤tPosition); +struct TagXML +{ + std::string Header; + std::string Elements; + bool IsFull; +}; -/** - * Determine tag type ( opening, empty, closing ), populates and returns Params - * (map<string,string>) object with xml tag attributes - * @param fileContent to check for missing tag closing - * @param tag single string: key0="value0" ... keyN="valueN" - * @return std::map of attributes: { { "key0", "value0" } ,..., { "keyN", - * "valueN" } } - */ -Params GetTagAttributes(const std::string &fileContent, const std::string &tag); +void RemoveCommentsXML(std::string ¤tContent) noexcept; -/** - * Set members m_Groups and m_HostLanguage from XML file content, called within - * Init functions - * @param fileContent file Content in a single string - * @param mpiComm MPI Communicator passed from application passed to Transport - * method if required - * @param hostLanguage return the host language from fileContent - * @param transforms return the modified transforms vector if there are - * variables with transformations - * @param groups passed returns the map of groups defined in fileContent - */ +TagXML GetTagXML(const std::string tagName, const std::string &content, + std::string::size_type &position); + +std::string::size_type +GetStringPositionXML(const std::string input, const std::string &content, + const std::string::size_type &startPosition) noexcept; -void RemoveXMLComments(std::string ¤tContent) noexcept; +Params GetTagAttributesXML(const std::string &tagHeader); /** * Called inside the ADIOS XML constructors to get contents from file, @@ -77,19 +57,16 @@ void InitXML(const std::string configXML, const MPI_Comm mpiComm, std::vector<std::shared_ptr<Transform>> &transforms, std::map<std::string, IO> &ios); -void InitIOXML(const std::string &ioTag, const MPI_Comm mpiComm, +void InitIOXML(const TagXML &ioXML, const MPI_Comm mpiComm, const bool debugMode, std::vector<std::shared_ptr<Transform>> &transforms, std::map<std::string, IO> &ios); -void InitEngineXML(const std::string &engineTag, const bool debugMode, IO &io); +void InitEngineXML(const TagXML &engineXML, const bool debugMode, IO &io); -void InitTransportXML(const std::string &transportTag, const bool debugMode, - IO &io); +void InitTransportXML(const TagXML &transportXML, const bool debugMode, IO &io); -Params ParseParamsXML(const std::string &tag, - const std::string::size_type elementsStartPosition, - const bool debugMode); +Params ParseParamsXML(const std::string &tagContents, const bool debugMode); } #endif /* ADIOS2_HELPER_ADIOSXML_H_ */ -- GitLab