text: /*
   * Distributed under the OSI-approved Apache License, Version 2.0.  See
   * accompanying file Copyright.txt for details.
   *
   * IO.cpp
   *
   *  Created on: Jan 6, 2017
   *      Author: William F Godoy godoywf@ornl.gov
   */
  
  #include "IO.h"
  #include "IO.tcc"
  
  #include <memory>
  #include <mutex>
  #include <sstream>
  #include <utility> // std::pair
  
  #include "adios2/common/ADIOSMacros.h"
  
  #include "adios2/engine/bp3/BP3Reader.h"
  #include "adios2/engine/bp3/BP3Writer.h"
  #include "adios2/engine/bp4/BP4Reader.h"
  #include "adios2/engine/bp4/BP4Writer.h"
  #ifdef ADIOS2_HAVE_BP5
  #include "adios2/engine/bp5/BP5Reader.h"
  #include "adios2/engine/bp5/BP5Writer.h"
  #endif
  #include "adios2/engine/inline/InlineReader.h"
  #include "adios2/engine/inline/InlineWriter.h"
  #include "adios2/engine/mhs/MhsReader.h"
  #include "adios2/engine/mhs/MhsWriter.h"
  #include "adios2/engine/null/NullEngine.h"
  #include "adios2/engine/nullcore/NullCoreWriter.h"
  #include "adios2/engine/plugin/PluginEngine.h"
  #include "adios2/engine/skeleton/SkeletonReader.h"
  #include "adios2/engine/skeleton/SkeletonWriter.h"
  
  #include "adios2/helper/adiosComm.h"
  #include "adios2/helper/adiosFunctions.h" //BuildParametersMap
  #include "adios2/helper/adiosString.h"
  #include <adios2sys/SystemTools.hxx> // FileIsDirectory()
  
  #ifdef ADIOS2_HAVE_DATAMAN // external dependencies
  #include "adios2/engine/dataman/DataManReader.h"
  #include "adios2/engine/dataman/DataManWriter.h"
  #endif
  
  #ifdef ADIOS2_HAVE_SST // external dependencies
  #include "adios2/engine/sst/SstReader.h"
  #include "adios2/engine/sst/SstWriter.h"
  #endif
  
  namespace adios2
  {
  namespace core
  {
  
  IO::EngineFactoryEntry IO_MakeEngine_HDF5();
  
  namespace
  {
  std::unordered_map<std::string, IO::EngineFactoryEntry> Factory = {
      {"bp3",
       {IO::MakeEngine<engine::BP3Reader>, IO::MakeEngine<engine::BP3Writer>}},
      {"bp4",
       {IO::MakeEngine<engine::BP4Reader>, IO::MakeEngine<engine::BP4Writer>}},
      {"bp5",
  #ifdef ADIOS2_HAVE_BP5
       {IO::MakeEngine<engine::BP5Reader>, IO::MakeEngine<engine::BP5Writer>}
  #else
       IO::NoEngineEntry("ERROR: this version didn't compile with "
                         "BP5 library, can't use BP5 engine\n")
  #endif
      },
      {"dataman",
  #ifdef ADIOS2_HAVE_DATAMAN
       {IO::MakeEngine<engine::DataManReader>,
        IO::MakeEngine<engine::DataManWriter>}
  #else
       IO::NoEngineEntry("ERROR: this version didn't compile with "
                         "DataMan library, can't use DataMan engine\n")
  #endif
      },
      {"ssc", IO::NoEngineEntry("ERROR: this version didn't compile with "
                                "SSC library, can't use SSC engine\n")},
      {"mhs",
  #ifdef ADIOS2_HAVE_MHS
       {IO::MakeEngine<engine::MhsReader>, IO::MakeEngine<engine::MhsWriter>}
  #else
       IO::NoEngineEntry("ERROR: this version didn't compile with "
                         "MHS library, can't use MHS engine\n")
  #endif
      },
      {"sst",
  #ifdef ADIOS2_HAVE_SST
       {IO::MakeEngine<engine::SstReader>, IO::MakeEngine<engine::SstWriter>}
  #else
       IO::NoEngineEntry("ERROR: this version didn't compile with "
                         "Sst library, can't use Sst engine\n")
  #endif
      },
      {"effis",
  #ifdef ADIOS2_HAVE_SST
       {IO::MakeEngine<engine::SstReader>, IO::MakeEngine<engine::SstWriter>}
  #else
       IO::NoEngineEntry("ERROR: this version didn't compile with "
                         "Sst library, can't use Sst engine\n")
  #endif
      },
      {"dataspaces",
       IO::NoEngineEntry("ERROR: this version didn't compile with "
                         "DataSpaces library, can't use DataSpaces engine\n")},
      {"hdf5",
  #ifdef ADIOS2_HAVE_HDF5
       IO_MakeEngine_HDF5()
  #else
       IO::NoEngineEntry("ERROR: this version didn't compile with "
                         "HDF5 library, can't use HDF5 engine\n")
  #endif
      },
      {"skeleton",
       {IO::MakeEngine<engine::SkeletonReader>,
        IO::MakeEngine<engine::SkeletonWriter>}},
      {"inline",
       {IO::MakeEngine<engine::InlineReader>,
        IO::MakeEngine<engine::InlineWriter>}},
      {"null",
       {IO::MakeEngine<engine::NullEngine>, IO::MakeEngine<engine::NullEngine>}},
      {"nullcore",
       {IO::NoEngine("ERROR: nullcore engine does not support read mode"),
        IO::MakeEngine<engine::NullCoreWriter>}},
      {"plugin",
       {IO::MakeEngine<plugin::PluginEngine>,
        IO::MakeEngine<plugin::PluginEngine>}},
  };
  
  // Synchronize access to the factory in case one thread is
  // looking up while another registers additional entries.
  std::mutex FactoryMutex;
  
  std::unordered_map<std::string, IO::EngineFactoryEntry>::const_iterator
  FactoryLookup(std::string const &name)
  {
      std::lock_guard<std::mutex> factoryGuard(FactoryMutex);
      (void)factoryGuard;
      return Factory.find(name);
  }
  
  struct ThrowError
  {
      std::shared_ptr<Engine> operator()(IO &, const std::string &, const Mode,
                                         helper::Comm) const
      {
          helper::Throw<std::invalid_argument>("Core", "IO", "Operator", Err);
          return nullptr;
      }
      std::string Err;
  };
  
  } // end anonymous namespace
  
  IO::MakeEngineFunc IO::NoEngine(std::string e) { return ThrowError{e}; }
  
  IO::EngineFactoryEntry IO::NoEngineEntry(std::string e)
  {
      return {NoEngine(e), NoEngine(e)};
  }
  
  void IO::RegisterEngine(const std::string &engineType, EngineFactoryEntry entry)
  {
      std::lock_guard<std::mutex> factoryGuard(FactoryMutex);
      Factory[engineType] = std::move(entry);
  }
  
  IO::IO(ADIOS &adios, const std::string name, const bool inConfigFile,
         const std::string hostLanguage)
  : m_ADIOS(adios), m_Name(name), m_HostLanguage(hostLanguage),
    m_InConfigFile(inConfigFile)
  {
  }
  
  IO::~IO() = default;
  
  void IO::SetEngine(const std::string engineType) noexcept
  {
      auto lf_InsertParam = [&](const std::string &key,
                                const std::string &value) {
          m_Parameters.insert(std::pair<std::string, std::string>(key, value));
      };
  
      /* First step in handling virtual engine names */
      std::string finalEngineType;
      std::string engineTypeLC = engineType;
      std::transform(engineTypeLC.begin(), engineTypeLC.end(),
                     engineTypeLC.begin(), ::tolower);
      if (engineTypeLC == "insituviz" || engineTypeLC == "insituvisualization")
      {
          finalEngineType = "SST";
          lf_InsertParam("FirstTimestepPrecious", "true");
          lf_InsertParam("RendezvousReaderCount", "0");
          lf_InsertParam("QueueLimit", "3");
          lf_InsertParam("QueueFullPolicy", "Discard");
          lf_InsertParam("AlwaysProvideLatestTimestep", "false");
      }
      else if (engineTypeLC == "insituanalysis")
      {
          finalEngineType = "SST";
          lf_InsertParam("FirstTimestepPrecious", "false");
          lf_InsertParam("RendezvousReaderCount", "1");
          lf_InsertParam("QueueLimit", "1");
          lf_InsertParam("QueueFullPolicy", "Block");
          lf_InsertParam("AlwaysProvideLatestTimestep", "false");
      }
      else if (engineTypeLC == "codecoupling")
      {
          finalEngineType = "SST";
          lf_InsertParam("FirstTimestepPrecious", "false");
          lf_InsertParam("RendezvousReaderCount", "1");
          lf_InsertParam("QueueLimit", "1");
          lf_InsertParam("QueueFullPolicy", "Block");
          lf_InsertParam("AlwaysProvideLatestTimestep", "false");
      }
      else if (engineTypeLC == "filestream")
      {
          finalEngineType = "filestream";
          lf_InsertParam("OpenTimeoutSecs", "3600");
          lf_InsertParam("StreamReader", "true");
      }
      /* "file" is handled entirely in IO::Open() as it needs the name */
      else
      {
          finalEngineType = engineType;
      }
  
      m_EngineType = finalEngineType;
  }
  void IO::SetIOMode(const IOMode ioMode) { m_IOMode = ioMode; }
  
  void IO::SetParameters(const Params &parameters) noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      for (const auto &parameter : parameters)
      {
          m_Parameters[parameter.first] = parameter.second;
      }
  }
  
  void IO::SetParameters(const std::string &parameters)
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      adios2::Params parameterMap =
          adios2::helper::BuildParametersMap(parameters, '=', ',');
      SetParameters(parameterMap);
  }
  
  void IO::SetParameter(const std::string key, const std::string value) noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      m_Parameters[key] = value;
  }
  
  Params &IO::GetParameters() noexcept { return m_Parameters; }
  
  void IO::ClearParameters() noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      m_Parameters.clear();
  }
  
  size_t IO::AddTransport(const std::string type, const Params &parameters)
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      Params parametersMap(parameters);
  
      if (parameters.count("transport") == 1 ||
          parameters.count("Transport") == 1)
      {
          helper::Throw<std::invalid_argument>(
              "Core", "IO", "AddTransport",
              "key Transport (or transport) is not allowed in transport "
              "parameters");
      }
  
      CheckTransportType(type);
  
      parametersMap["transport"] = type;
      m_TransportsParameters.push_back(parametersMap);
      return m_TransportsParameters.size() - 1;
  }
  
  void IO::SetTransportParameter(const size_t transportIndex,
                                 const std::string key, const std::string value)
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      if (transportIndex >= m_TransportsParameters.size())
      {
          helper::Throw<std::invalid_argument>(
              "Core", "IO", "SetTransportParameter",
              "transport Index " + std::to_string(transportIndex) +
                  " does not exist");
      }
  
      m_TransportsParameters[transportIndex][key] = value;
  }
  
  const VarMap &IO::GetVariables() const noexcept { return m_Variables; }
  
  const AttrMap &IO::GetAttributes() const noexcept { return m_Attributes; }
  
  bool IO::InConfigFile() const noexcept { return m_InConfigFile; }
  
  void IO::SetDeclared() noexcept { m_IsDeclared = true; }
  
  void IO::SetArrayOrder(const ArrayOrdering ArrayOrder) noexcept
  {
      if (ArrayOrder == ArrayOrdering::Auto)
      {
          if (helper::IsRowMajor(m_HostLanguage))
              m_ArrayOrder = ArrayOrdering::RowMajor;
          else
              m_ArrayOrder = ArrayOrdering::ColumnMajor;
      }
      else
          m_ArrayOrder = ArrayOrder;
  }
  
  bool IO::IsDeclared() const noexcept { return m_IsDeclared; }
  
  bool IO::RemoveVariable(const std::string &name) noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::RemoveVariable");
      bool isRemoved = false;
      auto itVariable = m_Variables.find(name);
      // variable exists
      if (itVariable != m_Variables.end())
      {
          m_Variables.erase(itVariable);
          isRemoved = true;
      }
      return isRemoved;
  }
  
  void IO::RemoveAllVariables() noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::RemoveAllVariables");
      m_Variables.clear();
  }
  
  bool IO::RemoveAttribute(const std::string &name) noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::RemoveAttribute");
      bool isRemoved = false;
      auto itAttribute = m_Attributes.find(name);
      // attribute exists
      if (itAttribute != m_Attributes.end())
      {
          // first remove the Attribute object
          const DataType type(itAttribute->second->m_Type);
  
          if (type == DataType::None)
          {
              // nothing to do
          }
          else
          {
              m_Attributes.erase(itAttribute);
              isRemoved = true;
          }
      }
  
      return isRemoved;
  }
  
  void IO::RemoveAllAttributes() noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::RemoveAllAttributes");
      m_Attributes.clear();
  }
  
  std::map<std::string, Params>
  IO::GetAvailableVariables(const std::set<std::string> &keys) noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::GetAvailableVariables");
  
      std::map<std::string, Params> variablesInfo;
      for (const auto &variablePair : m_Variables)
      {
          const std::string variableName = variablePair.first;
          const DataType type = InquireVariableType(variableName);
  
          if (type == DataType::Compound)
          {
          }
  #define declare_template_instantiation(T)                                      \
      else if (type == helper::GetDataType<T>())                                 \
      {                                                                          \
          variablesInfo[variableName] = GetVariableInfo<T>(variableName, keys);  \
      }
          ADIOS2_FOREACH_STDTYPE_1ARG(declare_template_instantiation)
  #undef declare_template_instantiation
      }
  
      return variablesInfo;
  }
  
  std::map<std::string, Params>
  IO::GetAvailableAttributes(const std::string &variableName,
                             const std::string separator,
                             const bool fullNameKeys) noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::GetAvailableAttributes");
      std::map<std::string, Params> attributesInfo;
  
      if (!variableName.empty())
      {
          auto itVariable = m_Variables.find(variableName);
          const DataType type = InquireVariableType(itVariable);
  
          if (type == DataType::Compound)
          {
          }
          else
          {
              attributesInfo = itVariable->second->GetAttributesInfo(
                  *this, separator, fullNameKeys);
          }
          return attributesInfo;
      }
  
      // return all attributes if variable name is empty
      for (const auto &attributePair : m_Attributes)
      {
          const std::string &name = attributePair.first;
  
          if (attributePair.second->m_Type == DataType::Compound)
          {
          }
          else
          {
              attributesInfo[name] = attributePair.second->GetInfo();
          }
      }
      return attributesInfo;
  }
  
  DataType IO::InquireVariableType(const std::string &name) const noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      auto itVariable = m_Variables.find(name);
      return InquireVariableType(itVariable);
  }
  
  DataType IO::InquireVariableType(const VarMap::const_iterator itVariable) const
      noexcept
  {
      if (itVariable == m_Variables.end())
      {
          return DataType::None;
      }
  
      const DataType type = itVariable->second->m_Type;
  
      if (m_ReadStreaming)
      {
          if (type == DataType::Compound)
          {
          }
          else
          {
              if (!itVariable->second->IsValidStep(m_EngineStep + 1))
              {
                  return DataType::None;
              }
          }
      }
  
      return type;
  }
  
  DataType IO::InquireAttributeType(const std::string &name,
                                    const std::string &variableName,
                                    const std::string separator) const noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      const std::string globalName =
          helper::GlobalName(name, variableName, separator);
  
      auto itAttribute = m_Attributes.find(globalName);
      if (itAttribute == m_Attributes.end())
      {
          return DataType::None;
      }
  
      return itAttribute->second->m_Type;
  }
  
  void IO::AddOperation(const std::string &variable,
                        const std::string &operatorType,
                        const Params &parameters) noexcept
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      m_VarOpsPlaceholder[variable].push_back({operatorType, parameters});
  }
  
  Engine &IO::Open(const std::string &name, const Mode mode, helper::Comm comm)
  {
      PERFSTUBS_SCOPED_TIMER("IO::Open");
      auto itEngineFound = m_Engines.find(name);
      const bool isEngineFound = (itEngineFound != m_Engines.end());
      bool isEngineActive = false;
      Mode mode_to_use = mode;
  
      if (isEngineFound)
      {
          if (*itEngineFound->second)
          {
              isEngineActive = true;
          }
      }
  
      if (isEngineFound)
      {
          if (isEngineActive) // check if active
          {
              helper::Throw<std::invalid_argument>(
                  "Core", "IO", "Open", "Engine " + name + " is opened twice");
          }
      }
  
      if (isEngineFound)
      {
          if (!isEngineActive)
          {
              m_Engines.erase(name);
          }
      }
  
      std::shared_ptr<Engine> engine;
      const bool isDefaultEngine = m_EngineType.empty() ? true : false;
      std::string engineTypeLC = m_EngineType;
      if (!isDefaultEngine)
      {
          std::transform(engineTypeLC.begin(), engineTypeLC.end(),
                         engineTypeLC.begin(), ::tolower);
      }
  
      /* Second step in handling virtual engines */
      /* BPFile for read needs to use BP5, BP4, or BP3 depending on the file's
       * version
       */
      if ((engineTypeLC == "file" || engineTypeLC == "bpfile" ||
           engineTypeLC == "bp" || isDefaultEngine))
      {
          if (helper::EndsWith(name, ".h5", false))
          {
              engineTypeLC = "hdf5";
          }
          else if ((mode_to_use == Mode::Read) ||
                   (mode_to_use == Mode::ReadRandomAccess))
          {
              if (adios2sys::SystemTools::FileIsDirectory(name))
              {
                  char v = helper::BPVersion(name, comm, m_TransportsParameters);
                  engineTypeLC = "bp";
                  engineTypeLC.push_back(v);
              }
              else if (adios2sys::SystemTools::FileIsDirectory(name + ".tier0"))
              {
                  engineTypeLC = "mhs";
              }
              else
              {
                  if (helper::EndsWith(name, ".bp", false))
                  {
                      engineTypeLC = "bp3";
                  }
                  else
                  {
                      /* We need to figure out the type of file
                       * from the file itself
                       */
                      if (helper::IsHDF5File(name, comm, m_TransportsParameters))
                      {
                          engineTypeLC = "hdf5";
                      }
                      else
                      {
                          engineTypeLC = "bp3";
                      }
                  }
              }
          }
          else
          {
              // File default for writing: BP4
              engineTypeLC = "bp4";
          }
      }
  
      /* Note: Mismatch between BP4/BP5 writer and FileStream reader is not
         handled if writer has not created the directory yet, when FileStream
         falls back to default (BP4) */
      if (engineTypeLC == "filestream")
      {
          char v = helper::BPVersion(name, comm, m_TransportsParameters);
          engineTypeLC = "bp";
          engineTypeLC.push_back(v);
          // std::cout << "Engine " << engineTypeLC << " selected for FileStream"
          //          << std::endl;
      }
  
      if ((engineTypeLC != "bp5") && (mode_to_use == Mode::ReadRandomAccess))
      {
          // only BP5 special-cases file-reader random access mode
          mode_to_use = Mode::Read;
      }
      // For the inline engine, there must be exactly 1 reader, and exactly 1
      // writer.
      if (engineTypeLC == "inline")
      {
          if (mode_to_use == Mode::Append)
          {
              helper::Throw<std::runtime_error>(
                  "Core", "IO", "Open",
                  "Append mode is not supported in the inline engine.");
          }
  
          // See inline.rst:44
          if (mode_to_use == Mode::Sync)
          {
              helper::Throw<std::runtime_error>(
                  "Core", "IO", "Open",
                  "Sync mode is not supported in the inline engine.");
          }
  
          if (m_Engines.size() >= 2)
          {
              std::string msg =
                  "Failed to add engine " + name + " to IO \'" + m_Name + "\'. ";
              msg += "An inline engine must have exactly one writer, and one "
                     "reader. ";
              msg += "There are already two engines declared, so no more can be "
                     "added.";
              helper::Throw<std::runtime_error>("Core", "IO", "Open", msg);
          }
          // Now protect against declaration of two writers, or declaration of
          // two readers:
          if (m_Engines.size() == 1)
          {
              auto engine_ptr = m_Engines.begin()->second;
              if (engine_ptr->OpenMode() == mode_to_use)
              {
                  std::string msg =
                      "The previously added engine " + engine_ptr->m_Name +
                      " is already opened in same mode requested for " + name +
                      ". ";
                  msg += "The inline engine requires exactly one writer and one "
                         "reader.";
                  helper::Throw<std::runtime_error>("Core", "IO", "Open", msg);
              }
          }
      }
  
      auto f = FactoryLookup(engineTypeLC);
      if (f != Factory.end())
      {
          if ((mode_to_use == Mode::Read) ||
              (mode_to_use == Mode::ReadRandomAccess))
          {
              engine =
                  f->second.MakeReader(*this, name, mode_to_use, std::move(comm));
          }
          else
          {
              engine =
                  f->second.MakeWriter(*this, name, mode_to_use, std::move(comm));
          }
      }
      else
      {
          helper::Throw<std::invalid_argument>("Core", "IO", "Open",
                                               "Engine type " + m_EngineType +
                                                   " is not valid");
      }
  
      auto itEngine = m_Engines.emplace(name, std::move(engine));
  
      if (!itEngine.second)
      {
          helper::Throw<std::invalid_argument>(
              "Core", "IO", "Open", "failed to create Engine " + m_EngineType);
      }
      // return a reference
      return *itEngine.first->second.get();
  }
  
  Engine &IO::Open(const std::string &name, const Mode mode)
  {
      return Open(name, mode, m_ADIOS.GetComm().Duplicate());
  }
  Group &IO::CreateGroup(char delimiter)
  {
      m_Gr = std::make_shared<Group>("", delimiter, *this);
      m_Gr->BuildTree();
      return *m_Gr;
  }
  Engine &IO::GetEngine(const std::string &name)
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      auto itEngine = m_Engines.find(name);
      if (itEngine == m_Engines.end())
      {
          helper::Throw<std::invalid_argument>("Core", "IO", "GetEngine",
                                               "Engine " + name + " not found");
      }
      // return a reference
      return *itEngine->second.get();
  }
  
  void IO::RemoveEngine(const std::string &name)
  {
      auto itEngine = m_Engines.find(name);
      if (itEngine != m_Engines.end())
      {
          m_Engines.erase(itEngine);
      }
  }
  
  void IO::EnterComputationBlock() noexcept
  {
      for (auto &enginePair : m_Engines)
      {
          auto &engine = enginePair.second;
          if (engine->OpenMode() != Mode::Read)
          {
              enginePair.second->EnterComputationBlock();
          }
      }
  }
  
  void IO::ExitComputationBlock() noexcept
  {
      for (auto &enginePair : m_Engines)
      {
          auto &engine = enginePair.second;
          if (engine->OpenMode() != Mode::Read)
          {
              enginePair.second->ExitComputationBlock();
          }
      }
  }
  
  void IO::FlushAll()
  {
      PERFSTUBS_SCOPED_TIMER("IO::FlushAll");
      for (auto &enginePair : m_Engines)
      {
          auto &engine = enginePair.second;
          if (engine->OpenMode() != Mode::Read)
          {
              enginePair.second->Flush();
          }
      }
  }
  
  void IO::ResetVariablesStepSelection(const bool zeroStart,
                                       const std::string hint)
  {
      PERFSTUBS_SCOPED_TIMER("IO::other");
      for (auto itVariable = m_Variables.begin(); itVariable != m_Variables.end();
           ++itVariable)
      {
          const DataType type = InquireVariableType(itVariable);
  
          if (type == DataType::None)
          {
              continue;
          }
  
          if (type == DataType::Compound)
          {
          }
          else
          {
              VariableBase &variable = *itVariable->second;
              variable.CheckRandomAccessConflict(hint);
              variable.ResetStepsSelection(zeroStart);
              variable.m_RandomAccess = false;
          }
      }
  }
  
  void IO::SetPrefixedNames(const bool isStep) noexcept
  {
      const std::set<std::string> attributes = helper::KeysToSet(m_Attributes);
      const std::set<std::string> variables = helper::KeysToSet(m_Variables);
  
      for (auto itVariable = m_Variables.begin(); itVariable != m_Variables.end();
           ++itVariable)
      {
          // if for each step (BP4), check if variable type is not empty
          // (means variable exist in that step)
          const DataType type = isStep ? InquireVariableType(itVariable)
                                       : itVariable->second->m_Type;
  
          if (type == DataType::None)
          {
              continue;
          }
  
          if (type == DataType::Compound)
          {
          }
          else
          {
              VariableBase &variable = *itVariable->second;
              variable.m_PrefixedVariables =
                  helper::PrefixMatches(variable.m_Name, variables);
              variable.m_PrefixedAttributes =
                  helper::PrefixMatches(variable.m_Name, attributes);
          }
      }
  
      m_IsPrefixedNames = true;
  }
  
  // PRIVATE
  void IO::CheckAttributeCommon(const std::string &name) const
  {
      auto itAttribute = m_Attributes.find(name);
      if (itAttribute != m_Attributes.end())
      {
          helper::Throw<std::invalid_argument>(
              "Core", "IO", "CheckAttributeCommon",
              "Attribute " + name + " exists in IO " + m_Name +
                  ", in call to DefineAttribute");
      }
  }
  
  void IO::CheckTransportType(const std::string type) const
  {
      if (type.empty() || type.find("=") != type.npos)
      {
          helper::Throw<std::invalid_argument>(
              "Core", "IO", "CheckTransportType",
              "wrong first argument " + type +
                  ", must be a single word for a supported transport type, in "
                  "call to IO AddTransport");
      }
  }
  
  // Explicitly instantiate the necessary public template implementations
  #define define_template_instantiation(T)                                       \
      template Variable<T> &IO::DefineVariable<T>(const std::string &,           \
                                                  const Dims &, const Dims &,    \
                                                  const Dims &, const bool);     \
      template Variable<T> *IO::InquireVariable<T>(const std::string &) noexcept;
  
  ADIOS2_FOREACH_STDTYPE_1ARG(define_template_instantiation)
  #undef define_template_instatiation
  
  #define declare_template_instantiation(T)                                      \
      template Attribute<T> &IO::DefineAttribute<T>(                             \
          const std::string &, const T *, const size_t, const std::string &,     \
          const std::string, const bool);                                        \
      template Attribute<T> &IO::DefineAttribute<T>(                             \
          const std::string &, const T &, const std::string &,                   \
          const std::string, const bool);                                        \
      template Attribute<T> *IO::InquireAttribute<T>(                            \
          const std::string &, const std::string &, const std::string) noexcept;
  
  ADIOS2_FOREACH_ATTRIBUTE_STDTYPE_1ARG(declare_template_instantiation)
  #undef declare_template_instantiation
  
  } // end namespace core
  } // end namespace adios2
  