/*
* Hdf5File.cpp, part of LatAnalyze 3
*
* Copyright (C) 2013 - 2015 Antonin Portelli, Matt Spraggs
*
* LatAnalyze 3 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LatAnalyze 3 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LatAnalyze 3. If not, see .
*/
#include
#include
#include
using namespace std;
using namespace Latan;
#ifndef H5_NO_NAMESPACE
using namespace H5NS;
#endif
constexpr unsigned int maxGroupNameSize = 1024u;
const short dMatType = static_cast(IoObject::IoType::dMat);
const short dMatSampleType = static_cast(IoObject::IoType::dMatSample);
const short rgStateType = static_cast(IoObject::IoType::rgState);
/******************************************************************************
* Hdf5File implementation *
******************************************************************************/
// constructors ////////////////////////////////////////////////////////////////
Hdf5File::Hdf5File(void)
{}
Hdf5File::Hdf5File(const std::string &name, const unsigned int mode)
{
open(name, mode);
}
// destructor //////////////////////////////////////////////////////////////////
Hdf5File::~Hdf5File(void)
{
close();
}
// access //////////////////////////////////////////////////////////////////////
void Hdf5File::save(const DMat &m, const string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
Group group;
Attribute attr;
DataSet dataset;
hsize_t dim[2] = {static_cast(m.rows()),
static_cast(m.cols())};
hsize_t attrDim = 1;
DataSpace dataSpace(2, dim), attrSpace(1, &attrDim);
group = h5File_->createGroup(name.c_str() + nameOffset(name));
attr = group.createAttribute("type", PredType::NATIVE_SHORT, attrSpace);
attr.write(PredType::NATIVE_SHORT, &dMatType);
dataset = group.createDataSet("data", PredType::NATIVE_DOUBLE, dataSpace);
dataset.write(m.data(), PredType::NATIVE_DOUBLE);
}
void Hdf5File::save(const DMatSample &sample, const string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
Group group;
Attribute attr;
DataSet dataset;
hsize_t dim[2] = {static_cast(sample[central].rows()),
static_cast(sample[central].cols())};
hsize_t attrDim = 1;
DataSpace dataSpace(2, dim), attrSpace(1, &attrDim);
const long int nSample = sample.size();
string datasetName;
group = h5File_->createGroup(name.c_str() + nameOffset(name));
attr = group.createAttribute("type", PredType::NATIVE_SHORT, attrSpace);
attr.write(PredType::NATIVE_SHORT, &dMatSampleType);
attr = group.createAttribute("nSample", PredType::NATIVE_LONG, attrSpace);
attr.write(PredType::NATIVE_LONG, &nSample);
FOR_STAT_ARRAY(sample, s)
{
datasetName = (s == central) ? "data_C" : ("data_S_" + strFrom(s));
dataset = group.createDataSet(datasetName.c_str(),
PredType::NATIVE_DOUBLE,
dataSpace);
dataset.write(sample[s].data(), PredType::NATIVE_DOUBLE);
}
}
void Hdf5File::save(const RandGenState &state, const string &name)
{
if (name.empty())
{
LATAN_ERROR(Io, "trying to save data with an empty name");
}
Group group;
Attribute attr;
DataSet dataset;
hsize_t dim = RLXG_STATE_SIZE;
hsize_t attrDim = 1;
DataSpace dataSpace(1, &dim), attrSpace(1, &attrDim);
group = h5File_->createGroup(name.c_str() + nameOffset(name));
attr = group.createAttribute("type", PredType::NATIVE_SHORT, attrSpace);
attr.write(PredType::NATIVE_SHORT, &rgStateType);
dataset = group.createDataSet("data", PredType::NATIVE_INT, dataSpace);
dataset.write(state.data(), PredType::NATIVE_INT);
}
// read first name ////////////////////////////////////////////////////////////
string Hdf5File::getFirstName(void)
{
return getFirstGroupName();
}
// tests ///////////////////////////////////////////////////////////////////////
bool Hdf5File::isOpen(void) const
{
return (h5File_ != nullptr);
}
// check names for forbidden characters ////////////////////////////////////////
size_t Hdf5File::nameOffset(const string &name)
{
size_t ret = 0;
string badChars = "/";
for (auto c : badChars)
{
size_t pos = name.rfind(c);
if (pos != string::npos and pos > ret)
{
ret = pos;
}
}
return ret;
}
// IO //////////////////////////////////////////////////////////////////////////
void Hdf5File::close(void)
{
if (isOpen())
{
h5File_->close();
}
h5File_.reset(nullptr);
name_ = "";
mode_ = Mode::null;
deleteData();
}
void Hdf5File::open(const string &name, const unsigned int mode)
{
if (isOpen())
{
LATAN_ERROR(Io, "file already opened with name '" + name_ + "'");
}
else
{
unsigned int h5Mode = 0;
name_ = name;
mode_ = mode;
if (mode & Mode::write)
{
h5Mode |= H5F_ACC_TRUNC;
}
if (mode & Mode::read)
{
h5Mode |= H5F_ACC_RDONLY;
}
if (mode & Mode::append)
{
h5Mode |= H5F_ACC_RDWR|H5F_ACC_CREAT;
}
h5File_.reset(new H5File(name_.c_str(), h5Mode));
}
}
string Hdf5File::getFirstGroupName(void)
{
string res;
if ((mode_ & Mode::read)&&(isOpen()))
{
auto firstGroupName = [](hid_t loc_id, const char *name, void *fname)
{
H5G_stat_t statbuf;
H5Gget_objinfo(loc_id, name, 0, &statbuf);
if ((statbuf.type == H5G_GROUP) && (strlen((char *)fname) == 0))
{
strncpy((char *)fname, name, maxGroupNameSize);
}
return 0;
};
char groupName[maxGroupNameSize] = "";
h5File_->iterateElems("/", nullptr, firstGroupName, groupName);
res = groupName;
}
else
{
if (isOpen())
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened in read mode");
}
else
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened");
}
return "";
}
return res;
}
void Hdf5File::load(DMat &m, const DataSet &d)
{
DataSpace dataspace;
hsize_t dim[2];
dataspace = d.getSpace();
dataspace.getSimpleExtentDims(dim);
m.resize(dim[0], dim[1]);
d.read(m.data(), PredType::NATIVE_DOUBLE);
}
void Hdf5File::load(RandGenState &state, const DataSet &d)
{
DataSpace dataspace;
hsize_t dim[1];
dataspace = d.getSpace();
dataspace.getSimpleExtentDims(dim);
if (dim[0] != RLXG_STATE_SIZE)
{
LATAN_ERROR(Io, "random generator state has a wrong length");
}
d.read(state.data(), PredType::NATIVE_INT);
}
string Hdf5File::load(const string &name)
{
if ((mode_ & Mode::read)&&(isOpen()))
{
string groupName;
Group group;
Attribute attribute;
DataSet dataset;
IoObject::IoType type;
groupName = (name.empty()) ? getFirstGroupName() : name;
if (groupName.empty())
{
LATAN_ERROR(Io, "file '" + name_ + "' is empty");
}
group = h5File_->openGroup(groupName.c_str());
attribute = group.openAttribute("type");
attribute.read(PredType::NATIVE_SHORT, &type);
switch (type)
{
case IoObject::IoType::dMat:
{
DMat *pt = new DMat;
data_[groupName].reset(pt);
dataset = group.openDataSet("data");
load(*pt, dataset);
break;
}
case IoObject::IoType::dMatSample:
{
DMatSample *pt = new DMatSample;
long int nSample;
data_[groupName].reset(pt);
attribute = group.openAttribute("nSample");
attribute.read(PredType::NATIVE_LONG, &nSample);
pt->resize(nSample);
FOR_STAT_ARRAY(*pt, s)
{
if (s == central)
{
dataset = group.openDataSet("data_C");
}
else
{
dataset =
group.openDataSet(("data_S_" + strFrom(s)).c_str());
}
load((*pt)[s], dataset);
}
break;
}
case IoObject::IoType::rgState:
{
RandGenState *pt = new RandGenState;
data_[groupName].reset(pt);
dataset = group.openDataSet("data");
load(*pt, dataset);
break;
}
default:
{
LATAN_ERROR(Io, "unknown data type ("
+ strFrom(static_cast(type)) + ") "
" (" + name_ + ":" + groupName + ")");
break;
}
}
return groupName;
}
else
{
if (isOpen())
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened in read mode");
}
else
{
LATAN_ERROR(Io, "file '" + name_ + "' is not opened");
}
return "";
}
}