iodump.cpp 7.48 KB
Newer Older
1
#include "iodump.h"
2
#include <iostream>
3
#include <sstream>
4
#include <sys/file.h>
5 6
#include <typeinfo>
#include <unistd.h>
7

8 9
namespace loadl {

10 11
static bool filter_available(H5Z_filter_t filter) {
	htri_t avail = H5Zfilter_avail(filter);
12
	if(avail == 0) {
13
		return false;
14
	}
15 16 17

	unsigned int filter_info;
	herr_t status = H5Zget_filter_info(filter, &filter_info);
18 19
	return status >= 0 && (filter_info & H5Z_FILTER_CONFIG_ENCODE_ENABLED) != 0 &&
	       (filter_info & H5Z_FILTER_CONFIG_DECODE_ENABLED) != 0;
20 21
}

22
static herr_t H5Ewalk_cb(unsigned int n, const H5E_error2_t *err_desc, void *client_data) {
23
	std::stringstream &s = *reinterpret_cast<std::stringstream *>(client_data);
24 25 26

	char *min_str = H5Eget_minor(err_desc->min_num);
	char *maj_str = H5Eget_major(err_desc->maj_num);
27 28
	s << fmt::format("#{}: {}:{} in {}(): {}\n", n, err_desc->file_name, err_desc->line,
	                 err_desc->func_name, err_desc->desc);
29 30
	s << fmt::format("    {}: {}\n", static_cast<int32_t>(err_desc->maj_num), maj_str);
	s << fmt::format("    {}: {}\n\n", static_cast<int32_t>(err_desc->min_num), min_str);
31 32 33 34 35 36 37

	free(min_str);
	free(maj_str);

	return 0;
}

Lukas Weber's avatar
Lukas Weber committed
38
iodump_exception::iodump_exception(const std::string &filename, const std::string &message) {
39 40 41
	std::stringstream s;
	H5Ewalk(H5E_DEFAULT, H5E_WALK_DOWNWARD, H5Ewalk_cb, &s);

Lukas Weber's avatar
Lukas Weber committed
42
	s << "File " << filename << " Error triggered: " << message;
43 44 45 46 47
	message_ = s.str();
}

const char *iodump_exception::what() const noexcept {
	return message_.c_str();
48 49
}

Lukas Weber's avatar
Lukas Weber committed
50 51
iodump::group::group(hid_t parent, const std::string &filename, const std::string &path)
	: filename_{filename} {
52 53 54 55
	group_ = H5Gopen(parent, path.c_str(), H5P_DEFAULT);
	if(group_ < 0) {
		group_ = H5Gcreate2(parent, path.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
		if(group_ < 0) {
Lukas Weber's avatar
Lukas Weber committed
56
			throw iodump_exception{filename_, "H5Gcreate"};
57 58
		}
	}
59 60
}

61
iodump::group::~group() {
62
	if(group_ < 0) {
63
		return;
64
	}
65 66
	herr_t status = H5Gclose(group_);
	if(status < 0) {
Lukas Weber's avatar
Lukas Weber committed
67
		std::cerr << iodump_exception{filename_, "H5Gclose"}.what();
68 69 70 71 72 73
		std::cerr << group_ << " …something went wrong in the destructor.\n";
		assert(false);
	}
}

iodump::group::iterator iodump::group::begin() const {
Lukas Weber's avatar
Lukas Weber committed
74
	return iodump::group::iterator{group_, filename_, 0};
75 76 77
}

iodump::group::iterator iodump::group::end() const {
78
	H5G_info_t info{};
79
	herr_t status = H5Gget_info(group_, &info);
80
	if(status < 0) {
Lukas Weber's avatar
Lukas Weber committed
81
		throw iodump_exception{filename_, "H5Gget_info"};
82
	}
83

Lukas Weber's avatar
Lukas Weber committed
84
	return iodump::group::iterator{group_, filename_, info.nlinks};
85 86
}

87
std::string iodump::group::iterator::operator*() {
88
	ssize_t name_size =
Lukas Weber's avatar
Lukas Weber committed
89
	    H5Lget_name_by_idx(group_, ".", H5_INDEX_NAME, H5_ITER_INC, idx_, nullptr, 0, H5P_DEFAULT);
90
	if(name_size < 0) {
Lukas Weber's avatar
Lukas Weber committed
91
		throw iodump_exception{filename_, "H5Lget_name_by_idx"};
92
	}
93

94
	std::vector<char> buf(name_size + 1);
Lukas Weber's avatar
Lukas Weber committed
95
	name_size = H5Lget_name_by_idx(group_, ".", H5_INDEX_NAME, H5_ITER_INC, idx_, buf.data(),
96
	                               buf.size(), H5P_DEFAULT);
97
	if(name_size < 0) {
Lukas Weber's avatar
Lukas Weber committed
98
		throw iodump_exception{filename_, "H5Lget_name_by_idx"};
99
	}
100 101 102 103

	return std::string(buf.data());
}

104
iodump::group::iterator iodump::group::iterator::operator++() {
105 106 107
	idx_++;
	return *this;
}
108 109

bool iodump::group::iterator::operator!=(const iterator &b) {
110 111 112
	return idx_ != b.idx_;
}

113
iodump iodump::create(const std::string &filename) {
114
	H5Eset_auto(H5E_DEFAULT, nullptr, nullptr);
115 116
	hid_t file = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);

117
	if(file < 0) {
Lukas Weber's avatar
Lukas Weber committed
118
		throw iodump_exception{filename, "H5Fcreate"};
119
	}
120

121
	return iodump{filename, file};
122 123
}

124
iodump iodump::open_readonly(const std::string &filename) {
125
	H5Eset_auto(H5E_DEFAULT, nullptr, nullptr);
126
	hid_t file = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
127
	if(file < 0) {
Lukas Weber's avatar
Lukas Weber committed
128
		throw iodump_exception{filename, "H5Fopen"};
129 130
	}
	return iodump{filename, file};
131 132
}

133
iodump iodump::open_readwrite(const std::string &filename) {
134
	H5Eset_auto(H5E_DEFAULT, nullptr, nullptr);
135 136 137 138
	if(access(filename.c_str(), R_OK) != F_OK) {
		create(filename);
	}

139
	hid_t file = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
140
	if(file < 0) {
Lukas Weber's avatar
Lukas Weber committed
141
		throw iodump_exception{filename, "H5Fopen"};
142 143
	}
	return iodump{filename, file};
144 145
}

146 147
iodump::iodump(std::string filename, hid_t h5_file)
    : filename_{std::move(filename)}, h5_file_{h5_file} {
148
	if(compression_filter_ != 0 && !filter_available(compression_filter_)) {
Lukas Weber's avatar
Lukas Weber committed
149
		throw iodump_exception{filename_, "H5Filter not available."};
150
	}
151 152 153 154 155 156
}

iodump::~iodump() {
	H5Fclose(h5_file_);
}

157
iodump::group iodump::get_root() {
Lukas Weber's avatar
Lukas Weber committed
158
	return group{h5_file_, filename_, "/"};
159 160
}

161 162 163 164
iodump::h5_handle iodump::group::create_dataset(const std::string &name, hid_t datatype,
                                                hsize_t size, hsize_t chunk_size,
                                                H5Z_filter_t compression_filter,
                                                bool unlimited) const {
165 166 167
	herr_t status;

	if(exists(name)) {
168 169 170
		h5_handle dataset{H5Dopen2(group_, name.c_str(), H5P_DEFAULT), H5Dclose};
		h5_handle dataspace{H5Dget_space(*dataset), H5Sclose};

171
		hssize_t oldsize = H5Sget_simple_extent_npoints(*dataspace);
172 173

		if(oldsize < 0) {
Lukas Weber's avatar
Lukas Weber committed
174
			throw iodump_exception{filename_, "H5Sget_simple_extent_npoints"};
175
		}
176
		if(static_cast<hsize_t>(oldsize) != size) {
177 178
			throw std::runtime_error{
			    "iodump: tried to write into an existing dataset with different dimensions!"};
179 180 181
		}

		return dataset;
182
	} else {
183
		hid_t dataspace_h;
184
		if(!unlimited) {
185
			dataspace_h = H5Screate_simple(1, &size, nullptr);
186 187
		} else {
			hsize_t maxdim = H5S_UNLIMITED;
188
			dataspace_h = H5Screate_simple(1, &size, &maxdim);
189
		}
190
		h5_handle dataspace{dataspace_h, H5Sclose};
191

192
		h5_handle plist{H5Pcreate(H5P_DATASET_CREATE), H5Pclose};
193 194

		if(chunk_size > 1) { // do not use compression on small datasets
195 196
			status = H5Pset_chunk(*plist, 1, &chunk_size);
			if(status < 0) {
Lukas Weber's avatar
Lukas Weber committed
197
				throw iodump_exception{filename_, "H5Pset_chunk"};
198
			}
199

200
			if(compression_filter == H5Z_FILTER_DEFLATE) {
201 202
				status = H5Pset_deflate(*plist, 6);
				if(status < 0) {
Lukas Weber's avatar
Lukas Weber committed
203
					throw iodump_exception{filename_, "H5Pset_deflate"};
204
				}
205 206 207
			}
		}

208 209 210
		return h5_handle{H5Dcreate2(group_, name.c_str(), datatype, *dataspace, H5P_DEFAULT, *plist,
		                            H5P_DEFAULT),
		                 H5Dclose};
211 212
	}
}
213

214
iodump::group iodump::group::open_group(const std::string &path) const {
Lukas Weber's avatar
Lukas Weber committed
215
	return group{group_, filename_, path};
216
}
Stefan Weßel's avatar
ic  
Stefan Weßel committed
217

218
size_t iodump::group::get_extent(const std::string &name) const {
219 220
	h5_handle dataset{H5Dopen2(group_, name.c_str(), H5P_DEFAULT), H5Dclose};
	h5_handle dataspace{H5Dget_space(*dataset), H5Sclose};
221

222 223
	int size = H5Sget_simple_extent_npoints(*dataspace); // rank > 1 will get flattened when loaded.
	if(size < 0) {
Lukas Weber's avatar
Lukas Weber committed
224
		throw iodump_exception{filename_, "H5Sget_simple_extent_npoints"};
225
	}
226 227 228

	return size;
}
229 230 231 232 233

bool iodump::group::exists(const std::string &path) const {
	htri_t exists = H5Lexists(group_, path.c_str(), H5P_DEFAULT);
	if(exists == 0) {
		return false;
234 235 236
	}

	if(exists < 0) {
Lukas Weber's avatar
Lukas Weber committed
237
		throw iodump_exception{filename_, "H5Lexists"};
238 239 240 241
	}

	return true;
}
242 243

iodump::h5_handle::h5_handle(hid_t handle, herr_t (*closer)(hid_t))
244
    : closer_{closer}, handle_{handle} {
245
	if(handle < 0) {
Lukas Weber's avatar
Lukas Weber committed
246
		throw iodump_exception{"{undef}", "h5_handle"};
247 248 249
	}
}

250
iodump::h5_handle::h5_handle(h5_handle &&h) noexcept : closer_{h.closer_}, handle_{h.handle_} {
251 252 253 254 255 256 257 258 259
	h.handle_ = -1;
}

iodump::h5_handle::~h5_handle() {
	if(handle_ < 0) {
		return;
	}
	herr_t status = closer_(handle_);
	if(status < 0) {
Lukas Weber's avatar
Lukas Weber committed
260
		std::cerr << iodump_exception{"{undef}", "~h5_handle"}.what() << "\n";
261 262 263 264 265 266 267
		std::abort();
	}
}

hid_t iodump::h5_handle::operator*() {
	return handle_;
}
268 269 270 271 272

bool file_exists(const std::string &path) {
	struct stat buf;
	return stat(path.c_str(), &buf) == 0;
}
273
}