Skip to content

Commit 236e1c2

Browse files
committed
handle more possible I/O errors
1 parent 4b15e72 commit 236e1c2

2 files changed

Lines changed: 57 additions & 14 deletions

File tree

mt-kahypar/io/hypergraph_io.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,16 @@ namespace mt_kahypar::io {
5656
bool& has_vertex_weights) {
5757
if (!is_line_ending(mapped_file, pos)) {
5858
// read the (up to) three 0/1 format digits
59-
uint32_t format_num = read_number<uint32_t>(mapped_file, pos, current_line, length, "format specifier");
60-
// TODO
61-
ASSERT(format_num < 100, "Vertex sizes in input file are not supported.");
62-
ASSERT(format_num / 10 == 0 || format_num / 10 == 1);
59+
uint32_t format_num = read_number<uint32_t>(mapped_file, pos, current_line, length, "weight type");
60+
if (format_num >= 100) {
61+
parsing_exception(current_line, "header has invalid weight type: " + STR(format_num) +
62+
" (should be one of 00/01/10/11; note that vertex sizes are not supported)");
63+
}
64+
bool format_is_valid = (format_num / 10 == 0 || format_num / 10 == 1) && (format_num % 10 == 0 || format_num % 10 == 1);
65+
if (!format_is_valid) {
66+
parsing_exception(current_line, "header has invalid weight type: " + STR(format_num) + " (should be one of 00/01/10/11)");
67+
}
6368
has_vertex_weights = (format_num / 10 == 1);
64-
ASSERT(format_num % 10 == 0 || format_num % 10 == 1);
6569
has_edge_weights = (format_num % 10 == 1);
6670
}
6771
}
@@ -108,6 +112,7 @@ namespace mt_kahypar::io {
108112
size_t& current_line,
109113
const size_t length,
110114
const HyperedgeID num_hyperedges,
115+
const HypernodeID num_hypernodes,
111116
const bool has_hyperedge_weights,
112117
HyperedgeVector& hyperedges,
113118
vec<HyperedgeWeight>& hyperedges_weight,
@@ -159,7 +164,12 @@ namespace mt_kahypar::io {
159164
Hyperedge hyperedge;
160165
while ( !is_line_ending(mapped_file, current_pos) ) {
161166
HypernodeID pin = read_number<HypernodeID>(mapped_file, current_pos, current_line, current_end, "vertex ID");
162-
ASSERT(pin > 0, V(current_hyperedge_id));
167+
if (pin == 0) {
168+
parsing_exception(current_line, "0 is not a valid vertex ID; the hMETIS file format requires that IDs start at 1");
169+
}
170+
if (pin > num_hypernodes) {
171+
parsing_exception(current_line, "invalid vertex ID: " + STR(pin) + " (larger than number of vertices)");
172+
}
163173
hyperedge.push_back(pin - 1);
164174
}
165175
do_line_ending(mapped_file, current_pos, current_line);
@@ -216,7 +226,9 @@ namespace mt_kahypar::io {
216226
if ( has_hypernode_weights ) {
217227
hypernodes_weight.resize(num_hypernodes);
218228
for ( HypernodeID hn = 0; hn < num_hypernodes; ++hn ) {
219-
ASSERT(pos > 0 && pos < length);
229+
if (pos >= length) {
230+
parsing_exception("found only " + STR(hn) + " hypernode weights, but there are " + STR(num_hypernodes) + " hypernodes");
231+
}
220232
ASSERT(mapped_file[pos - 1] == '\n');
221233
hypernodes_weight[hn] = read_number<HypernodeWeight>(mapped_file, pos, current_line, length, "vertex weight");
222234
do_line_ending(mapped_file, pos, current_line);
@@ -249,7 +261,7 @@ namespace mt_kahypar::io {
249261
// Read Hyperedges
250262
HighResClockTimepoint hyperedges_start = std::chrono::high_resolution_clock::now();
251263
HyperedgeReadResult res =
252-
readHyperedges(handle.mapped_file, pos, current_line, handle.length, num_hyperedges,
264+
readHyperedges(handle.mapped_file, pos, current_line, handle.length, num_hyperedges, num_hypernodes,
253265
has_hyperedge_weights, hyperedges, hyperedges_weight, remove_single_pin_hes);
254266
num_hyperedges -= res.num_removed_single_pin_hyperedges;
255267
num_removed_single_pin_hyperedges = res.num_removed_single_pin_hyperedges;
@@ -384,7 +396,12 @@ namespace mt_kahypar::io {
384396

385397
while ( !is_line_ending(mapped_file, current_pos) ) {
386398
const HypernodeID target = read_number<HypernodeID>(mapped_file, current_pos, current_line, current_end, "node ID");
387-
ASSERT(target > 0 && (target - 1) < num_vertices, V(target));
399+
if (target == 0) {
400+
parsing_exception(current_line, "0 is not a valid node ID; the METIS file format requires that IDs start at 1");
401+
}
402+
if (target > num_vertices) {
403+
parsing_exception(current_line, "invalid node ID: " + STR(target) + " (larger than number of nodes)");
404+
}
388405

389406
// process forward edges, ignore backward edges
390407
if ( current_vertex_id < (target - 1) ) {
@@ -407,7 +424,11 @@ namespace mt_kahypar::io {
407424
LOG << V(second_pass_time);
408425

409426
// finally, we copy the local edge lists to the global list
410-
copy_to_global_list(local_edges, edges);
427+
size_t actual_num_edges = copy_to_global_list(local_edges, edges);
428+
if (actual_num_edges != num_edges) {
429+
parsing_exception("number of edges in header (=" + STR(num_edges) +
430+
") does not match actual number of (forward) edges in file (=" + STR(actual_num_edges) + ")");
431+
}
411432
if ( has_edge_weights ) {
412433
copy_to_global_list(local_edges_weight, edges_weight);
413434
ASSERT(edges.size() == edges_weight.size());

mt-kahypar/io/io_utils.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,17 @@ void goto_next_line_unchecked(char* mapped_file, size_t& pos, size_t& current_li
209209
}
210210
}
211211

212+
template<typename ResultType>
212213
MT_KAHYPAR_ATTRIBUTE_ALWAYS_INLINE
213-
int64_t read_number(char* mapped_file, size_t& pos, const size_t current_line, const size_t length) {
214-
int64_t number = 0;
214+
ResultType read_number(char* mapped_file, size_t& pos, const size_t current_line, const size_t length, const char* expected_type) {
215215
while ( mapped_file[pos] == ' ' ) {
216216
++pos;
217217
}
218+
if ( is_line_ending(mapped_file, pos) ) {
219+
parsing_exception(current_line, std::string("expected to find ") + expected_type + ", but line already ends");
220+
}
221+
222+
ResultType number = 0;
218223
for ( ; pos < length; ++pos ) {
219224
if ( mapped_file[pos] == ' ' || is_line_ending(mapped_file, pos) ) {
220225
while ( mapped_file[pos] == ' ' ) {
@@ -225,6 +230,20 @@ int64_t read_number(char* mapped_file, size_t& pos, const size_t current_line, c
225230
if (mapped_file[pos] < '0' || mapped_file[pos] > '9') {
226231
parsing_exception(current_line, "invalid symbol: " + std::string(1, mapped_file[pos]));
227232
}
233+
234+
ResultType digit = mapped_file[pos] - '0';
235+
if (number > (std::numeric_limits<ResultType>::max() - digit) / 10) {
236+
// if the number would overflow, read the whole number and return a nice error message
237+
std::string number_as_string = STR(number);
238+
for ( ; mapped_file[pos] != ' ' && !is_line_ending(mapped_file, pos); ++pos ) {
239+
number_as_string += mapped_file[pos];
240+
}
241+
std::string msg = std::string("number overflows input range for ") + expected_type + ": " + number_as_string;
242+
if constexpr (sizeof(ResultType) < 8) {
243+
msg += " (build with -DKAHYPAR_USE_64_BIT_IDS=ON to support larger ID ranges)";
244+
}
245+
parsing_exception(current_line, msg);
246+
}
228247
number = number * 10 + (mapped_file[pos] - '0');
229248
}
230249
return number;
@@ -253,12 +272,14 @@ vec<LineRange> split_lines(char* mapped_file,
253272

254273
// find line endings very fast
255274
line_positions.emplace_back(pos, current_line);
256-
while ( line_positions.size() < expected_num_lines + 1 && pos < length ) {
275+
while ( true ) {
257276
// skip comments
258277
while ( mapped_file[pos] == '%' && pos < length ) {
259278
goto_next_line_unchecked(mapped_file, pos, current_line, length);
260279
}
261280

281+
if ( line_positions.size() > expected_num_lines || pos >= length ) break;
282+
262283
goto_next_line_unchecked(mapped_file, pos, current_line, length);
263284
line_positions.emplace_back(pos, current_line);
264285
}
@@ -298,7 +319,7 @@ vec<LineRange> split_lines(char* mapped_file,
298319
}
299320

300321
template<typename T>
301-
void copy_to_global_list(const vec<vec<T>>& source, vec<T>& target) {
322+
size_t copy_to_global_list(const vec<vec<T>>& source, vec<T>& target) {
302323
vec<size_t> prefix_sum;
303324
prefix_sum.reserve(source.size() + 1);
304325
prefix_sum.push_back(0);
@@ -316,6 +337,7 @@ void copy_to_global_list(const vec<vec<T>>& source, vec<T>& target) {
316337
target[start + j] = source[i][j];
317338
}
318339
});
340+
return prefix_sum.back();
319341
}
320342

321343
} // namespace io

0 commit comments

Comments
 (0)