LCOV - code coverage report
Current view: top level - src - odbxx.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 16.8 % 1375 231
Test Date: 2025-11-11 10:26:08 Functions: 18.6 % 86 16

            Line data    Source code
       1              : /********************************************************************\
       2              : 
       3              :   Name:         odbxx.cxx
       4              :   Created by:   Stefan Ritt
       5              : 
       6              :   Contents:     Object oriented interface to ODB implementation file
       7              : 
       8              : \********************************************************************/
       9              : 
      10              : #include <string>
      11              : #include <iostream>
      12              : #include <fstream>
      13              : #include <sstream>
      14              : #include <map>
      15              : #include <stdexcept>
      16              : #include <algorithm>
      17              : #include <initializer_list>
      18              : #include <cstring>
      19              : #include <bitset>
      20              : #include <functional>
      21              : 
      22              : #include "midas.h"
      23              : #include "odbxx.h"
      24              : #include "mexcept.h"
      25              : #include "mstrlcpy.h"
      26              : #include "mjson.h"
      27              : 
      28              : // #include "mleak.hxx" // un-comment for memory leak debugging
      29              : 
      30              : /*------------------------------------------------------------------*/
      31              : 
      32              : namespace midas {
      33              : 
      34              :    //----------------------------------------------------------------
      35              : 
      36              :    //---- static initializers ---------------------------------------
      37              :    thread_local odb::odb_source odb::s_odb_source = odb::ONLINE;
      38              :    thread_local std::string odb::s_odb_source_str = std::string("");
      39              : 
      40              :    // initialize static variables
      41              :    HNDLE odb::s_hDB = 0;
      42              :    bool odb::s_debug = false;
      43              :    bool odb::s_connected_odb = false;
      44              :    std::vector<midas::odb *> g_watchlist = {};
      45              : 
      46              :    // static functions ----------------------------------------------
      47              : 
      48              :    // initialize s_hDB, internal use only
      49           78 :    void odb::init_hdb() {
      50           78 :       if (s_odb_source != odb::ONLINE)
      51            0 :          mthrow("Operation only possible if connected to an online ODB");
      52              : 
      53           78 :       if (s_hDB == 0)
      54            2 :          cm_get_experiment_database(&s_hDB, nullptr);
      55           78 :       if (s_hDB == 0)
      56            0 :          mthrow("Please call cm_connect_experiment() before accessing the ODB");
      57           78 :       s_connected_odb = true;
      58           78 :    }
      59              : 
      60              :    // search for a key with a specific hKey, needed for callbacks
      61            0 :    midas::odb *odb::search_hkey(midas::odb *po, int hKey) {
      62            0 :       if (po->m_hKey == hKey)
      63            0 :          return po;
      64            0 :       if (po->m_tid == TID_KEY) {
      65            0 :          for (int i = 0; i < po->m_num_values; i++) {
      66            0 :             midas::odb *pot = search_hkey(po->m_data[i].get_podb(), hKey);
      67            0 :             if (pot != nullptr)
      68            0 :                return pot;
      69              :          }
      70              :       }
      71            0 :       return nullptr;
      72              :    }
      73              : 
      74              :    // check if a key exists in the ODB
      75            2 :    bool odb::exists(const std::string &name) {
      76            2 :       init_hdb();
      77            2 :       if (!odb::is_connected_odb())
      78            0 :          return false;
      79              :       HNDLE hkey;
      80            2 :       return db_find_key(s_hDB, 0, name.c_str(), &hkey) == DB_SUCCESS;
      81              :    }
      82              : 
      83              :    // delete a key in the ODB
      84            0 :    int odb::delete_key(const std::string &name) {
      85            0 :       init_hdb();
      86            0 :       if (!odb::is_connected_odb())
      87            0 :          return false;
      88              :       HNDLE hkey;
      89            0 :       auto status = db_find_key(s_hDB, 0, name.c_str(), &hkey);
      90            0 :       if (status != DB_SUCCESS) {
      91            0 :          if (s_debug)
      92            0 :             std::cout << "Delete key " + name << " not found in ODB" << std::endl;
      93            0 :          return status;
      94              :       }
      95            0 :       if (s_debug)
      96            0 :          std::cout << "Delete ODB key " + name << std::endl;
      97            0 :       return db_delete_key(s_hDB, hkey, false);
      98              :    }
      99              : 
     100              :    // load file into ODB
     101            0 :    void odb::load(const std::string &filename, const std::string &odb_path) {
     102            0 :       init_hdb();
     103            0 :       if (!odb::is_connected_odb())
     104            0 :          mthrow("Cannot connect to ODB");
     105              : 
     106              :       HNDLE hkey;
     107            0 :       auto status = db_find_key(s_hDB, 0, odb_path.c_str(), &hkey);
     108            0 :       if (status == DB_NO_KEY)
     109            0 :          db_create_key(s_hDB, 0, odb_path.c_str(), TID_KEY);
     110            0 :       status = db_find_key(s_hDB, 0, odb_path.c_str(), &hkey);
     111            0 :       if (status != DB_SUCCESS)
     112            0 :          mthrow("ODB path " + odb_path + " not found in ODB");
     113              : 
     114              :       KEY key;
     115            0 :       db_get_key(s_hDB, hkey, &key);
     116            0 :       if (key.type != TID_KEY)
     117            0 :          mthrow("ODB path " + odb_path + " does not point to a directory");
     118              : 
     119            0 :       db_load(s_hDB, hkey, filename.c_str(), false);
     120            0 :    }
     121              : 
     122              :    // global callback function for db_watch()
     123            0 :    void odb::watch_callback(int hDB, int hKey, int index, void *info) {
     124            0 :       midas::odb *po = static_cast<midas::odb *>(info);
     125            0 :       if (po->m_data == nullptr)
     126            0 :          mthrow("Callback received for a midas::odb object which went out of scope");
     127            0 :       midas::odb *poh = search_hkey(po, hKey);
     128            0 :       if (poh == nullptr) {
     129            0 :          auto s = db_get_path(hDB, hKey);
     130            0 :          mthrow("New key  \"" + s + "\" has been created in ODB after calling watch()");
     131            0 :       }
     132              : 
     133            0 :       poh->m_last_index = index;
     134            0 :       po->m_watch_callback(*poh);
     135            0 :       poh->m_last_index = -1;
     136            0 :    }
     137              : 
     138              :    // create ODB key
     139            0 :    int odb::create(const char *name, int type) {
     140            0 :       init_hdb();
     141            0 :       int status = -1;
     142            0 :       if (is_connected_odb())
     143            0 :          status = db_create_key(s_hDB, 0, name, type);
     144            0 :       if (status != DB_SUCCESS && status != DB_KEY_EXIST)
     145            0 :          mthrow("Cannot create key " + std::string(name) + ", db_create_key() status = " + std::to_string(status));
     146            0 :       return status;
     147              :    }
     148              : 
     149              :    // private functions ---------------------------------------------
     150              : 
     151            2 :    void odb::set_flags_recursively(uint32_t f) {
     152            2 :       m_flags = f;
     153            2 :       if (m_tid == TID_KEY) {
     154            0 :          for (int i = 0; i < m_num_values; i++)
     155            0 :             m_data[i].get_odb().set_flags_recursively(f);
     156              :       }
     157            2 :    }
     158              : 
     159            0 :    void odb::odb_from_xml_remote(const std::string &str) {
     160            0 :       init_hdb();
     161              : 
     162              :       HNDLE hKey;
     163            0 :       int status = db_find_key(s_hDB, 0, str.c_str(), &hKey);
     164            0 :       if (status != DB_SUCCESS)
     165            0 :          mthrow("ODB key \"" + str + "\" not found in ODB");
     166              : 
     167            0 :       int bsize = 1000000;
     168            0 :       char *buffer = (char *)malloc(bsize);
     169              :       do {
     170            0 :          int size = bsize;
     171            0 :          status = db_copy_xml(s_hDB, hKey, buffer, &size, false);
     172            0 :          if (status == DB_TRUNCATED) {
     173            0 :             bsize *= 2;
     174            0 :             buffer = (char *)realloc(buffer, bsize);
     175              :          }
     176            0 :       } while (status == DB_TRUNCATED);
     177              : 
     178            0 :       if (status != DB_SUCCESS)
     179            0 :          mthrow("Cannot retrieve XML data, status = " + std::to_string(status));
     180              : 
     181            0 :       if (s_debug)
     182            0 :          std::cout << "Retrieved XML tree for \"" + str + "\"" << std::endl;
     183              : 
     184            0 :       char error[256] = "";
     185            0 :       PMXML_NODE tree = mxml_parse_buffer(buffer, error, sizeof(error), NULL);
     186            0 :       if (error[0]) {
     187            0 :          std::cout << "MXML error, buffer =\n" << buffer << std::endl;
     188            0 :          mthrow("MXML error: " + std::string(error));
     189              :       }
     190            0 :       free(buffer);
     191              : 
     192            0 :       odb_from_xml(&tree->child[2], this);
     193            0 :       m_hKey = hKey;
     194              : 
     195            0 :       mxml_free_tree(tree);
     196            0 :    }
     197              : 
     198            0 :    void odb::odb_from_xml_string(const std::string &str, const std::string &subkey) {
     199            0 :       char error[256] = "";
     200            0 :       PMXML_NODE tree = mxml_parse_buffer(str.c_str(), error, sizeof(error), NULL);
     201            0 :       if (error[0]) {
     202            0 :          std::cout << "MXML error, buffer =\n" << str << std::endl;
     203            0 :          mthrow("MXML error: " + std::string(error));
     204              :       }
     205              : 
     206              :       PMXML_NODE root;
     207            0 :       if (subkey == "/")
     208            0 :          root = &tree->child[2];
     209              :       else {
     210              :          // convert ODB path to XPATH
     211            0 :          std::stringstream ss(subkey);
     212            0 :          std::string dir, xpath;
     213            0 :          while (std::getline(ss, dir, '/'))
     214            0 :             if (!dir.empty())
     215            0 :                xpath += "/dir[@name=\"" + dir + "\"]";
     216              : 
     217              :          // std::string query("/dir[@name=\"Buffer sizes\"]");
     218            0 :          root = mxml_find_node(&tree->child[2], xpath.c_str());
     219            0 :          if (root == nullptr) {
     220              :             // try with last element being a key
     221            0 :             size_t pos = xpath.rfind("dir");
     222            0 :             if (pos != std::string::npos)
     223            0 :                xpath.replace(pos, 3, "key");
     224            0 :             root = mxml_find_node(&tree->child[2], xpath.c_str());
     225            0 :             if (root == nullptr)
     226            0 :                mthrow("ODB key \"" + subkey + "\" not found in ODB");
     227              :          }
     228            0 :       }
     229              : 
     230            0 :       odb_from_xml(root, this);
     231            0 :       mxml_free_tree(tree);
     232              : 
     233            0 :       this->set_auto_create(false);
     234            0 :       this->set_auto_refresh_read(false);
     235            0 :       this->set_auto_refresh_write(false);
     236            0 :       this->set_auto_enlarge_array(false);
     237            0 :       this->set_write_protect(true);
     238            0 :       this->set_trigger_hotlink(false);
     239            0 :    }
     240              : 
     241            0 :    std::vector<std::string> split(std::string input, char delimiter = '/') {
     242            0 :       std::vector<std::string> result;
     243            0 :       std::string item;
     244              : 
     245            0 :       if (!input.empty() && input.front() == '/')
     246            0 :          input.erase(0, 1);
     247            0 :       if (!input.empty() && input.back() == '/')
     248            0 :          input.pop_back();
     249              : 
     250            0 :       std::stringstream ss(input);
     251              : 
     252            0 :       while (std::getline(ss, item, delimiter)) {
     253            0 :          result.push_back(item);
     254              :       }
     255              : 
     256            0 :       return result;
     257            0 :    }
     258              : 
     259            0 :    void odb::odb_from_json_string(const std::string &str, const std::string &subkey) {
     260              : 
     261            0 :       MJsonNode* node = MJsonNode::Parse(str.c_str());
     262            0 :       if (node->GetType() == MJSON_ERROR)
     263            0 :          mthrow("Error parsing JSON: " + node->GetError());
     264              : 
     265              :       // node->Dump();
     266              : 
     267            0 :       if (subkey == "/") {
     268            0 :          odb_from_json(node, "root", TID_KEY, this);
     269              :       } else {
     270              : 
     271              :          // split path into vector of subdirectories
     272            0 :          auto dirs = split(subkey);
     273              : 
     274            0 :          int tid = TID_KEY;
     275              : 
     276              :          // traverse subdirectories
     277            0 :          std::string root_name = "root";
     278            0 :          for (std::string subdir : dirs) {
     279            0 :             const MJsonStringVector* names = node->GetObjectNames();
     280            0 :             const MJsonNodeVector* nodes = node->GetObjectNodes();
     281              : 
     282              :             int i;
     283            0 :             for (i=0; i<(int)names->size(); i++) {
     284            0 :                const char *name = (*names)[i].c_str();
     285            0 :                MJsonNode *subnode = (*nodes)[i];
     286            0 :                if (strchr(name, '/')) // skip special entries
     287            0 :                   continue;
     288            0 :                if (node->GetType() == MJSON_OBJECT)
     289            0 :                   if (name == subdir) {
     290            0 :                      auto key = node->FindObjectNode((std::string(name) + "/key").c_str());
     291            0 :                      if (key)
     292            0 :                         tid = key->FindObjectNode("type")->GetInt();
     293              :                      else
     294            0 :                         tid = TID_KEY;
     295              : 
     296            0 :                      node = subnode;
     297            0 :                      root_name = subdir;
     298            0 :                      break;
     299              :                   }
     300              :                }
     301            0 :                if (i == (int)names->size())
     302            0 :                   mthrow("Subdirectory \"" + subdir + "\" not found in JSON");
     303            0 :             }
     304              : 
     305            0 :          odb_from_json(node, root_name, tid, this);
     306            0 :       }
     307              : 
     308              : 
     309            0 :       this->set_auto_create(false);
     310            0 :       this->set_auto_refresh_read(false);
     311            0 :       this->set_auto_refresh_write(false);
     312            0 :       this->set_auto_enlarge_array(false);
     313            0 :       this->set_write_protect(true);
     314            0 :       this->set_trigger_hotlink(false);
     315            0 :    }
     316              : 
     317              :    // resize internal m_data array, keeping old values
     318            0 :    void odb::resize_mdata(int size) {
     319            0 :       auto new_array = new u_odb[size]{};
     320              :       int i;
     321            0 :       for (i = 0; i < m_num_values && i < size; i++) {
     322            0 :          new_array[i] = m_data[i];
     323            0 :          if (m_tid == TID_KEY)
     324            0 :             m_data[i].set_odb(nullptr); // move odb*
     325            0 :          if (m_tid == TID_STRING || m_tid == TID_LINK)
     326            0 :             m_data[i].set_string_ptr(nullptr); // move std::string*
     327              :       }
     328            0 :       for (; i < size; i++) {
     329            0 :          if (m_tid == TID_STRING || m_tid == TID_LINK)
     330            0 :             new_array[i].set_string(""); // allocates new string
     331              :       }
     332            0 :       delete[] m_data;
     333            0 :       m_data = new_array;
     334            0 :       m_num_values = size;
     335            0 :       for (i = 0; i < m_num_values; i++) {
     336            0 :          m_data[i].set_tid(m_tid);
     337            0 :          m_data[i].set_parent(this);
     338              :       }
     339            0 :    }
     340              : 
     341              :    // get function for strings
     342            2 :    void odb::get(std::string &s, bool quotes, bool refresh) {
     343            2 :       if (refresh && is_auto_refresh_read())
     344            2 :          read();
     345              : 
     346              :       // put value into quotes
     347            2 :       s = "";
     348            2 :       std::string sd;
     349            4 :       for (int i = 0; i < m_num_values; i++) {
     350            2 :          m_data[i].get(sd);
     351            2 :          if (quotes)
     352            0 :             s += "\"";
     353            2 :          s += sd;
     354            2 :          if (quotes)
     355            0 :             s += "\"";
     356            2 :          if (i < m_num_values - 1)
     357            0 :             s += ",";
     358              :       }
     359            2 :    }
     360              : 
     361              :    // public functions ----------------------------------------------
     362              : 
     363              :    // Deep copy constructor
     364            2 :    odb::odb(const odb &o) : odb() {
     365            2 :       m_tid = o.m_tid;
     366            2 :       m_name = o.m_name;
     367            2 :       m_num_values = o.m_num_values;
     368            2 :       m_hKey = o.m_hKey;
     369            2 :       m_watch_callback = o.m_watch_callback;
     370            4 :       m_data = new midas::u_odb[m_num_values]{};
     371            4 :       for (int i = 0; i < m_num_values; i++) {
     372            2 :          m_data[i].set_tid(m_tid);
     373            2 :          m_data[i].set_parent(this);
     374            2 :          if (m_tid == TID_STRING || m_tid == TID_LINK) {
     375              :             // set_string() creates a copy of our string
     376            2 :             m_data[i].set_string(o.m_data[i]);
     377            0 :          } else if (m_tid == TID_KEY) {
     378              :             // recursive call to create a copy of the odb object
     379            0 :             midas::odb *po = o.m_data[i].get_podb();
     380            0 :             midas::odb *pc = new midas::odb(*po);
     381            0 :             pc->set_parent(this);
     382            0 :             m_data[i].set(pc);
     383              :          } else {
     384              :             // simply pass basic types
     385            0 :             m_data[i] = o.m_data[i];
     386            0 :             m_data[i].set_parent(this);
     387              :          }
     388              :       }
     389            2 :    }
     390              : 
     391            0 :    void odb::deep_copy(odb &d, const odb &s) {
     392            0 :       d.m_tid = s.m_tid;
     393            0 :       d.m_name = s.m_name;
     394            0 :       d.m_num_values = s.m_num_values;
     395            0 :       d.m_hKey = s.m_hKey;
     396            0 :       d.m_watch_callback = s.m_watch_callback;
     397            0 :       d.m_data = new midas::u_odb[d.m_num_values]{};
     398            0 :       for (int i = 0; i < d.m_num_values; i++) {
     399            0 :          d.m_data[i].set_tid(d.m_tid);
     400            0 :          d.m_data[i].set_parent(&d);
     401            0 :          if (d.m_tid == TID_STRING || d.m_tid == TID_LINK) {
     402              :             // set_string() creates a copy of our string
     403            0 :             d.m_data[i].set_string(s.m_data[i]);
     404            0 :          } else if (d.m_tid == TID_KEY) {
     405              :             // recursive call to create a copy of the odb object
     406            0 :             midas::odb *po = s.m_data[i].get_podb();
     407            0 :             midas::odb *pc = new midas::odb(*po);
     408            0 :             pc->set_parent(&d);
     409            0 :             d.m_data[i].set(pc);
     410              :          } else {
     411              :             // simply pass basic types
     412            0 :             d.m_data[i] = s.m_data[i];
     413            0 :             d.m_data[i].set_parent(this);
     414              :          }
     415              :       }
     416            0 :    }
     417              : 
     418              :    // return full path from ODB
     419            0 :    std::string odb::get_full_path() {
     420            0 :       if (m_name[0] == '/')
     421            0 :          return m_name;
     422            0 :       if (m_parent)
     423            0 :          return m_parent->get_full_path() + "/" + m_name;
     424              : 
     425            0 :       if (!is_connected_odb() || m_hKey == -1)
     426            0 :          return m_name;
     427              : 
     428            0 :       std::string str = db_get_path(s_hDB, m_hKey);
     429            0 :       if (str == "/") // change "/" to ""
     430            0 :          str = "";
     431            0 :       return str;
     432            0 :    }
     433              : 
     434              :    // return parent object
     435            0 :    std::string odb::get_parent_path() {
     436            0 :       std::string s = get_full_path();
     437            0 :       std::size_t i = s.find_last_of("/");
     438            0 :       s = s.substr(0, i);
     439            0 :       return s;
     440            0 :    }
     441              : 
     442              :    // return size of ODB key
     443            0 :    int odb::size() {
     444            0 :       return m_num_values;
     445              :    }
     446              : 
     447              :    // Resize an ODB key
     448            0 :    void odb::resize(int size) {
     449            0 :       resize_mdata(size);
     450            0 :       if (this->is_auto_refresh_write()) {
     451            0 :          int status = db_set_num_values(s_hDB, m_hKey, size);
     452            0 :          if (status != DB_SUCCESS)
     453            0 :             mthrow("db_set_num_values for ODB key \"" + get_full_path() +
     454              :                    "\" failed with status " + std::to_string(status));
     455              :       }
     456            0 :    }
     457              : 
     458              :    // Resize an ODB key with default value
     459            0 :    void odb::resize(int size, bool b) {
     460            0 :       int old_size = m_num_values;
     461            0 :       resize_mdata(size);
     462            0 :       if (this->is_auto_refresh_write()) {
     463            0 :          int status = db_set_num_values(s_hDB, m_hKey, size);
     464            0 :          if (status != DB_SUCCESS)
     465            0 :             mthrow("db_set_num_values for ODB key \"" + get_full_path() +
     466              :                    "\" failed with status " + std::to_string(status));
     467              :       }
     468            0 :       if (size > old_size) {
     469            0 :          for (int i=old_size ; i<size ; i++)
     470            0 :             m_data[i] = b;
     471            0 :          if (this->is_auto_refresh_write())
     472            0 :             write();
     473              :       }
     474            0 :    }
     475              : 
     476            0 :    std::string odb::print() {
     477            0 :       std::string s;
     478            0 :       s = "{\n";
     479            0 :       print(s, 1);
     480            0 :       s += "\n}";
     481            0 :       return s;
     482            0 :    }
     483              : 
     484            0 :    std::string odb::dump() {
     485            0 :       std::string s;
     486            0 :       s = "{\n";
     487            0 :       dump(s, 1);
     488            0 :       s += "\n}";
     489            0 :       return s;
     490            0 :    }
     491              : 
     492              :    // print current object with all sub-objects nicely indented
     493            0 :    void odb::print(std::string &s, int indent) {
     494            0 :       for (int i = 0; i < indent; i++)
     495            0 :          s += "   ";
     496            0 :       if (m_tid == TID_KEY) {
     497            0 :          s += "\"" + m_name + "\": {\n";
     498            0 :          for (int i = 0; i < m_num_values; i++) {
     499            0 :             std::string v;
     500              :             // recursive call
     501            0 :             m_data[i].get_odb().print(v, indent + 1);
     502            0 :             s += v;
     503            0 :             if (i < m_num_values - 1)
     504            0 :                s += ",\n";
     505              :             else
     506            0 :                s += "\n";
     507            0 :          }
     508            0 :          for (int i = 0; i < indent; i++)
     509            0 :             s += "   ";
     510            0 :          s += "}";
     511              :       } else {
     512            0 :          s += "\"" + m_name + "\": ";
     513            0 :          if (m_num_values > 1)
     514            0 :             s += "[";
     515            0 :          std::string v;
     516            0 :          get(v, m_tid == TID_STRING || m_tid == TID_LINK);
     517            0 :          if (m_tid == TID_LINK)
     518            0 :             s += " -> ";
     519            0 :          s += v;
     520            0 :          if (m_num_values > 1)
     521            0 :             s += "]";
     522            0 :       }
     523            0 :    }
     524              : 
     525              :    // dump current object in the same way as odbedit saves as json
     526            0 :    void odb::dump(std::string &s, int indent) {
     527            0 :       if (m_name != "root")
     528            0 :          for (int i = 0; i < indent; i++)
     529            0 :             s += "   ";
     530            0 :       if (m_tid == TID_KEY) {
     531            0 :          if (m_name == "root")
     532            0 :             indent--;
     533              :          else {
     534            0 :             s += "\"" + m_name + "\": {\n";
     535              :          }
     536            0 :          for (int i = 0; i < m_num_values; i++) {
     537            0 :             std::string v;
     538            0 :             m_data[i].get_odb().dump(v, indent + 1);
     539            0 :             s += v;
     540            0 :             if (i < m_num_values - 1)
     541            0 :                s += ",\n";
     542              :             else
     543            0 :                s += "\n";
     544            0 :          }
     545            0 :          if (m_name != "root") {
     546            0 :             for (int i = 0; i < indent; i++)
     547            0 :                s += "   ";
     548            0 :             s += "}";
     549              :          }
     550              :       } else {
     551              :          KEY key;
     552            0 :          db_get_key(s_hDB, m_hKey, &key);
     553            0 :          s += "\"" + m_name + "/key\": ";
     554            0 :          s += "{ \"type\": " + std::to_string(m_tid) + ", ";
     555            0 :          s += "\"access_mode\": " + std::to_string(key.access_mode) + ", ";
     556            0 :          s += "\"last_written\": " + std::to_string(key.last_written) + "},\n";
     557            0 :          for (int i = 0; i < indent; i++)
     558            0 :             s += "   ";
     559            0 :          s += "\"" + m_name + "\": ";
     560            0 :          if (m_num_values > 1)
     561            0 :             s += "[";
     562            0 :          std::string v;
     563            0 :          get(v, m_tid == TID_STRING || m_tid == TID_LINK);
     564            0 :          s += v;
     565            0 :          if (m_num_values > 1)
     566            0 :             s += "]";
     567            0 :       }
     568            0 :    }
     569              : 
     570              :    // save ODB tree into file in JSON format
     571            0 :    void odb::save(const std::string &filename) {
     572            0 :       std::string buffer;
     573              : 
     574            0 :       std::string header = "{\n";
     575            0 :       header += "  \"/MIDAS version\" : \"2.1\",\n";
     576            0 :       header += "  \"/filename\" : \"" + filename + "\",\n";
     577              : 
     578            0 :       std::string path = get_full_path();
     579            0 :       if (path == "")
     580            0 :          path = "/";
     581            0 :       header += "  \"/ODB path\" : \"" + path + "\",\n\n";
     582              : 
     583            0 :       odb::dump(buffer, 1);
     584              : 
     585            0 :       buffer += "\n}\n";
     586              : 
     587            0 :       std::ofstream f(filename);
     588            0 :       if (!f.is_open())
     589            0 :          mthrow("Cannot open file \"" + filename);
     590              : 
     591            0 :       f << header << buffer;
     592            0 :       f.close();
     593            0 :    }
     594              : 
     595              : 
     596              :    // check if key contains a certain subkey
     597            0 :    bool odb::is_subkey(std::string str) {
     598            0 :       if (m_tid != TID_KEY)
     599            0 :          return false;
     600              : 
     601            0 :       std::string first = str;
     602            0 :       std::string tail{};
     603            0 :       if (str.find('/') != std::string::npos) {
     604            0 :          first = str.substr(0, str.find('/'));
     605            0 :          tail = str.substr(str.find('/') + 1);
     606              :       }
     607              : 
     608              :       int i;
     609            0 :       for (i = 0; i < m_num_values; i++)
     610            0 :          if (m_data[i].get_odb().get_name() == first)
     611            0 :             break;
     612            0 :       if (i == m_num_values)
     613            0 :          return false;
     614              : 
     615            0 :       if (!tail.empty())
     616            0 :          return m_data[i].get_odb().is_subkey(tail);
     617              : 
     618            0 :       return true;
     619            0 :    }
     620              : 
     621            4 :    odb &odb::get_subkey(std::string str) {
     622            4 :       if (m_tid == 0) {
     623            0 :          if (is_auto_create()) {
     624            0 :             m_tid = TID_KEY;
     625            0 :             int status = db_create_key(s_hDB, 0, m_name.c_str(), m_tid);
     626            0 :             if (status != DB_SUCCESS && status != DB_CREATED && status != DB_KEY_EXIST)
     627            0 :                mthrow("Cannot create ODB key \"" + m_name + "\", status" + std::to_string(status));
     628            0 :             db_find_key(s_hDB, 0, m_name.c_str(), &m_hKey);
     629            0 :             if (s_debug) {
     630            0 :                if (m_name[0] == '/')
     631            0 :                   std::cout << "Created ODB key \"" + m_name + "\"" << std::endl;
     632              :                else
     633            0 :                   std::cout << "Created ODB key \"" + get_full_path() + "\"" << std::endl;
     634              :             }
     635              :             // strip path from name
     636            0 :             if (m_name.find_last_of('/') != std::string::npos)
     637            0 :                m_name = m_name.substr(m_name.find_last_of('/') + 1);
     638              :          } else
     639            0 :             mthrow("Invalid key \"" + m_name + "\" does not have subkeys");
     640              : 
     641              :       }
     642            4 :       if (m_tid != TID_KEY)
     643            0 :          mthrow("ODB key \"" + get_full_path() + "\" does not have subkeys");
     644              : 
     645            4 :       std::string first = str;
     646            4 :       std::string tail{};
     647            4 :       if (str.find('/') != std::string::npos) {
     648            0 :          first = str.substr(0, str.find('/'));
     649            0 :          tail = str.substr(str.find('/') + 1);
     650              :       }
     651              : 
     652              :       int i;
     653           16 :       for (i = 0; i < m_num_values; i++)
     654           16 :          if (equal_ustring(first.c_str(), m_data[i].get_odb().get_name().c_str()))
     655            4 :             break;
     656            4 :       if (i == m_num_values) {
     657            0 :          if (is_auto_create()) {
     658            0 :             if (m_num_values == 0) {
     659            0 :                m_num_values = 1;
     660            0 :                m_data = new u_odb[1]{};
     661            0 :                i = 0;
     662              :             } else {
     663              :                // resize array
     664            0 :                resize_mdata(m_num_values + 1);
     665            0 :                i = m_num_values - 1;
     666              :             }
     667            0 :             midas::odb *o = new midas::odb();
     668            0 :             m_data[i].set_tid(TID_KEY);
     669            0 :             m_data[i].set_parent(this);
     670            0 :             o->set_name(get_full_path() + "/" + str);
     671            0 :             o->set_tid(0); // tid is currently undefined
     672            0 :             o->set_flags(get_flags());
     673            0 :             o->set_parent(this);
     674            0 :             m_data[i].set(o);
     675              :          } else
     676            0 :             mthrow("ODB key \"" + get_full_path() + "\" does not contain subkey \"" + first + "\"");
     677              :       }
     678            4 :       if (!tail.empty())
     679            0 :          return m_data[i].get_odb().get_subkey(tail);
     680              : 
     681            4 :       return *m_data[i].get_podb();
     682            4 :    }
     683              : 
     684              :    // get number of subkeys in ODB, return number and vector of names
     685            4 :    int odb::get_subkeys(std::vector<std::string> &name) {
     686            4 :       if (m_tid != TID_KEY)
     687            0 :          return 0;
     688            4 :       if (m_hKey == 0 || m_hKey == -1)
     689            0 :          mthrow("get_sub-keys called with invalid m_hKey for ODB key \"" + m_name + "\"");
     690              : 
     691              :       // count number of subkeys in ODB
     692            4 :       std::vector<HNDLE> hlist;
     693            4 :       int n = 0;
     694            4 :       for (int i = 0;; i++) {
     695              :          HNDLE h;
     696           40 :          int status = db_enum_key(s_hDB, m_hKey, i, &h);
     697           40 :          if (status != DB_SUCCESS)
     698            4 :             break;
     699              :          KEY key;
     700           36 :          db_get_key(s_hDB, h, &key);
     701           36 :          hlist.push_back(h);
     702           36 :          name.push_back(key.name);
     703           36 :          n = i + 1;
     704           36 :       }
     705              : 
     706            4 :       return n;
     707            4 :    }
     708              : 
     709              :    // obtain key definition from ODB and allocate local data array
     710           76 :    bool odb::read_key(const std::string &path) {
     711           76 :       init_hdb();
     712              : 
     713           76 :       int status = db_find_key(s_hDB, 0, path.c_str(), &m_hKey);
     714           76 :       if (status != DB_SUCCESS)
     715            0 :          return false;
     716              : 
     717              :       KEY key;
     718           76 :       status = db_get_key(s_hDB, m_hKey, &key);
     719           76 :       if (status != DB_SUCCESS)
     720            0 :          mthrow("db_get_key for ODB key \"" + path +
     721              :                 "\" failed with status " + std::to_string(status));
     722              : 
     723              :       // check for correct type if given as parameter
     724           76 :       if (m_tid > 0 && m_tid != (int) key.type)
     725            0 :          mthrow("ODB key \"" + get_full_path() +
     726              :                 "\" has different type than specified");
     727              : 
     728           76 :       if (s_debug)
     729            0 :          std::cout << "Get definition for ODB key \"" + get_full_path() + "\"" << std::endl;
     730              : 
     731           76 :       m_tid = key.type;
     732           76 :       m_name = key.name;
     733           76 :       if (m_tid == TID_KEY) {
     734              : 
     735              :          // merge ODB keys with local keys
     736           22 :          for (int i = 0; i < m_num_values; i++) {
     737           18 :             std::string p(path);
     738           18 :             if (p.back() != '/')
     739           18 :                p += "/";
     740           18 :             p += m_data[i].get_odb().get_name();
     741              :             HNDLE h;
     742           18 :             status = db_find_key(s_hDB, 0, p.c_str(), &h);
     743           18 :             if (status != DB_SUCCESS) {
     744              :                // if key does not exist in ODB write it
     745            0 :                m_data[i].get_odb().write_key(p, true);
     746            0 :                m_data[i].get_odb().write();
     747              :             } else {
     748              :                // check key type
     749              :                KEY key;
     750           18 :                status = db_get_key(s_hDB, h, &key);
     751           18 :                if (status != DB_SUCCESS)
     752            0 :                   mthrow("db_get_key for ODB key \"" + get_full_path() +
     753              :                          "\" failed with status " + std::to_string(status));
     754           18 :                if (m_data[i].get_odb().get_tid() != (int)key.type) {
     755              :                   // write key if different
     756            0 :                   m_data[i].get_odb().write_key(p, true);
     757            0 :                   m_data[i].get_odb().write();
     758              :                }
     759           18 :                if (m_data[i].get_odb().get_tid() == TID_KEY) {
     760              :                   // update subkey structure
     761            0 :                   m_data[i].get_odb().read_key(p);
     762              :                }
     763              :             }
     764           18 :          }
     765              : 
     766              :          // read back everything from ODB
     767            4 :          std::vector<std::string> name;
     768            4 :          m_num_values = get_subkeys(name);
     769           22 :          delete[] m_data;
     770           40 :          m_data = new midas::u_odb[m_num_values]{};
     771           40 :          for (int i = 0; i < m_num_values; i++) {
     772           36 :             std::string p(path);
     773           36 :             if (p.back() != '/')
     774           36 :                p += "/";
     775           36 :             p += name[i];
     776           36 :             midas::odb *o = new midas::odb(p.c_str());
     777           36 :             o->set_parent(this);
     778           36 :             m_data[i].set_tid(TID_KEY);
     779           36 :             m_data[i].set_parent(this);
     780           36 :             m_data[i].set(o);
     781           36 :          }
     782            4 :       } else  {
     783           72 :          m_num_values = key.num_values;
     784          108 :          delete[] m_data;
     785          144 :          m_data = new midas::u_odb[m_num_values]{};
     786          144 :          for (int i = 0; i < m_num_values; i++) {
     787           72 :             m_data[i].set_tid(m_tid);
     788           72 :             m_data[i].set_parent(this);
     789              :          }
     790              :       }
     791              : 
     792           76 :       return true;
     793              :    }
     794              : 
     795              :    // create key in ODB if it does not exist, otherwise check key type
     796            0 :    bool odb::write_key(std::string &path, bool force_write) {
     797            0 :       int status = db_find_key(s_hDB, 0, path.c_str(), &m_hKey);
     798            0 :       if (status != DB_SUCCESS) {
     799            0 :          if (m_tid == 0) // auto-create subdir
     800            0 :             m_tid = TID_KEY;
     801            0 :          if (m_tid > 0 && m_tid < TID_LAST) {
     802            0 :             status = db_create_key(s_hDB, 0, path.c_str(), m_tid);
     803            0 :             if (status != DB_SUCCESS)
     804            0 :                mthrow("ODB key \"" + path + "\" cannot be created");
     805            0 :             status = db_find_key(s_hDB, 0, path.c_str(), &m_hKey);
     806            0 :             if (status != DB_SUCCESS)
     807            0 :                mthrow("ODB key \"" + path + "\" not found after creation");
     808            0 :             if (s_debug) {
     809            0 :                if (path[0] == '/')
     810            0 :                   std::cout << "Created ODB key \"" + path + "\"" << std::endl;
     811              :                else
     812            0 :                   std::cout << "Created ODB key \"" + get_full_path() + "\"" << std::endl;
     813              :             }
     814              :          } else
     815            0 :             mthrow("ODB key \"" + path + "\" cannot be found");
     816            0 :          return true;
     817              :       } else {
     818              :          KEY key;
     819            0 :          status = db_get_key(s_hDB, m_hKey, &key);
     820            0 :          if (status != DB_SUCCESS)
     821            0 :             mthrow("db_get_key for ODB key \"" + path +
     822              :                    "\" failed with status " + std::to_string(status));
     823            0 :          if (m_tid == 0)
     824            0 :             m_tid = key.type;
     825              : 
     826              :          // check for correct type
     827            0 :          if (m_tid > 0 && m_tid != (int) key.type) {
     828            0 :             if (force_write) {
     829              :                // delete and recreate key
     830            0 :                status = db_delete_key(s_hDB, m_hKey, false);
     831            0 :                if (status != DB_SUCCESS)
     832            0 :                   mthrow("db_delete_key for ODB key \"" + path +
     833              :                          "\" failed with status " + std::to_string(status));
     834            0 :                status = db_create_key(s_hDB, 0, path.c_str(), m_tid);
     835            0 :                if (status != DB_SUCCESS)
     836            0 :                   mthrow("ODB key \"" + path + "\" cannot be created");
     837            0 :                status = db_find_key(s_hDB, 0, path.c_str(), &m_hKey);
     838            0 :                if (status != DB_SUCCESS)
     839            0 :                   mthrow("ODB key \"" + path + "\" not found after creation");
     840            0 :                if (s_debug)
     841            0 :                   std::cout << "Re-created ODB key \"" + get_full_path() << "\" with different type" << std::endl;
     842              :             } else
     843              :                // abort
     844            0 :                mthrow("ODB key \"" + get_full_path() +
     845              :                       "\" has differnt type than specified");
     846            0 :          } else if (s_debug)
     847            0 :             std::cout << "Validated ODB key \"" + get_full_path() + "\"" << std::endl;
     848              : 
     849            0 :          return false;
     850              :       }
     851              :    }
     852              : 
     853              : 
     854              :    // retrieve data from ODB and assign it to this object
     855           38 :    void odb::read() {
     856           38 :       if (!is_connected_odb())
     857            0 :          return;
     858              : 
     859              :       // check if deleted
     860           38 :       if (is_deleted())
     861            0 :          mthrow("ODB key \"" + m_name + "\" cannot be pulled because it has been deleted");
     862              : 
     863           38 :       if (m_hKey == 0)
     864            0 :          return; // needed to print un-connected objects
     865              : 
     866           38 :       if (m_tid == 0)
     867            0 :          mthrow("Read of invalid ODB key \"" + m_name + "\"");
     868              : 
     869           38 :       if (m_hKey == -1) {
     870              :          // connect un-connected object (crated via XML)
     871            0 :          std::string path = get_full_path();
     872              : 
     873            0 :          int status = db_find_key(s_hDB, 0, path.c_str(), &m_hKey);
     874            0 :          if (status != DB_SUCCESS)
     875            0 :             mthrow("Cannot connect key \"" + path + "\" to ODB");
     876            0 :       }
     877              : 
     878           38 :       int status{};
     879           38 :       if (m_tid == TID_STRING) {
     880              :          KEY key;
     881           10 :          db_get_key(s_hDB, m_hKey, &key);
     882           10 :          char *str = (char *) malloc(key.total_size);
     883           10 :          int size = key.total_size;
     884           10 :          status = db_get_data(s_hDB, m_hKey, str, &size, m_tid);
     885           20 :          for (int i = 0; i < m_num_values; i++)
     886           10 :             m_data[i].set(str + i * key.item_size);
     887           10 :          free(str);
     888           28 :       } else if (m_tid == TID_KEY) {
     889            0 :          std::vector<std::string> name;
     890            0 :          int n = get_subkeys(name);
     891            0 :          if (n != m_num_values) {
     892              :             // if subdirs have changed, rebuild it
     893            0 :             delete[] m_data;
     894            0 :             m_num_values = n;
     895            0 :             m_data = new midas::u_odb[m_num_values]{};
     896            0 :             for (int i = 0; i < m_num_values; i++) {
     897            0 :                std::string k(get_full_path());
     898            0 :                k += "/" + name[i];
     899            0 :                midas::odb *o = new midas::odb(k.c_str());
     900            0 :                o->set_parent(this);
     901            0 :                m_data[i].set_tid(TID_KEY);
     902            0 :                m_data[i].set_parent(this);
     903            0 :                m_data[i].set(o);
     904            0 :             }
     905              :          }
     906            0 :          for (int i = 0; i < m_num_values; i++)
     907            0 :             m_data[i].get_odb().read();
     908            0 :          status = DB_SUCCESS;
     909            0 :       } else {
     910              :          // resize local array if number of values has changed
     911              :          KEY key;
     912           28 :          status = db_get_key(s_hDB, m_hKey, &key);
     913           28 :          if (key.num_values != m_num_values) {
     914            0 :             delete[] m_data;
     915            0 :             m_num_values = key.num_values;
     916            0 :             m_data = new midas::u_odb[m_num_values]{};
     917            0 :             for (int i = 0; i < m_num_values; i++) {
     918            0 :                m_data[i].set_tid(m_tid);
     919            0 :                m_data[i].set_parent(this);
     920              :             }
     921              :          }
     922              : 
     923           28 :          int size = rpc_tid_size(m_tid) * m_num_values;
     924           28 :          void *buffer = malloc(size);
     925           28 :          void *p = buffer;
     926           28 :          status = db_get_data(s_hDB, m_hKey, p, &size, m_tid);
     927           56 :          for (int i = 0; i < m_num_values; i++) {
     928           28 :             if (m_tid == TID_UINT8)
     929            0 :                m_data[i].set(*static_cast<uint8_t *>(p));
     930           28 :             else if (m_tid == TID_INT8)
     931            0 :                m_data[i].set(*static_cast<int8_t *>(p));
     932           28 :             else if (m_tid == TID_UINT16)
     933            0 :                m_data[i].set(*static_cast<uint16_t *>(p));
     934           28 :             else if (m_tid == TID_INT16)
     935            0 :                m_data[i].set(*static_cast<int16_t *>(p));
     936           28 :             else if (m_tid == TID_UINT32)
     937            8 :                m_data[i].set(*static_cast<uint32_t *>(p));
     938           20 :             else if (m_tid == TID_INT32)
     939            4 :                m_data[i].set(*static_cast<int32_t *>(p));
     940           16 :             else if (m_tid == TID_UINT64)
     941            0 :                m_data[i].set(*static_cast<uint64_t *>(p));
     942           16 :             else if (m_tid == TID_INT64)
     943            0 :                m_data[i].set(*static_cast<int64_t *>(p));
     944           16 :             else if (m_tid == TID_BOOL)
     945           16 :                m_data[i].set(*static_cast<bool *>(p));
     946            0 :             else if (m_tid == TID_FLOAT)
     947            0 :                m_data[i].set(*static_cast<float *>(p));
     948            0 :             else if (m_tid == TID_DOUBLE)
     949            0 :                m_data[i].set(*static_cast<double *>(p));
     950            0 :             else if (m_tid == TID_STRING)
     951            0 :                m_data[i].set(std::string(static_cast<const char *>(p)));
     952            0 :             else if (m_tid == TID_LINK)
     953            0 :                m_data[i].set(std::string(static_cast<const char *>(p)));
     954              :             else
     955            0 :                mthrow("Invalid type ID " + std::to_string(m_tid));
     956              : 
     957           28 :             p = static_cast<char *>(p) + rpc_tid_size(m_tid);
     958              :          }
     959           28 :          free(buffer);
     960              :       }
     961              : 
     962           38 :       if (status != DB_SUCCESS)
     963            0 :          mthrow("db_get_data for ODB key \"" + get_full_path() +
     964              :                 "\" failed with status " + std::to_string(status));
     965           38 :       if (s_debug) {
     966            0 :          if (m_tid == TID_KEY) {
     967            0 :             std::cout << "Get ODB key \"" + get_full_path() + "[0..." +
     968            0 :                          std::to_string(m_num_values - 1) + "]\"" << std::endl;
     969              :          } else {
     970            0 :             std::string s;
     971            0 :             get(s, false, false);
     972            0 :             if (m_num_values > 1) {
     973            0 :                if (m_tid == TID_STRING || m_tid == TID_LINK)
     974            0 :                   std::cout << "Get ODB key \"" + get_full_path() + "[0..." +
     975            0 :                                std::to_string(m_num_values - 1) + "]\": [\"" + s + "\"]" << std::endl;
     976              :                else
     977            0 :                   std::cout << "Get ODB key \"" + get_full_path() + "[0..." +
     978            0 :                                std::to_string(m_num_values - 1) + "]\": [" + s + "]" << std::endl;
     979              :             } else {
     980            0 :                if (m_tid == TID_STRING || m_tid == TID_LINK)
     981            0 :                   std::cout << "Get ODB key \"" + get_full_path() + "\": \"" + s + "\"" << std::endl;
     982              :                else
     983            0 :                   std::cout << "Get ODB key \"" + get_full_path() + "\": " + s << std::endl;
     984              :             }
     985            0 :          }
     986              :       }
     987              :    }
     988              : 
     989              :    // retrieve individual member of array
     990            0 :    void odb::read(int index) {
     991            0 :       if (!is_connected_odb())
     992            0 :          return;
     993              : 
     994            0 :       if (m_hKey == 0 || m_hKey == -1)
     995            0 :          return; // needed to print un-connected objects
     996              : 
     997            0 :       if (m_tid == 0)
     998            0 :          mthrow("Pull of invalid ODB key \"" + m_name + "\"");
     999              : 
    1000            0 :       int status{};
    1001            0 :       if (m_tid == TID_STRING || m_tid == TID_LINK) {
    1002              :          KEY key;
    1003            0 :          db_get_key(s_hDB, m_hKey, &key);
    1004            0 :          char *str = (char *) malloc(key.item_size);
    1005            0 :          int size = key.item_size;
    1006            0 :          status = db_get_data_index(s_hDB, m_hKey, str, &size, index, m_tid);
    1007            0 :          m_data[index].set(str);
    1008            0 :          free(str);
    1009            0 :       } else if (m_tid == TID_KEY) {
    1010            0 :          m_data[index].get_odb().read();
    1011            0 :          status = DB_SUCCESS;
    1012              :       } else {
    1013            0 :          int size = rpc_tid_size(m_tid);
    1014            0 :          void *buffer = malloc(size);
    1015            0 :          void *p = buffer;
    1016            0 :          status = db_get_data_index(s_hDB, m_hKey, p, &size, index, m_tid);
    1017            0 :          if (m_tid == TID_UINT8)
    1018            0 :             m_data[index].set(*static_cast<uint8_t *>(p));
    1019            0 :          else if (m_tid == TID_INT8)
    1020            0 :             m_data[index].set(*static_cast<int8_t *>(p));
    1021            0 :          else if (m_tid == TID_UINT16)
    1022            0 :             m_data[index].set(*static_cast<uint16_t *>(p));
    1023            0 :          else if (m_tid == TID_INT16)
    1024            0 :             m_data[index].set(*static_cast<int16_t *>(p));
    1025            0 :          else if (m_tid == TID_UINT32)
    1026            0 :             m_data[index].set(*static_cast<uint32_t *>(p));
    1027            0 :          else if (m_tid == TID_INT32)
    1028            0 :             m_data[index].set(*static_cast<int32_t *>(p));
    1029            0 :          else if (m_tid == TID_UINT64)
    1030            0 :             m_data[index].set(*static_cast<uint64_t *>(p));
    1031            0 :          else if (m_tid == TID_INT64)
    1032            0 :             m_data[index].set(*static_cast<int64_t *>(p));
    1033            0 :          else if (m_tid == TID_BOOL)
    1034            0 :             m_data[index].set(*static_cast<bool *>(p));
    1035            0 :          else if (m_tid == TID_FLOAT)
    1036            0 :             m_data[index].set(*static_cast<float *>(p));
    1037            0 :          else if (m_tid == TID_DOUBLE)
    1038            0 :             m_data[index].set(*static_cast<double *>(p));
    1039            0 :          else if (m_tid == TID_STRING)
    1040            0 :             m_data[index].set(std::string(static_cast<const char *>(p)));
    1041            0 :          else if (m_tid == TID_LINK)
    1042            0 :             m_data[index].set(std::string(static_cast<const char *>(p)));
    1043              :          else
    1044            0 :             mthrow("Invalid type ID " + std::to_string(m_tid));
    1045              : 
    1046            0 :          free(buffer);
    1047              :       }
    1048              : 
    1049            0 :       if (status != DB_SUCCESS)
    1050            0 :          mthrow("db_get_data for ODB key \"" + get_full_path() +
    1051              :                 "\" failed with status " + std::to_string(status));
    1052            0 :       if (s_debug) {
    1053            0 :          std::string s;
    1054            0 :          m_data[index].get(s);
    1055            0 :          if (m_tid == TID_STRING || m_tid == TID_LINK)
    1056            0 :             std::cout << "Get ODB key \"" + get_full_path() + "[" +
    1057            0 :                          std::to_string(index) + "]\": [\"" + s + "\"]" << std::endl;
    1058              :          else
    1059            0 :             std::cout << "Get ODB key \"" + get_full_path() + "[" +
    1060            0 :                          std::to_string(index) + "]\": [" + s + "]" << std::endl;
    1061            0 :       }
    1062              :    }
    1063              : 
    1064              :    // push individual member of an array
    1065            2 :    void odb::write(int index, int str_size) {
    1066            2 :       if (!is_connected_odb())
    1067            0 :          return;
    1068              : 
    1069            2 :       if (m_hKey == -1) {
    1070              :          // connect un-connected object (crated via XML)
    1071            0 :          std::string path = get_full_path();
    1072              : 
    1073            0 :          int status = db_find_key(s_hDB, 0, path.c_str(), &m_hKey);
    1074            0 :          if (status != DB_SUCCESS)
    1075            0 :             mthrow("Cannot connect key \"" + path + "\" to ODB");
    1076              : 
    1077            2 :       } else if (m_hKey == 0) {
    1078            0 :          if (is_auto_create()) {
    1079            0 :             std::string to_create = m_name[0] == '/' ? m_name : get_full_path();
    1080            0 :             int status = db_create_key(s_hDB, 0, to_create.c_str(), m_tid);
    1081            0 :             if (status != DB_SUCCESS && status != DB_CREATED && status != DB_KEY_EXIST)
    1082            0 :                mthrow("Cannot create ODB key \"" + to_create + "\", status =" + std::to_string(status));
    1083            0 :             db_find_key(s_hDB, 0, to_create.c_str(), &m_hKey);
    1084            0 :             if (s_debug) {
    1085            0 :                std::cout << "Created ODB key \"" + to_create + "\"" << std::endl;
    1086              :             }
    1087              :             // strip path from name
    1088            0 :             if (m_name.find_last_of('/') != std::string::npos)
    1089            0 :                m_name = m_name.substr(m_name.find_last_of('/') + 1);
    1090            0 :          } else
    1091            0 :             mthrow("Write of un-connected ODB key \"" + m_name + "\" not possible");
    1092              :       }
    1093              : 
    1094              :       // don't write keys
    1095            2 :       if (m_tid == TID_KEY)
    1096            0 :          return;
    1097              : 
    1098            2 :       int status{};
    1099            2 :       if (m_tid == TID_STRING || m_tid == TID_LINK) {
    1100              :          KEY key;
    1101            2 :          db_get_key(s_hDB, m_hKey, &key);
    1102            2 :          std::string s;
    1103            2 :          m_data[index].get(s);
    1104            2 :          if (m_num_values == 1) {
    1105            2 :             int size = key.item_size;
    1106            2 :             if (key.item_size == 0 || !is_preserve_string_size())
    1107            2 :                size = s.size() + 1;
    1108            2 :             if (str_size > 0)
    1109            2 :                size = str_size;
    1110            2 :             char *ss = (char *)malloc(size+1);
    1111            2 :             mstrlcpy(ss, s.c_str(), size);
    1112            2 :             if (is_trigger_hotlink())
    1113            2 :                status = db_set_data(s_hDB, m_hKey, ss, size, 1, m_tid);
    1114              :             else
    1115            0 :                status = db_set_data1(s_hDB, m_hKey, ss, size, 1, m_tid);
    1116            2 :             free(ss);
    1117              :          } else {
    1118            0 :             if (key.item_size == 0)
    1119            0 :                key.item_size = s.size() + 1;
    1120            0 :             if (str_size > 0) {
    1121            0 :                if (key.item_size > 0 && key.item_size != str_size) {
    1122            0 :                   std::cout << "ODB string size mismatch for \"" << get_full_path() <<
    1123            0 :                   "\" (" << key.item_size << " vs " << str_size << "). ODB key recreated."
    1124            0 :                             << std::endl;
    1125            0 :                   if (is_trigger_hotlink())
    1126            0 :                      status = db_set_data(s_hDB, m_hKey, s.c_str(), str_size, 1, m_tid);
    1127              :                   else
    1128            0 :                      status = db_set_data1(s_hDB, m_hKey, s.c_str(), str_size, 1, m_tid);
    1129              :                }
    1130            0 :                key.item_size = str_size;
    1131              :             }
    1132            0 :             status = db_set_data_index1(s_hDB, m_hKey, s.c_str(), key.item_size, index, m_tid, is_trigger_hotlink());
    1133              :          }
    1134            2 :          if (s_debug) {
    1135            0 :             if (m_num_values > 1)
    1136            0 :                std::cout << "Set ODB key \"" + get_full_path() + "[" + std::to_string(index) + "]\" = \"" + s
    1137            0 :                          + "\"" << std::endl;
    1138              :             else
    1139            0 :                std::cout << "Set ODB key \"" + get_full_path() + "\" = \"" + s + "\""<< std::endl;
    1140              :          }
    1141            2 :       } else {
    1142            0 :          u_odb u = m_data[index];
    1143            0 :          if (m_tid == TID_BOOL) {
    1144            0 :             u.set_parent(nullptr);
    1145            0 :             BOOL b = static_cast<bool>(u); // "bool" is only 1 Byte, BOOL is 4 Bytes
    1146            0 :             status = db_set_data_index1(s_hDB, m_hKey, &b, rpc_tid_size(m_tid), index, m_tid, is_trigger_hotlink());
    1147              :          } else {
    1148            0 :             status = db_set_data_index1(s_hDB, m_hKey, &u, rpc_tid_size(m_tid), index, m_tid, is_trigger_hotlink());
    1149              :          }
    1150            0 :          if (s_debug) {
    1151            0 :             std::string s;
    1152            0 :             u.get(s);
    1153            0 :             if (m_num_values > 1)
    1154            0 :                std::cout << "Set ODB key \"" + get_full_path() + "[" + std::to_string(index) + "]\" = " + s
    1155            0 :                          << std::endl;
    1156              :             else
    1157            0 :                std::cout << "Set ODB key \"" + get_full_path() + "\" = " + s << std::endl;
    1158            0 :          }
    1159            0 :       }
    1160            2 :       if (status != DB_SUCCESS)
    1161            0 :          mthrow("db_set_data_index for ODB key \"" + get_full_path() +
    1162              :                 "\" failed with status " + std::to_string(status));
    1163              :    }
    1164              : 
    1165              :    // write all members of an array to the ODB
    1166            2 :    void odb::write(int str_size) {
    1167              : 
    1168              :       // check if deleted
    1169            2 :       if (is_deleted())
    1170            0 :          mthrow("ODB key \"" + m_name + "\" cannot be written because it has been deleted");
    1171              : 
    1172              :       // write subkeys
    1173            2 :       if (m_tid == TID_KEY) {
    1174            0 :          for (int i = 0; i < m_num_values; i++)
    1175            0 :             m_data[i].get_odb().write();
    1176            0 :          return;
    1177              :       }
    1178              : 
    1179            2 :       if (m_tid == 0 && m_data[0].get_tid() != 0)
    1180            0 :          m_tid = m_data[0].get_tid();
    1181              : 
    1182            2 :       if (m_tid < 1 || m_tid >= TID_LAST)
    1183            0 :          mthrow("Invalid TID for ODB key \"" + get_full_path() + "\"");
    1184              : 
    1185            2 :       if ((m_hKey == 0  || m_hKey == -1) && !is_auto_create())
    1186            0 :          mthrow("Writing ODB key \"" + m_name +
    1187              :                 "\" is not possible because of invalid key handle");
    1188              : 
    1189              :       // if index operator [] returned previously a certain index, write only this one
    1190            2 :       if (m_last_index != -1) {
    1191            0 :          write(m_last_index, str_size);
    1192            0 :          m_last_index = -1;
    1193            0 :          return;
    1194              :       }
    1195              : 
    1196            2 :       if (m_num_values == 1) {
    1197            2 :          write(0, str_size);
    1198            2 :          return;
    1199              :       }
    1200              : 
    1201            0 :       if (m_hKey == -1) {
    1202              :          // connect un-connected object (crated via XML)
    1203            0 :          std::string path = get_full_path();
    1204              : 
    1205            0 :          int status = db_find_key(s_hDB, 0, path.c_str(), &m_hKey);
    1206            0 :          if (status != DB_SUCCESS)
    1207            0 :             mthrow("Cannot connect key \"" + path + "\" to ODB");
    1208              : 
    1209            0 :       } else if (m_hKey == 0) {
    1210            0 :          if (is_auto_create()) {
    1211            0 :             std::string to_create = m_name[0] == '/' ? m_name : get_full_path();
    1212            0 :             int status = db_create_key(s_hDB, 0, to_create.c_str(), m_tid);
    1213            0 :             if (status != DB_SUCCESS && status != DB_CREATED && status != DB_KEY_EXIST)
    1214            0 :                mthrow("Cannot create ODB key \"" + to_create + "\", status" + std::to_string(status));
    1215            0 :             db_find_key(s_hDB, 0, to_create.c_str(), &m_hKey);
    1216            0 :             if (s_debug) {
    1217            0 :                std::cout << "Created ODB key \"" + to_create + "\"" << std::endl;
    1218              :             }
    1219              :             // strip path from name
    1220            0 :             if (m_name.find_last_of('/') != std::string::npos)
    1221            0 :                m_name = m_name.substr(m_name.find_last_of('/') + 1);
    1222            0 :          } else
    1223            0 :             mthrow("Write of un-connected ODB key \"" + m_name + "\" not possible");
    1224              :       }
    1225              : 
    1226            0 :       int status{};
    1227            0 :       if (m_tid == TID_STRING || m_tid == TID_LINK) {
    1228            0 :          if (is_preserve_string_size() || m_num_values > 1) {
    1229              :             KEY key;
    1230            0 :             db_get_key(s_hDB, m_hKey, &key);
    1231            0 :             if (key.item_size == 0 || key.total_size == 0) {
    1232            0 :                int size = 1;
    1233            0 :                for (int i = 0; i < m_num_values; i++) {
    1234            0 :                   std::string d;
    1235            0 :                   m_data[i].get(d);
    1236            0 :                   if ((int) d.size() + 1 > size)
    1237            0 :                      size = d.size() + 1;
    1238            0 :                }
    1239              :                // round up to multiples of 32
    1240            0 :                size = (((size - 1) / 32) + 1) * 32;
    1241            0 :                key.item_size = size;
    1242            0 :                key.total_size = size * m_num_values;
    1243              :             }
    1244            0 :             char *str = (char *) calloc(m_num_values, key.item_size);
    1245            0 :             for (int i = 0; i < m_num_values; i++) {
    1246            0 :                std::string d;
    1247            0 :                m_data[i].get(d);
    1248            0 :                strncpy(str + i * key.item_size, d.c_str(), key.item_size);
    1249            0 :             }
    1250            0 :             if (is_trigger_hotlink())
    1251            0 :                status = db_set_data(s_hDB, m_hKey, str, key.item_size * m_num_values, m_num_values, m_tid);
    1252              :             else
    1253            0 :                status = db_set_data1(s_hDB, m_hKey, str, key.item_size * m_num_values, m_num_values, m_tid);
    1254            0 :             free(str);
    1255            0 :             if (s_debug) {
    1256            0 :                std::string s;
    1257            0 :                get(s, true, false);
    1258            0 :                std::cout << "Set ODB key \"" + get_full_path() +
    1259            0 :                             "[0..." + std::to_string(m_num_values - 1) + "]\" = [" + s + "]" << std::endl;
    1260            0 :             }
    1261              :          } else {
    1262            0 :             std::string s;
    1263            0 :             m_data[0].get(s);
    1264            0 :             if (is_trigger_hotlink())
    1265            0 :                status = db_set_data(s_hDB, m_hKey, s.c_str(), s.length() + 1, 1, m_tid);
    1266              :             else
    1267            0 :                status = db_set_data1(s_hDB, m_hKey, s.c_str(), s.length() + 1, 1, m_tid);
    1268            0 :             if (s_debug)
    1269            0 :                std::cout << "Set ODB key \"" + get_full_path() + "\" = " + s << std::endl;
    1270            0 :          }
    1271            0 :       } else {
    1272            0 :          int size = rpc_tid_size(m_tid) * m_num_values;
    1273            0 :          uint8_t *buffer = (uint8_t *) malloc(size);
    1274            0 :          uint8_t *p = buffer;
    1275            0 :          for (int i = 0; i < m_num_values; i++) {
    1276            0 :             if (m_tid == TID_BOOL) {
    1277              :                // bool has 1 Byte, BOOL has 4 Bytes
    1278            0 :                BOOL b = static_cast<bool>(m_data[i]);
    1279            0 :                memcpy(p, &b, rpc_tid_size(m_tid));
    1280              :             } else {
    1281            0 :                memcpy(p, (void*)&m_data[i], rpc_tid_size(m_tid));
    1282              :             }
    1283            0 :             p += rpc_tid_size(m_tid);
    1284              :          }
    1285            0 :          if (is_trigger_hotlink())
    1286            0 :             status = db_set_data(s_hDB, m_hKey, buffer, size, m_num_values, m_tid);
    1287              :          else
    1288            0 :             status = db_set_data1(s_hDB, m_hKey, buffer, size, m_num_values, m_tid);
    1289            0 :          free(buffer);
    1290            0 :          if (s_debug) {
    1291            0 :             std::string s;
    1292            0 :             get(s, false, false);
    1293            0 :             if (m_num_values > 1)
    1294            0 :                std::cout << "Set ODB key \"" + get_full_path() + "[0..." + std::to_string(m_num_values - 1) +
    1295            0 :                             "]\" = [" + s + "]" << std::endl;
    1296              :             else
    1297            0 :                std::cout << "Set ODB key \"" + get_full_path() + "\" = " + s << std::endl;
    1298            0 :          }
    1299              :       }
    1300              : 
    1301            0 :       if (status != DB_SUCCESS)
    1302            0 :          mthrow("db_set_data for ODB key \"" + get_full_path() +
    1303              :                 "\" failed with status " + std::to_string(status));
    1304              :    }
    1305              : 
    1306            0 :    void recurse_del_keys_not_in_defaults(std::string path, HNDLE hDB, HNDLE hKey, midas::odb& default_odb) {
    1307              :       // Delete any subkeys that are not in the list of defaults.
    1308              :       KEY key;
    1309            0 :       db_get_key(hDB, hKey, &key);
    1310              : 
    1311            0 :       if (key.type == TID_KEY) {
    1312            0 :          std::vector<std::string> to_delete;
    1313              : 
    1314            0 :          for (int i = 0;; i++) {
    1315              :             HNDLE hSubKey;
    1316            0 :             int status = db_enum_key(hDB, hKey, i, &hSubKey);
    1317            0 :             if (status != DB_SUCCESS)
    1318            0 :                break;
    1319              : 
    1320              :             KEY subKey;
    1321            0 :             db_get_key(hDB, hSubKey, &subKey);
    1322            0 :             std::string full_path = path + "/" + subKey.name;
    1323              : 
    1324            0 :             if (!default_odb.is_subkey(subKey.name)) {
    1325            0 :                to_delete.push_back(subKey.name);
    1326              : 
    1327            0 :                if (default_odb.get_debug()) {
    1328            0 :                   std::cout << "Deleting " << full_path << " as not in list of defaults" << std::endl;
    1329              :                }
    1330            0 :             } else if (key.type == TID_KEY) {
    1331            0 :                recurse_del_keys_not_in_defaults(full_path, hDB, hSubKey, default_odb[(const char *)(subKey.name)]);
    1332              :             }
    1333            0 :          }
    1334              : 
    1335            0 :          for (auto name : to_delete) {
    1336              :             HNDLE hSubKey;
    1337            0 :             db_find_key(hDB, hKey, name.c_str(), &hSubKey);
    1338            0 :             db_delete_key(hDB, hSubKey, FALSE);
    1339            0 :          }
    1340            0 :       }
    1341            0 :    }
    1342              : 
    1343            0 :    void recurse_get_defaults_order(std::string path, midas::odb& default_odb, std::map<std::string, std::vector<std::string> >& retval) {
    1344            0 :       for (midas::odb& sub : default_odb) {
    1345            0 :          if (sub.get_tid() == TID_KEY) {
    1346            0 :             recurse_get_defaults_order(path + "/" + sub.get_name(), sub, retval);
    1347              :          }
    1348              : 
    1349            0 :          retval[path].push_back(sub.get_name());
    1350              :       }
    1351            0 :    }
    1352              : 
    1353            0 :    void recurse_fix_order(midas::odb& default_odb, std::map<std::string, std::vector<std::string> >& user_order) {
    1354            0 :       std::string path = default_odb.get_full_path();
    1355              : 
    1356            0 :       if (user_order.find(path) != user_order.end()) {
    1357            0 :          default_odb.fix_order(user_order[path]);
    1358              :       }
    1359              : 
    1360            0 :       for (midas::odb& it : default_odb) {
    1361            0 :          if (it.get_tid() == TID_KEY) {
    1362            0 :             recurse_fix_order(it, user_order);
    1363              :          }
    1364              :       }
    1365            0 :    }
    1366              : 
    1367            0 :    void odb::fix_order(std::vector<std::string> target_order) {
    1368              :       // Fix the order of ODB keys to match that specified in target_order.
    1369              :       // The in-ODB representation is simple, as we can just use db_reorder_key()
    1370              :       // on anything that's in the wrong place.
    1371              :       // The in-memory representation is a little trickier, but we just copy raw
    1372              :       // memory into a temporary array, so we don't have to delete/recreate the
    1373              :       // u_odb objects.
    1374            0 :       std::vector<std::string> curr_order;
    1375              : 
    1376            0 :       if (get_subkeys(curr_order) <= 0) {
    1377              :          // Not a TID_KEY (or no keys)
    1378            0 :          return;
    1379              :       }
    1380              : 
    1381            0 :       if (target_order.size() != curr_order.size() || (int)target_order.size() != m_num_values) {
    1382            0 :          return;
    1383              :       }
    1384              : 
    1385            0 :       HNDLE hKey = get_hkey();
    1386            0 :       bool force_order = false;
    1387              : 
    1388              :       // Temporary location where we'll store in-memory u_odb objects in th
    1389              :       // correct order.
    1390            0 :       u_odb* new_m_data = new u_odb[m_num_values];
    1391              : 
    1392            0 :       for (int i = 0; i < m_num_values; i++) {
    1393            0 :          if (force_order || curr_order[i] != target_order[i]) {
    1394            0 :             force_order = true;
    1395              :             HNDLE hSubKey;
    1396              : 
    1397              :             // Fix the order in the ODB
    1398            0 :             db_find_key(s_hDB, hKey, target_order[i].c_str(), &hSubKey);
    1399            0 :             db_reorder_key(s_hDB, hSubKey, i);
    1400              :          }
    1401              : 
    1402              :          // Fix the order in memory
    1403            0 :          auto curr_it = std::find(curr_order.begin(), curr_order.end(), target_order[i]);
    1404              : 
    1405            0 :          if (curr_it == curr_order.end()) {
    1406              :             // Logic error - bail to avoid doing any damage to the in-memory version.
    1407            0 :             delete[] new_m_data;
    1408            0 :             return;
    1409              :          }
    1410              : 
    1411            0 :          int curr_idx = curr_it - curr_order.begin();
    1412            0 :          new_m_data[i] = m_data[curr_idx];
    1413              :       }
    1414              : 
    1415              :       // Final update of the in-memory version so they are in the correct order
    1416            0 :       for (int i = 0; i < m_num_values; i++) {
    1417            0 :          m_data[i] = new_m_data[i];
    1418              : 
    1419              :          // Nullify pointers that point to the same object in
    1420              :          // m_data and new_m_data, so the underlying object doesn't
    1421              :          // get destroyed when we delete new_m_data.
    1422            0 :          new_m_data[i].set_string_ptr(nullptr);
    1423            0 :          new_m_data[i].set_odb(nullptr);
    1424              :       }
    1425              : 
    1426            0 :       delete[] new_m_data;
    1427            0 :    }
    1428              : 
    1429              :    // connect function with separated path and key name
    1430            0 :    void odb::connect(const std::string &p, const std::string &name, bool write_defaults, bool delete_keys_not_in_defaults) {
    1431            0 :       init_hdb();
    1432              : 
    1433            0 :       if (!name.empty())
    1434            0 :          m_name = name;
    1435            0 :       std::string path(p);
    1436              : 
    1437            0 :       if (path.empty())
    1438            0 :          mthrow("odb::connect() cannot be called with an empty ODB path");
    1439              : 
    1440            0 :       if (path[0] != '/')
    1441            0 :          mthrow("odb::connect(\"" + path + "\"): path must start with leading \"/\"");
    1442              : 
    1443            0 :       if (path.back() != '/')
    1444            0 :          path += "/";
    1445              : 
    1446            0 :       path += m_name;
    1447              : 
    1448              :       HNDLE hKey;
    1449            0 :       int status = db_find_key(s_hDB, 0, path.c_str(), &hKey);
    1450            0 :       bool key_exists = (status == DB_SUCCESS);
    1451            0 :       bool created = false;
    1452              : 
    1453            0 :       if (key_exists && delete_keys_not_in_defaults) {
    1454              :          // Recurse down to delete keys as needed.
    1455              :          // We need to do this recursively BEFORE calling read/read_key for the first time
    1456              :          // to ensure that subdirectories get handled correctly.
    1457            0 :          recurse_del_keys_not_in_defaults(path, s_hDB, hKey, *this);
    1458              :       }
    1459              : 
    1460            0 :       if (!key_exists || write_defaults) {
    1461            0 :          created = write_key(path, write_defaults);
    1462              :       } else {
    1463            0 :          read_key(path);
    1464              :       }
    1465              : 
    1466              :       // correct wrong parent ODB from initializer_list
    1467            0 :       for (int i = 0; i < m_num_values; i++)
    1468            0 :          m_data[i].set_parent(this);
    1469              : 
    1470            0 :       if (m_tid == TID_KEY) {
    1471            0 :          for (int i = 0; i < m_num_values; i++)
    1472            0 :             m_data[i].get_odb().connect(get_full_path(), m_data[i].get_odb().get_name(), write_defaults);
    1473            0 :       } else if (created || write_defaults) {
    1474            0 :          write();
    1475              :       } else {
    1476            0 :          read();
    1477              :       }
    1478            0 :    }
    1479              : 
    1480              :    // send key definitions and data with optional subkeys to certain path in ODB
    1481            0 :    void odb::connect(std::string str, bool write_defaults, bool delete_keys_not_in_defaults) {
    1482              : 
    1483            0 :       if (str.empty())
    1484            0 :          mthrow("odb::connect() cannot be called with an empty ODB path");
    1485              : 
    1486            0 :       if (str[0] != '/')
    1487            0 :          mthrow("odb::connect(\"" + str + "\"): path must start with leading \"/\"");
    1488              : 
    1489            0 :       if (str == "/")
    1490            0 :          mthrow("odb::connect(\"" + str + "\"): root ODB tree is not allowed");
    1491              : 
    1492            0 :       if (str.back() == '/')
    1493            0 :          str = str.substr(0, str.size()-1);
    1494              : 
    1495              :       // separate ODB path and key nam
    1496            0 :       std::string name;
    1497            0 :       std::string path;
    1498            0 :       name = str.substr(str.find_last_of('/') + 1);
    1499            0 :       path = str.substr(0, str.find_last_of('/') + 1);
    1500              : 
    1501            0 :       connect(path, name, write_defaults, delete_keys_not_in_defaults);
    1502            0 :    }
    1503              : 
    1504              :    // shorthand for the same behavior as db_check_record:
    1505              :    // - keep values of keys that already exist with the correct type
    1506              :    // - add keys that user provided but aren't in ODB already
    1507              :    // - delete keys that are in ODB but not in user's settings
    1508              :    // - re-order ODB keys to match user's order
    1509            0 :    void odb::connect_and_fix_structure(std::string path) {
    1510              :       // Store the order the user specified.
    1511              :       // Need to do this recursively before calling connect(), as the first
    1512              :       // read() in that function merges user keys and existing keys.
    1513            0 :       std::map<std::string, std::vector<std::string> > user_order;
    1514            0 :       recurse_get_defaults_order(path, *this, user_order);
    1515              : 
    1516              :       // Main connect() that adds/deletes/updates keys as needed.
    1517            0 :       connect(path, false, true);
    1518              : 
    1519              :       // Fix order in ODB (and memory)
    1520            0 :       recurse_fix_order(*this, user_order);
    1521            0 :    }
    1522              : 
    1523            0 :    void odb::delete_key() {
    1524            0 :       init_hdb();
    1525              : 
    1526            0 :       m_name = get_full_path();
    1527              : 
    1528            0 :       if (this->is_write_protect())
    1529            0 :          mthrow("Cannot modify write protected key \"" + m_name + "\"");
    1530              : 
    1531              : 
    1532              :       // delete key in ODB
    1533            0 :       int status = db_delete_key(s_hDB, m_hKey, FALSE);
    1534            0 :       if (status != DB_SUCCESS && status != DB_INVALID_HANDLE)
    1535            0 :          mthrow("db_delete_key for ODB key \"" + m_name +
    1536              :                 "\" returnd error code " + std::to_string(status));
    1537              : 
    1538            0 :       if (s_debug)
    1539            0 :          std::cout << "Deleted ODB key \"" + m_name + "\"" << std::endl;
    1540              : 
    1541              :       // invalidate this object
    1542            0 :       delete[] m_data;
    1543            0 :       m_data = nullptr;
    1544            0 :       m_num_values = 0;
    1545            0 :       m_tid = 0;
    1546            0 :       m_hKey = 0;
    1547              : 
    1548              :       // set flag that this object has been deleted
    1549            0 :       set_deleted(true);
    1550            0 :    }
    1551              : 
    1552            0 :    void odb::set_mode(int mode) {
    1553              :       // set mode of ODB key
    1554              :       // default is MODE_READ | MODE_WRITE | MODE_DELETE
    1555              : 
    1556            0 :       init_hdb();
    1557              : 
    1558              :       // set mode in ODB
    1559            0 :       int status = db_set_mode(s_hDB, m_hKey, mode, TRUE);
    1560              : 
    1561            0 :       if (status != DB_SUCCESS && status != DB_INVALID_HANDLE)
    1562            0 :          mthrow("db_set_mode for ODB key \"" + get_full_path() +
    1563              :                 "\" returnd error code " + std::to_string(status));
    1564              : 
    1565            0 :       if (s_debug)
    1566            0 :          std::cout << "Set mode of ODB key \"" + get_full_path() + "\" to " << mode << std::endl;
    1567            0 :    }
    1568              : 
    1569            0 :    int odb::get_mode() {
    1570            0 :       init_hdb();
    1571              : 
    1572              :       // set mode in ODB
    1573              :       KEY key;
    1574            0 :       int status = db_get_key(s_hDB, m_hKey, &key);
    1575              : 
    1576            0 :       if (status != DB_SUCCESS && status != DB_INVALID_HANDLE)
    1577            0 :          mthrow("db_get_key for ODB key \"" + get_full_path() +
    1578              :                 "\" returnd error code " + std::to_string(status));
    1579              : 
    1580            0 :       return key.access_mode;
    1581              :    }
    1582              : 
    1583            0 :    unsigned int odb::get_last_written() {
    1584            0 :       init_hdb();
    1585              : 
    1586              :       // set mode in ODB
    1587              :       KEY key;
    1588            0 :       int status = db_get_key(s_hDB, m_hKey, &key);
    1589              : 
    1590            0 :       if (status != DB_SUCCESS && status != DB_INVALID_HANDLE)
    1591            0 :          mthrow("db_get_key for ODB key \"" + get_full_path() +
    1592              :                 "\" returnd error code " + std::to_string(status));
    1593              : 
    1594            0 :       return (unsigned int) (key.last_written);
    1595              :    }
    1596              : 
    1597            0 :    void odb::watch(std::function<void(midas::odb &)> f) {
    1598            0 :       if (m_hKey == 0 || m_hKey == -1)
    1599            0 :          mthrow("watch() called for ODB key \"" + m_name +
    1600              :                 "\" which is not connected to ODB");
    1601              : 
    1602              :       // create a deep copy of current object in case it
    1603              :       // goes out of scope
    1604            0 :       midas::odb* ow = new midas::odb(*this);
    1605              : 
    1606            0 :       ow->m_watch_callback = f;
    1607            0 :       db_watch(s_hDB, m_hKey, midas::odb::watch_callback, ow);
    1608              : 
    1609              :       // put object into watchlist
    1610            0 :       g_watchlist.push_back(ow);
    1611            0 :    }
    1612              : 
    1613            0 :    void odb::unwatch()
    1614              :    {
    1615            0 :       for (int i=0 ; i<(int) g_watchlist.size() ; i++) {
    1616            0 :          if (g_watchlist[i]->get_hkey() == this->get_hkey()) {
    1617            0 :             db_unwatch(s_hDB, g_watchlist[i]->get_hkey());
    1618            0 :             delete g_watchlist[i];
    1619            0 :             g_watchlist.erase(g_watchlist.begin() + i);
    1620            0 :             i--;
    1621              :          }
    1622              :       }
    1623            0 :    }
    1624              : 
    1625            0 :    void odb::unwatch_all()
    1626              :    {
    1627            0 :       for (int i=0 ; i<(int) g_watchlist.size() ; i++) {
    1628            0 :          db_unwatch(s_hDB, g_watchlist[i]->get_hkey());
    1629            0 :          delete g_watchlist[i];
    1630              :       }
    1631            0 :       g_watchlist.clear();
    1632            0 :    }
    1633              : 
    1634            0 :    void odb::set(std::string s)
    1635              :    {
    1636            0 :       if (this->is_write_protect())
    1637            0 :          mthrow("Cannot modify write protected key \"" + get_full_path() + "\"");
    1638              : 
    1639            0 :       if (m_tid == TID_BOOL)
    1640            0 :          s = (s == "y" || s == "1") ? "1" : "0";
    1641              : 
    1642            0 :       m_num_values = 1;
    1643            0 :       m_data = new u_odb[1];
    1644            0 :       m_data[0].set_parent(this);
    1645            0 :       m_data[0].set_tid(m_tid);
    1646            0 :       m_data[0].set(s);
    1647            0 :    }
    1648              : 
    1649            0 :    void odb::set(std::string s, int i)
    1650              :    {
    1651            0 :       if (this->is_write_protect())
    1652            0 :          mthrow("Cannot modify write protected key \"" + get_full_path() + "\"");
    1653              : 
    1654            0 :       if (m_tid == TID_BOOL)
    1655            0 :          s = (s == "y" || s == "1") ? "1" : "0";
    1656              : 
    1657            0 :       if (m_data == nullptr)
    1658            0 :          m_data = new u_odb[m_num_values];
    1659            0 :       m_data[i].set_parent(this);
    1660            0 :       m_data[i].set_tid(m_tid);
    1661            0 :       m_data[i].set(s);
    1662            0 :    }
    1663              : 
    1664            2 :    void odb::set_string_size(std::string s, int size)
    1665              :    {
    1666            2 :       if (this->is_write_protect())
    1667            0 :          mthrow("Cannot modify write protected key \"" + get_full_path() + "\"");
    1668              : 
    1669            2 :       m_num_values = 1;
    1670            2 :       m_tid = TID_STRING;
    1671            4 :       m_data = new u_odb[1];
    1672            2 :       m_data[0].set_parent(this);
    1673            2 :       m_data[0].set_tid(m_tid);
    1674            2 :       m_data[0].set_string_size(s, size);
    1675            2 :       set_preserve_string_size(true);
    1676            2 :    }
    1677              : 
    1678            0 :    void odb::set_odb(odb *o, int i)
    1679              :    {
    1680            0 :       if (this->is_write_protect())
    1681            0 :          mthrow("Cannot modify write protected key \"" + get_full_path() + "\"");
    1682              : 
    1683            0 :       if (m_data == nullptr)
    1684            0 :          m_data = new u_odb[m_num_values];
    1685            0 :       m_data[i].set_parent(this);
    1686            0 :       m_data[i].set_tid(m_tid);
    1687            0 :       m_data[i].set_odb(o);
    1688            0 :    }
    1689              : 
    1690              :    //-----------------------------------------------
    1691              : 
    1692              :    //---- u_odb implementations calling functions from odb
    1693              : 
    1694          110 :    u_odb::~u_odb() {
    1695          110 :       if (m_tid == TID_STRING || m_tid == TID_LINK)
    1696           18 :          delete m_string;
    1697           92 :       else if (m_tid == TID_KEY)
    1698           36 :          delete m_odb;
    1699          110 :    }
    1700              : 
    1701              :    // get function for strings
    1702            6 :    void u_odb::get(std::string &s) {
    1703            6 :       if (m_tid == TID_UINT8)
    1704            0 :          s = std::to_string(m_uint8);
    1705            6 :       else if (m_tid == TID_INT8)
    1706            0 :          s = std::to_string(m_int8);
    1707            6 :       else if (m_tid == TID_UINT16)
    1708            0 :          s = std::to_string(m_uint16);
    1709            6 :       else if (m_tid == TID_INT16)
    1710            0 :          s = std::to_string(m_int16);
    1711            6 :       else if (m_tid == TID_UINT32)
    1712            0 :          s = std::to_string(m_uint32);
    1713            6 :       else if (m_tid == TID_INT32)
    1714            0 :          s = std::to_string(m_int32);
    1715            6 :       else if (m_tid == TID_UINT64)
    1716            0 :          s = std::to_string(m_uint64);
    1717            6 :       else if (m_tid == TID_INT64)
    1718            0 :          s = std::to_string(m_int64);
    1719            6 :       else if (m_tid == TID_BOOL)
    1720            0 :          s = std::string(m_bool ? "true" : "false");
    1721            6 :       else if (m_tid == TID_FLOAT)
    1722            0 :          s = std::to_string(m_float);
    1723            6 :       else if (m_tid == TID_DOUBLE)
    1724            0 :          s = std::to_string(m_double);
    1725            6 :       else if (m_tid == TID_STRING)
    1726            6 :          s = *m_string;
    1727            0 :       else if (m_tid == TID_LINK)
    1728            0 :          s = *m_string;
    1729            0 :       else if (m_tid == TID_KEY)
    1730            0 :          m_odb->print(s, 0);
    1731            0 :       else if (m_tid == 0)
    1732            0 :          mthrow("Subkey \"" + m_parent_odb->get_name() + "\" not found");
    1733              :       else
    1734            0 :          mthrow("Invalid type ID " + std::to_string(m_tid));
    1735            6 :    }
    1736              : 
    1737              :    //---- u_odb assignment and arithmetic operators overloads which call odb::write()
    1738              : 
    1739              :    // overload assignment operators
    1740            0 :    uint8_t u_odb::operator=(uint8_t v) {
    1741            0 :       if (m_tid == 0)
    1742            0 :          m_tid = TID_UINT8;
    1743            0 :       set(v);
    1744            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1745            0 :          m_parent_odb->write();
    1746            0 :       return v;
    1747              :    }
    1748            0 :    int8_t u_odb::operator=(int8_t v) {
    1749            0 :       if (m_tid == 0)
    1750            0 :          m_tid = TID_INT8;
    1751            0 :       set(v);
    1752            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1753            0 :          m_parent_odb->write();
    1754            0 :       return v;
    1755              :    }
    1756            0 :    uint16_t u_odb::operator=(uint16_t v) {
    1757            0 :       if (m_tid == 0)
    1758            0 :          m_tid = TID_UINT16;
    1759            0 :       set(v);
    1760            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1761            0 :          m_parent_odb->write();
    1762            0 :       return v;
    1763              :    }
    1764            0 :    int16_t u_odb::operator=(int16_t v) {
    1765            0 :       if (m_tid == 0)
    1766            0 :          m_tid = TID_INT16;
    1767            0 :       set(v);
    1768            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1769            0 :          m_parent_odb->write();
    1770            0 :       return v;
    1771              :    }
    1772            0 :    uint32_t u_odb::operator=(uint32_t v) {
    1773            0 :       if (m_tid == 0)
    1774            0 :          m_tid = TID_UINT32;
    1775            0 :       set(v);
    1776            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1777            0 :          m_parent_odb->write();
    1778            0 :       return v;
    1779              :    }
    1780            0 :    int32_t u_odb::operator=(int32_t v) {
    1781            0 :       if (m_tid == 0)
    1782            0 :          m_tid = TID_INT32;
    1783            0 :       set(v);
    1784            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1785            0 :          m_parent_odb->write();
    1786            0 :       return v;
    1787              :    }
    1788            0 :    uint64_t u_odb::operator=(uint64_t v) {
    1789            0 :       if (m_tid == 0)
    1790            0 :          m_tid = TID_UINT64;
    1791            0 :       set(v);
    1792            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1793            0 :          m_parent_odb->write();
    1794            0 :       return v;
    1795              :    }
    1796            0 :    int64_t u_odb::operator=(int64_t v) {
    1797            0 :       if (m_tid == 0)
    1798            0 :          m_tid = TID_INT64;
    1799            0 :       set(v);
    1800            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1801            0 :          m_parent_odb->write();
    1802            0 :       return v;
    1803              :    }
    1804            0 :    bool u_odb::operator=(bool v) {
    1805            0 :       if (m_tid == 0)
    1806            0 :          m_tid = TID_BOOL;
    1807            0 :       set(v);
    1808            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1809            0 :          m_parent_odb->write();
    1810            0 :       return v;
    1811              :    }
    1812            0 :    float u_odb::operator=(float v) {
    1813            0 :       if (m_tid == 0)
    1814            0 :          m_tid = TID_FLOAT;
    1815            0 :       set(v);
    1816            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1817            0 :          m_parent_odb->write();
    1818            0 :       return v;
    1819              :    }
    1820            0 :    double u_odb::operator=(double v) {
    1821            0 :       if (m_tid == 0)
    1822            0 :          m_tid = TID_DOUBLE;
    1823            0 :       set(v);
    1824            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1825            0 :          m_parent_odb->write();
    1826            0 :       return v;
    1827              :    }
    1828            0 :    const char * u_odb::operator=(const char * v) {
    1829            0 :       if (m_tid == 0)
    1830            0 :          m_tid = TID_STRING;
    1831            0 :       set(v);
    1832            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1833            0 :          m_parent_odb->write();
    1834            0 :       return v;
    1835              :    }
    1836              : 
    1837            0 :    std::string * u_odb::operator=(std::string * v){
    1838            0 :       if (m_tid == 0)
    1839            0 :          m_tid = TID_STRING;
    1840            0 :        set(*v);
    1841            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1842            0 :           m_parent_odb->write();
    1843            0 :        return v;
    1844              :    }
    1845              : 
    1846            0 :    std::string u_odb::operator=(std::string v){
    1847            0 :       if (m_tid == 0)
    1848            0 :          m_tid = TID_STRING;
    1849            0 :       set(v);
    1850            0 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1851            0 :          m_parent_odb->write();
    1852            0 :       return v;
    1853              :    }
    1854              : 
    1855            2 :    void u_odb::set_string_size(std::string v, int size) {
    1856            2 :       m_tid = TID_STRING;
    1857            2 :       set(v);
    1858            2 :       if (m_parent_odb && m_parent_odb->is_auto_refresh_write())
    1859            2 :          m_parent_odb->write(size);
    1860            2 :    }
    1861              : 
    1862              :    // overload all standard conversion operators
    1863            0 :    u_odb::operator uint8_t() {
    1864            0 :       if (m_parent_odb)
    1865            0 :          m_parent_odb->set_last_index(-1);
    1866            0 :       return get<uint8_t>();
    1867              :    }
    1868            0 :    u_odb::operator int8_t() {
    1869            0 :       if (m_parent_odb)
    1870            0 :          m_parent_odb->set_last_index(-1);
    1871            0 :       return get<int8_t>();
    1872              :    }
    1873            0 :    u_odb::operator uint16_t() {
    1874            0 :       if (m_parent_odb)
    1875            0 :          m_parent_odb->set_last_index(-1);
    1876            0 :       return get<uint16_t>();
    1877              :    }
    1878            0 :    u_odb::operator int16_t() {
    1879            0 :       if (m_parent_odb)
    1880            0 :          m_parent_odb->set_last_index(-1);
    1881            0 :       return get<int16_t>();
    1882              :    }
    1883            0 :    u_odb::operator uint32_t() {
    1884            0 :       if (m_parent_odb)
    1885            0 :          m_parent_odb->set_last_index(-1);
    1886            0 :       return get<uint32_t>();
    1887              :    }
    1888            0 :    u_odb::operator int32_t() {
    1889            0 :       if (m_parent_odb)
    1890            0 :          m_parent_odb->set_last_index(-1);
    1891            0 :       return get<int32_t>();
    1892              :    }
    1893            0 :    u_odb::operator uint64_t() {
    1894            0 :       if (m_parent_odb)
    1895            0 :          m_parent_odb->set_last_index(-1);
    1896            0 :       return get<uint64_t>();
    1897              :    }
    1898            0 :    u_odb::operator int64_t() {
    1899            0 :       if (m_parent_odb)
    1900            0 :          m_parent_odb->set_last_index(-1);
    1901            0 :       return get<int64_t>();
    1902              :    }
    1903            0 :    u_odb::operator bool() {
    1904            0 :       if (m_parent_odb)
    1905            0 :          m_parent_odb->set_last_index(-1);
    1906            0 :       return get<bool>();
    1907              :    }
    1908            0 :    u_odb::operator float() {
    1909            0 :       if (m_parent_odb)
    1910            0 :          m_parent_odb->set_last_index(-1);
    1911            0 :       return get<float>();
    1912              :    }
    1913            0 :    u_odb::operator double() {
    1914            0 :       if (m_parent_odb)
    1915            0 :          m_parent_odb->set_last_index(-1);
    1916            0 :       return get<double>();
    1917              :    }
    1918            2 :    u_odb::operator std::string() {
    1919            2 :       if (m_parent_odb)
    1920            2 :          m_parent_odb->set_last_index(-1);
    1921            2 :       std::string s;
    1922            2 :       get(s);
    1923            2 :       return s;
    1924            0 :    }
    1925            0 :    u_odb::operator const char *() {
    1926            0 :       if (m_parent_odb)
    1927            0 :          m_parent_odb->set_last_index(-1);
    1928            0 :       if (m_tid != TID_STRING && m_tid != TID_LINK)
    1929            0 :          mthrow("Only ODB string keys can be converted to \"const char *\"");
    1930            0 :       return m_string->c_str();
    1931              :    }
    1932            0 :    u_odb::operator midas::odb&() {
    1933            0 :       if (m_parent_odb)
    1934            0 :          m_parent_odb->set_last_index(-1);
    1935            0 :       if (m_tid != TID_KEY)
    1936            0 :          mthrow("Only ODB directories can be converted to \"midas::odb &\"");
    1937            0 :       return *m_odb;
    1938              :    }
    1939              : 
    1940              : 
    1941            0 :    void u_odb::add(double inc, bool write) {
    1942            0 :       if (m_tid == TID_UINT8)
    1943            0 :          m_uint8 += inc;
    1944            0 :       else if (m_tid == TID_INT8)
    1945            0 :          m_int8 += inc;
    1946            0 :       else if (m_tid == TID_UINT16)
    1947            0 :          m_uint16 += inc;
    1948            0 :       else if (m_tid == TID_INT16)
    1949            0 :          m_int16 += inc;
    1950            0 :       else if (m_tid == TID_UINT32)
    1951            0 :          m_uint32 += inc;
    1952            0 :       else if (m_tid == TID_INT32)
    1953            0 :          m_int32 += inc;
    1954            0 :       else if (m_tid == TID_FLOAT)
    1955            0 :          m_float += static_cast<float>(inc);
    1956            0 :       else if (m_tid == TID_DOUBLE)
    1957            0 :          m_double += inc;
    1958              :       else
    1959            0 :          mthrow("Invalid arithmetic operation for ODB key \"" +
    1960              :                 m_parent_odb->get_full_path() + "\"");
    1961            0 :       if (write && m_parent_odb->is_auto_refresh_write())
    1962            0 :          m_parent_odb->write();
    1963            0 :    }
    1964              : 
    1965            0 :    void u_odb::mult(double f, bool write) {
    1966            0 :       int tid = m_parent_odb->get_tid();
    1967            0 :       if (tid == TID_UINT8)
    1968            0 :          m_uint8 *= f;
    1969            0 :       else if (tid == TID_INT8)
    1970            0 :          m_int8 *= f;
    1971            0 :       else if (tid == TID_UINT16)
    1972            0 :          m_uint16 *= f;
    1973            0 :       else if (tid == TID_INT16)
    1974            0 :          m_int16 *= f;
    1975            0 :       else if (tid == TID_UINT32)
    1976            0 :          m_uint32 *= f;
    1977            0 :       else if (tid == TID_INT32)
    1978            0 :          m_int32 *= f;
    1979            0 :       else if (tid == TID_FLOAT)
    1980            0 :          m_float *= f;
    1981            0 :       else if (tid == TID_DOUBLE)
    1982            0 :          m_double *= f;
    1983              :       else
    1984            0 :          mthrow("Invalid operation for ODB key \"" +
    1985              :                 m_parent_odb->get_full_path() + "\"");
    1986            0 :       if (write && m_parent_odb->is_auto_refresh_write())
    1987            0 :          m_parent_odb->write();
    1988            0 :    }
    1989              : 
    1990              : }; // namespace midas
        

Generated by: LCOV version 2.0-1