iodump.cpp 7.35 KB
Newer Older
1
#include "iodump.h"
Lukas Weber's avatar
Lukas Weber committed
2
#include <filesystem>
3
#include <iostream>
4
5
#include <sstream>
#include <typeinfo>
6

7
8
namespace loadl {

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

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

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

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

	free(min_str);
	free(maj_str);

	return 0;
}

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

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

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

Lukas Weber's avatar
Lukas Weber committed
49
iodump::group::group(hid_t parent, const std::string &filename, const std::string &path)
50
    : filename_{filename} {
51
52
53
54
	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
55
			throw iodump_exception{filename_, "H5Gcreate"};
56
57
		}
	}
58
59
}

60
iodump::group::~group() {
61
	if(group_ < 0) {
62
		return;
63
	}
64
65
	herr_t status = H5Gclose(group_);
	if(status < 0) {
Lukas Weber's avatar
Lukas Weber committed
66
		std::cerr << iodump_exception{filename_, "H5Gclose"}.what();
67
68
69
70
71
72
		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
73
	return iodump::group::iterator{group_, filename_, 0};
74
75
76
}

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

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

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

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

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

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

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

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

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

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

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

132
iodump iodump::open_readwrite(const std::string &filename) {
133
	H5Eset_auto(H5E_DEFAULT, nullptr, nullptr);
134
	if(!std::filesystem::exists(filename)) {
135
136
137
		create(filename);
	}

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

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

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

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

160
161
162
163
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 {
164
165
166
	herr_t status;

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

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

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

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

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

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

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

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

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

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

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

	return size;
}
228
229
230
231
232

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

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

	return true;
}
241
242

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

249
iodump::h5_handle::h5_handle(h5_handle &&h) noexcept : closer_{h.closer_}, handle_{h.handle_} {
250
251
252
253
254
255
256
257
258
	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
259
		std::cerr << iodump_exception{"{undef}", "~h5_handle"}.what() << "\n";
260
261
262
263
264
265
266
		std::abort();
	}
}

hid_t iodump::h5_handle::operator*() {
	return handle_;
}
267
}