285 my_input(std::move(input)),
286 my_nthreads(options.num_threads),
287 my_block_size(options.block_size)
289 sanisizer::as_size_type<std::vector<char> >(my_block_size);
295 std::size_t my_block_size;
300 template<
typename Input2_>
301 static bool chomp(Input2_& input) {
303 char x = input.get();
304 if (x !=
' ' && x !=
'\t' && x !=
'\r') {
307 if (!(input.advance())) {
314 template<
typename Input2_>
315 static bool advance_and_chomp(Input2_& input) {
318 if (!(input.advance())) {
324 template<
typename Input2_>
325 static bool skip_lines(Input2_& input,
LineIndex& current_line) {
328 char x = input.get();
331 if (!(input.advance())) {
334 }
while (input.get() !=
'\n');
335 }
else if (x !=
'\n') {
339 if (!input.advance()) {
348 bool my_passed_banner =
false;
350 struct ExpectedMatch {
351 ExpectedMatch(
bool found,
bool newline,
bool remaining) : found(found), newline(newline), remaining(remaining) {}
352 ExpectedMatch() : ExpectedMatch(false, false, false) {}
358 ExpectedMatch advance_past_expected_string() {
359 if (!(my_input->advance())) {
360 return ExpectedMatch(
true,
false,
false);
363 char next = my_input->get();
364 if (next ==
' ' || next ==
'\t' || next ==
'\r') {
365 if (!advance_and_chomp(*my_input)) {
366 return ExpectedMatch(
true,
false,
false);
368 if (my_input->get() ==
'\n') {
369 bool remaining = my_input->advance();
370 return ExpectedMatch(
true,
true, remaining);
372 return ExpectedMatch(
true,
false,
true);
374 }
else if (next ==
'\n') {
375 bool remaining = my_input->advance();
376 return ExpectedMatch(
true,
true, remaining);
380 return ExpectedMatch(
false,
true,
true);
383 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len, std::size_t start) {
389 for (std::size_t i = start; i < len; ++i) {
390 if (!my_input->advance()) {
391 return ExpectedMatch(
false,
false,
false);
393 if (my_input->get() != ptr[i]) {
394 return ExpectedMatch(
false,
false,
true);
397 return advance_past_expected_string();
400 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len) {
403 return is_expected_string(ptr, len, 1);
406 bool parse_banner_object() {
409 char x = my_input->get();
411 res = is_expected_string(
"matrix", 6);
412 my_details.
object = Object::MATRIX;
413 }
else if (x ==
'v') {
414 res = is_expected_string(
"vector", 6);
415 my_details.
object = Object::VECTOR;
419 throw std::runtime_error(
"first banner field should be one of 'matrix' or 'vector'");
421 if (!res.remaining) {
422 throw std::runtime_error(
"end of file reached after the first banner field");
428 bool parse_banner_format() {
431 char x = my_input->get();
433 res = is_expected_string(
"coordinate", 10);
434 my_details.
format = Format::COORDINATE;
435 }
else if (x ==
'a') {
436 res = is_expected_string(
"array", 5);
437 my_details.
format = Format::ARRAY;
441 throw std::runtime_error(
"second banner field should be one of 'coordinate' or 'array'");
443 if (!res.remaining) {
444 throw std::runtime_error(
"end of file reached after the second banner field");
450 bool parse_banner_field() {
453 char x = my_input->get();
455 res = is_expected_string(
"integer", 7);
456 my_details.
field = Field::INTEGER;
457 }
else if (x ==
'd') {
458 res = is_expected_string(
"double", 6);
459 my_details.
field = Field::DOUBLE;
460 }
else if (x ==
'c') {
461 res = is_expected_string(
"complex", 7);
462 my_details.
field = Field::COMPLEX;
463 }
else if (x ==
'p') {
464 res = is_expected_string(
"pattern", 7);
465 my_details.
field = Field::PATTERN;
466 }
else if (x ==
'r') {
467 res = is_expected_string(
"real", 4);
468 my_details.
field = Field::REAL;
472 throw std::runtime_error(
"third banner field should be one of 'real', 'integer', 'double', 'complex' or 'pattern'");
474 if (!res.remaining) {
475 throw std::runtime_error(
"end of file reached after the third banner field");
481 bool parse_banner_symmetry() {
484 char x = my_input->get();
486 res = is_expected_string(
"general", 7);
487 my_details.
symmetry = Symmetry::GENERAL;
488 }
else if (x ==
'h') {
489 res = is_expected_string(
"hermitian", 9);
490 my_details.
symmetry = Symmetry::HERMITIAN;
491 }
else if (x ==
's') {
492 if (my_input->advance()) {
493 char x = my_input->get();
495 res = is_expected_string(
"skew-symmetric", 14, 2);
496 my_details.
symmetry = Symmetry::SKEW_SYMMETRIC;
498 res = is_expected_string(
"symmetric", 9, 2);
499 my_details.
symmetry = Symmetry::SYMMETRIC;
505 throw std::runtime_error(
"fourth banner field should be one of 'general', 'hermitian', 'skew-symmetric' or 'symmetric'");
507 if (!res.remaining) {
508 throw std::runtime_error(
"end of file reached after the fourth banner field");
515 if (my_passed_banner) {
516 throw std::runtime_error(
"banner has already been scanned");
518 if (!(my_input->valid())) {
519 throw std::runtime_error(
"failed to find banner line before end of file");
521 if (my_input->get() !=
'%') {
522 throw std::runtime_error(
"first line of the file should be the banner");
525 auto found_banner = is_expected_string(
"%%MatrixMarket", 14);
526 if (!found_banner.remaining) {
527 throw std::runtime_error(
"end of file reached before matching the banner");
529 if (!found_banner.found) {
530 throw std::runtime_error(
"first line of the file should be the banner");
532 if (found_banner.newline) {
533 throw std::runtime_error(
"end of line reached before matching the banner");
536 if (parse_banner_object()) {
537 throw std::runtime_error(
"end of line reached after the first banner field");
539 if (parse_banner_format()) {
540 throw std::runtime_error(
"end of line reached after the second banner field");
544 if (my_details.
object == Object::MATRIX) {
545 if (parse_banner_field()) {
546 throw std::runtime_error(
"end of line reached after the third banner field");
548 eol = parse_banner_symmetry();
553 my_details.
symmetry = Symmetry::GENERAL;
556 eol = parse_banner_field();
559 my_passed_banner =
true;
566 if (!(my_input->advance())) {
567 throw std::runtime_error(
"end of file reached before the end of the banner line");
569 }
while (my_input->get() !=
'\n');
585 if (!my_passed_banner) {
586 throw std::runtime_error(
"banner has not yet been scanned");
594 template<
typename Integer_>
595 struct NotLastSizeInfo {
599 template<
typename Integer_>
600 struct LastSizeInfo {
602 bool remaining =
false;
605 template<
bool last_,
typename Integer_>
606 using SizeInfo =
typename std::conditional<last_, LastSizeInfo<Integer_>, NotLastSizeInfo<Integer_> >::type;
608 template<
bool last_,
typename Integer_,
class Input2_>
609 static SizeInfo<last_, Integer_> scan_integer_field(
bool size, Input2_& input,
LineIndex overall_line_count) {
610 SizeInfo<last_, Integer_> output;
613 auto what = [&]() -> std::string {
621 constexpr Integer_ max_limit = std::numeric_limits<Integer_>::max();
622 constexpr Integer_ max_limit_before_mult = max_limit / 10;
623 constexpr Integer_ max_limit_mod = max_limit % 10;
626 char x = input.get();
628 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
630 Integer_ delta = x -
'0';
632 if (output.index >= max_limit_before_mult && !(output.index == max_limit_before_mult && delta <= max_limit_mod)) {
633 throw std::runtime_error(
"integer overflow in " + what() +
" field on line " + std::to_string(overall_line_count + 1));
636 output.index += delta;
647 throw std::runtime_error(
"empty " + what() +
" field on line " + std::to_string(overall_line_count + 1));
649 if constexpr(last_) {
650 output.remaining = input.advance();
653 throw std::runtime_error(
"unexpected newline when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
654 case ' ':
case '\t':
case '\r':
655 if (!advance_and_chomp(input)) {
656 if constexpr(last_) {
659 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
662 if constexpr(last_) {
663 if (input.get() !=
'\n') {
664 throw std::runtime_error(
"expected newline after the last " + what() +
" field on line " + std::to_string(overall_line_count + 1));
666 output.remaining = input.advance();
670 throw std::runtime_error(
"unexpected character when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
673 if (!(input.advance())) {
674 if constexpr(last_) {
677 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
685 template<
bool last_,
class Input2_>
686 static SizeInfo<last_, Index_> scan_size_field(Input2_& input,
LineIndex overall_line_count) {
687 return scan_integer_field<last_, Index_>(
true, input, overall_line_count);
690 template<
class Input2_>
691 static SizeInfo<true, LineIndex> scan_line_count_field(Input2_& input,
LineIndex overall_line_count) {
692 return scan_integer_field<true, LineIndex>(
true, input, overall_line_count);
695 template<
bool last_,
class Input2_>
696 static SizeInfo<last_, Index_> scan_index_field(Input2_& input,
LineIndex overall_line_count) {
697 return scan_integer_field<last_, Index_>(
false, input, overall_line_count);
701 bool my_passed_size =
false;
702 Index_ my_nrows = 0, my_ncols = 0;
706 if (!(my_input->valid())) {
707 throw std::runtime_error(
"failed to find size line before end of file");
711 if (!skip_lines(*my_input, my_current_line)) {
712 throw std::runtime_error(
"failed to find size line before end of file");
714 if (!chomp(*my_input)) {
715 throw std::runtime_error(
"expected at least one size field on line " + std::to_string(my_current_line + 1));
718 if (my_details.
object == Object::MATRIX) {
719 if (my_details.
format == Format::COORDINATE) {
720 auto first_field = scan_size_field<false>(*my_input, my_current_line);
721 my_nrows = first_field.index;
723 auto second_field = scan_size_field<false>(*my_input, my_current_line);
724 my_ncols = second_field.index;
726 auto third_field = scan_line_count_field(*my_input, my_current_line);
727 my_nlines = third_field.index;
730 auto first_field = scan_size_field<false>(*my_input, my_current_line);
731 my_nrows = first_field.index;
733 auto second_field = scan_size_field<true>(*my_input, my_current_line);
734 my_ncols = second_field.index;
735 my_nlines = sanisizer::product<LineIndex>(my_nrows, my_ncols);
739 if (my_details.
format == Format::COORDINATE) {
740 auto first_field = scan_size_field<false>(*my_input, my_current_line);
741 my_nrows = first_field.index;
743 auto second_field = scan_line_count_field(*my_input, my_current_line);
744 my_nlines = second_field.index;
747 auto first_field = scan_size_field<true>(*my_input, my_current_line);
748 my_nlines = first_field.index;
749 my_nrows = my_nlines;
755 my_passed_size =
true;
767 if (!my_passed_size) {
768 throw std::runtime_error(
"size line has not yet been scanned");
781 if (!my_passed_size) {
782 throw std::runtime_error(
"size line has not yet been scanned");
795 if (!my_passed_size) {
796 throw std::runtime_error(
"size line has not yet been scanned");
813 template<
typename Type_>
815 ParseInfo() =
default;
816 ParseInfo(Type_ value,
bool remaining) : value(value), remaining(remaining) {}
821 template<
typename Workspace_>
822 bool configure_parallel_workspace(Workspace_& work) {
823 bool available = fill_to_next_newline(*my_input, work.buffer, my_block_size);
824 work.contents.clear();
825 work.overall_line = my_current_line;
826 my_current_line += count_newlines(work.buffer);
830 void check_num_lines_loop(
LineIndex data_line_count)
const {
831 if (data_line_count >= my_nlines) {
832 throw std::runtime_error(
"more lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
836 void check_num_lines_final(
bool finished,
LineIndex data_line_count)
const {
838 if (data_line_count != my_nlines) {
840 throw std::runtime_error(
"fewer lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
846 void check_matrix_coordinate_line(Index_ currow, Index_ curcol,
LineIndex overall_line_count)
const {
848 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
850 if (currow > my_nrows) {
851 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
854 throw std::runtime_error(
"column index must be positive on line " + std::to_string(overall_line_count + 1));
856 if (curcol > my_ncols) {
857 throw std::runtime_error(
"column index out of range on line " + std::to_string(overall_line_count + 1));
861 template<
typename Type_,
class Input2_,
typename FieldParser_,
class WrappedStore_>
862 bool scan_matrix_coordinate_non_pattern_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
863 bool valid = input.valid();
866 if (!skip_lines(input, overall_line_count)) {
870 throw std::runtime_error(
"expected at least three fields for a coordinate matrix on line " + std::to_string(overall_line_count + 1));
873 auto first_field = scan_index_field<false>(input, overall_line_count);
874 auto second_field = scan_index_field<false>(input, overall_line_count);
875 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
878 ParseInfo<Type_> res = fparser(input, overall_line_count);
879 if (!wstore(first_field.index, second_field.index, res.value)) {
882 ++overall_line_count;
883 valid = res.remaining;
889 template<
typename Type_,
class FieldParser_,
class Store_>
890 bool scan_matrix_coordinate_non_pattern(Store_ store) {
891 bool finished =
false;
894 if (my_nthreads == 1) {
895 FieldParser_ fparser;
896 finished = scan_matrix_coordinate_non_pattern_base<Type_>(
900 [&](Index_ r, Index_ c, Type_ value) ->
bool {
901 check_num_lines_loop(current_data_line);
903 return store(r, c, value);
909 std::vector<char> buffer;
910 FieldParser_ fparser;
911 std::vector<std::tuple<Index_, Index_, Type_> > contents;
915 ThreadPool<Workspace> tp(
916 [&](Workspace& work) ->
bool {
917 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
919 return scan_matrix_coordinate_non_pattern_base<Type_>(
923 [&](Index_ r, Index_ c, Type_ value) ->
bool {
924 work.contents.emplace_back(r, c, value);
933 [&](Workspace& work) ->
bool {
934 return configure_parallel_workspace(work);
936 [&](Workspace& work) ->
bool {
937 for (
const auto& con : work.contents) {
938 check_num_lines_loop(current_data_line);
939 if (!store(std::get<0>(con), std::get<1>(con), std::get<2>(con))) {
949 check_num_lines_final(finished, current_data_line);
954 template<
class Input2_,
class WrappedStore_>
955 bool scan_matrix_coordinate_pattern_base(Input2_& input,
LineIndex& overall_line_count, WrappedStore_ wstore)
const {
956 bool valid = input.valid();
959 if (!skip_lines(input, overall_line_count)) {
963 throw std::runtime_error(
"expected two fields for a pattern matrix on line " + std::to_string(overall_line_count + 1));
966 auto first_field = scan_index_field<false>(input, overall_line_count);
967 auto second_field = scan_index_field<true>(input, overall_line_count);
968 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
970 if (!wstore(first_field.index, second_field.index)) {
973 ++overall_line_count;
974 valid = second_field.remaining;
980 template<
class Store_>
981 bool scan_matrix_coordinate_pattern(Store_ store) {
982 bool finished =
false;
985 if (my_nthreads == 1) {
986 finished = scan_matrix_coordinate_pattern_base(
989 [&](Index_ r, Index_ c) ->
bool {
990 check_num_lines_loop(current_data_line);
998 std::vector<char> buffer;
999 std::vector<std::tuple<Index_, Index_> > contents;
1003 ThreadPool<Workspace> tp(
1004 [&](Workspace& work) ->
bool {
1005 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1007 return scan_matrix_coordinate_pattern_base(
1010 [&](Index_ r, Index_ c) ->
bool {
1011 work.contents.emplace_back(r, c);
1020 [&](Workspace& work) ->
bool {
1021 return configure_parallel_workspace(work);
1023 [&](Workspace& work) ->
bool {
1024 for (
const auto& con : work.contents) {
1025 check_num_lines_loop(current_data_line);
1026 if (!store(std::get<0>(con), std::get<1>(con))) {
1029 ++current_data_line;
1036 check_num_lines_final(finished, current_data_line);
1041 void check_vector_coordinate_line(Index_ currow,
LineIndex overall_line_count)
const {
1043 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
1045 if (currow > my_nrows) {
1046 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
1050 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1051 bool scan_vector_coordinate_non_pattern_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1052 bool valid = input.valid();
1055 if (!skip_lines(input, overall_line_count)) {
1058 if (!chomp(input)) {
1059 throw std::runtime_error(
"expected at least two fields for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1062 auto first_field = scan_index_field<false>(input, overall_line_count);
1063 check_vector_coordinate_line(first_field.index, overall_line_count);
1066 ParseInfo<Type_> res = fparser(input, overall_line_count);
1067 if (!wstore(first_field.index, res.value)) {
1070 ++overall_line_count;
1071 valid = res.remaining;
1077 template<
typename Type_,
class FieldParser_,
class Store_>
1078 bool scan_vector_coordinate_non_pattern(Store_ store) {
1079 bool finished =
false;
1082 if (my_nthreads == 1) {
1083 FieldParser_ fparser;
1084 finished = scan_vector_coordinate_non_pattern_base<Type_>(
1088 [&](Index_ r, Type_ value) ->
bool {
1089 check_num_lines_loop(current_data_line);
1090 ++current_data_line;
1091 return store(r, 1, value);
1097 std::vector<char> buffer;
1098 FieldParser_ fparser;
1099 std::vector<std::tuple<Index_, Type_> > contents;
1103 ThreadPool<Workspace> tp(
1104 [&](Workspace& work) ->
bool {
1105 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1107 return scan_vector_coordinate_non_pattern_base<Type_>(
1111 [&](Index_ r, Type_ value) ->
bool {
1112 work.contents.emplace_back(r, value);
1121 [&](Workspace& work) ->
bool {
1122 return configure_parallel_workspace(work);
1124 [&](Workspace& work) ->
bool {
1125 for (
const auto& con : work.contents) {
1126 check_num_lines_loop(current_data_line);
1127 if (!store(std::get<0>(con), 1, std::get<1>(con))) {
1130 ++current_data_line;
1137 check_num_lines_final(finished, current_data_line);
1142 template<
class Input2_,
class WrappedStore_>
1143 bool scan_vector_coordinate_pattern_base(Input2_& input,
LineIndex& overall_line_count, WrappedStore_ wstore)
const {
1144 bool valid = input.valid();
1147 if (!skip_lines(input, overall_line_count)) {
1150 if (!chomp(input)) {
1151 throw std::runtime_error(
"expected one field for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1154 auto first_field = scan_index_field<true>(input, overall_line_count);
1155 check_vector_coordinate_line(first_field.index, overall_line_count);
1157 if (!wstore(first_field.index)) {
1160 ++overall_line_count;
1161 valid = first_field.remaining;
1167 template<
class Store_>
1168 bool scan_vector_coordinate_pattern(Store_ store) {
1169 bool finished =
false;
1172 if (my_nthreads == 1) {
1173 finished = scan_vector_coordinate_pattern_base(
1176 [&](Index_ r) ->
bool {
1177 check_num_lines_loop(current_data_line);
1178 ++current_data_line;
1185 std::vector<char> buffer;
1186 std::vector<Index_> contents;
1190 ThreadPool<Workspace> tp(
1191 [&](Workspace& work) ->
bool {
1192 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1194 return scan_vector_coordinate_pattern_base(
1197 [&](Index_ r) ->
bool {
1198 work.contents.emplace_back(r);
1207 [&](Workspace& work) ->
bool {
1208 return configure_parallel_workspace(work);
1210 [&](Workspace& work) ->
bool {
1211 for (
const auto& r : work.contents) {
1212 check_num_lines_loop(current_data_line);
1216 ++current_data_line;
1223 check_num_lines_final(finished, current_data_line);
1228 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1229 bool scan_matrix_array_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1230 bool valid = input.valid();
1233 if (!skip_lines(input, overall_line_count)) {
1236 if (!chomp(input)) {
1237 throw std::runtime_error(
"expected at least one field for an array matrix on line " + std::to_string(overall_line_count + 1));
1241 ParseInfo<Type_> res = fparser(input, overall_line_count);
1242 if (!wstore(res.value)) {
1245 ++overall_line_count;
1246 valid = res.remaining;
1252 template<
typename Type_,
class FieldParser_,
class Store_>
1253 bool scan_matrix_array(Store_ store) {
1254 bool finished =
false;
1257 Index_ currow = 1, curcol = 1;
1258 auto increment = [&]() {
1260 if (currow > my_nrows) {
1266 if (my_nthreads == 1) {
1267 FieldParser_ fparser;
1268 finished = scan_matrix_array_base<Type_>(
1272 [&](Type_ value) ->
bool {
1273 check_num_lines_loop(current_data_line);
1274 if (!store(currow, curcol, value)) {
1277 ++current_data_line;
1285 std::vector<char> buffer;
1286 FieldParser_ fparser;
1287 std::vector<Type_> contents;
1291 ThreadPool<Workspace> tp(
1292 [&](Workspace& work) ->
bool {
1293 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1295 return scan_matrix_array_base<Type_>(
1299 [&](Type_ value) ->
bool {
1300 work.contents.emplace_back(value);
1309 [&](Workspace& work) ->
bool {
1310 return configure_parallel_workspace(work);
1312 [&](Workspace& work) ->
bool {
1313 for (
const auto& val : work.contents) {
1314 check_num_lines_loop(current_data_line);
1315 if (!store(currow, curcol, val)) {
1318 ++current_data_line;
1326 check_num_lines_final(finished, current_data_line);
1331 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1332 bool scan_vector_array_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1333 bool valid = input.valid();
1336 if (!skip_lines(input, overall_line_count)) {
1339 if (!chomp(input)) {
1340 throw std::runtime_error(
"expected at least one field for an array vector on line " + std::to_string(overall_line_count + 1));
1344 ParseInfo<Type_> res = fparser(input, overall_line_count);
1345 if (!wstore(res.value)) {
1348 ++overall_line_count;
1349 valid = res.remaining;
1355 template<
typename Type_,
class FieldParser_,
class Store_>
1356 bool scan_vector_array(Store_ store) {
1357 bool finished =
false;
1359 if (my_nthreads == 1) {
1360 FieldParser_ fparser;
1361 finished = scan_vector_array_base<Type_>(
1365 [&](Type_ value) ->
bool {
1366 check_num_lines_loop(current_data_line);
1367 ++current_data_line;
1368 return store(current_data_line, 1, value);
1374 std::vector<char> buffer;
1375 FieldParser_ fparser;
1376 std::vector<Type_> contents;
1380 ThreadPool<Workspace> tp(
1381 [&](Workspace& work) ->
bool {
1382 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1384 return scan_vector_array_base<Type_>(
1388 [&](Type_ value) ->
bool {
1389 work.contents.emplace_back(value);
1398 [&](Workspace& work) ->
bool {
1399 return configure_parallel_workspace(work);
1401 [&](Workspace& work) ->
bool {
1402 for (
const auto& val : work.contents) {
1403 check_num_lines_loop(current_data_line);
1404 ++current_data_line;
1405 if (!store(current_data_line, 1, val)) {
1414 check_num_lines_final(finished, current_data_line);
1419 void check_preamble()
const {
1420 if (!my_passed_banner || !my_passed_size) {
1421 throw std::runtime_error(
"banner or size lines have not yet been parsed");
1425 template<
typename Type_>
1426 class IntegerFieldParser {
1428 template<
class Input2_>
1429 ParseInfo<Type_> operator()(Input2_& input,
LineIndex overall_line_count) {
1430 char firstchar = input.get();
1431 bool negative = (firstchar ==
'-');
1432 if (negative || firstchar ==
'+') {
1433 if (!(input.advance())) {
1434 throw std::runtime_error(
"premature termination of an integer on line " + std::to_string(overall_line_count + 1));
1438 constexpr Type_ upper_limit = std::numeric_limits<Type_>::max();
1439 constexpr Type_ upper_limit_before_mult = upper_limit / 10;
1440 constexpr Type_ upper_limit_mod = upper_limit % 10;
1441 constexpr Type_ lower_limit = std::numeric_limits<Type_>::lowest();
1442 constexpr Type_ lower_limit_before_mult = lower_limit / 10;
1443 constexpr Type_ lower_limit_mod = -(lower_limit % 10);
1448 char x = input.get();
1450 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1452 Type_ delta = x -
'0';
1456 if (val <= lower_limit_before_mult && !(val == lower_limit_before_mult && delta <= lower_limit_mod)) {
1457 throw std::runtime_error(
"integer underflow on line " + std::to_string(overall_line_count + 1));
1462 if (val >= upper_limit_before_mult && !(val == upper_limit_before_mult && delta <= upper_limit_mod)) {
1463 throw std::runtime_error(
"integer overflow on line " + std::to_string(overall_line_count + 1));
1471 case ' ':
case '\t':
case '\r':
1472 if (!advance_and_chomp(input)) {
1473 return ParseInfo<Type_>(val,
false);
1475 if (input.get() !=
'\n') {
1476 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1478 return ParseInfo<Type_>(val, input.advance());
1486 throw std::runtime_error(
"empty integer field on line " + std::to_string(overall_line_count + 1));
1488 return ParseInfo<Type_>(val, input.advance());
1490 throw std::runtime_error(
"expected an integer value on line " + std::to_string(overall_line_count + 1));
1493 if (!(input.advance())) {
1498 return ParseInfo<Type_>(val,
false);
1516 template<
typename Type_ =
int,
class Store_>
1519 static_assert(std::is_integral<Type_>::value);
1521 auto wrapped_store = [&](Index_ r, Index_ c, Type_ val) ->
bool {
1522 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, Type_>::type,
bool>::value) {
1523 return store(r, c, val);
1530 if (my_details.
format == Format::COORDINATE) {
1531 if (my_details.
object == Object::MATRIX) {
1532 return scan_matrix_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1534 return scan_vector_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1537 if (my_details.
object == Object::MATRIX) {
1538 return scan_matrix_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1540 return scan_vector_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1546 template<
bool last_,
typename Type_,
typename Input2_>
1547 static ParseInfo<Type_> parse_real(Input2_& input, std::string& temporary, Index overall_line_count) {
1549 ParseInfo<Type_> output(0,
true);
1552 char x = input.get();
1555 if constexpr(last_) {
1559 if (temporary.empty()) {
1560 throw std::runtime_error(
"empty number field on line " + std::to_string(overall_line_count + 1));
1562 output.remaining = input.advance();
1564 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1566 goto final_processing;
1568 case ' ':
case '\t':
case '\r':
1569 if constexpr(last_) {
1570 if (!advance_and_chomp(input)) {
1571 output.remaining =
false;
1573 if (input.get() !=
'\n') {
1574 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1576 output.remaining = input.advance();
1579 if (!advance_and_chomp(input)) {
1580 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1582 if (input.get() ==
'\n') {
1583 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1586 goto final_processing;
1593 if (!(input.advance())) {
1594 if constexpr(last_) {
1595 output.remaining =
false;
1596 goto final_processing;
1598 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1603 if (temporary.size() >= 2 && temporary[0] ==
'0' && (temporary[1] ==
'x' || temporary[1] ==
'X')) {
1604 throw std::runtime_error(
"hexadecimal numbers are not allowed on line " + std::to_string(overall_line_count + 1));
1609 if constexpr(std::is_same<Type_, float>::value) {
1610 output.value = std::stof(temporary, &n);
1611 }
else if constexpr(std::is_same<Type_, long double>::value) {
1612 output.value = std::stold(temporary, &n);
1614 output.value = std::stod(temporary, &n);
1616 }
catch (std::invalid_argument& e) {
1617 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1620 if (n != temporary.size()) {
1621 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1626 template<
typename Type_>
1627 class RealFieldParser {
1629 template<
class Input2_>
1630 ParseInfo<Type_> operator()(Input2_& input,
LineIndex overall_line_count) {
1631 return parse_real<true, Type_>(input, my_temporary, overall_line_count);
1634 std::string my_temporary;
1651 template<
typename Type_ =
double,
class Store_>
1654 static_assert(std::is_floating_point<Type_>::value);
1656 auto store_real = [&](Index_ r, Index_ c, Type_ val) ->
bool {
1657 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, Type_>::type,
bool>::value) {
1658 return store(r, c, val);
1665 if (my_details.
format == Format::COORDINATE) {
1666 if (my_details.
object == Object::MATRIX) {
1667 return scan_matrix_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1669 return scan_vector_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1672 if (my_details.
object == Object::MATRIX) {
1673 return scan_matrix_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1675 return scan_vector_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1694 template<
typename Type_ =
double,
class Store_>
1700 template<
typename InnerType_>
1701 class ComplexFieldParser {
1703 template<
typename Input2_>
1704 ParseInfo<std::complex<InnerType_> > operator()(Input2_& input,
LineIndex overall_line_count) {
1705 auto first = parse_real<false, InnerType_>(input, my_temporary, overall_line_count);
1706 auto second = parse_real<true, InnerType_>(input, my_temporary, overall_line_count);
1707 ParseInfo<std::complex<InnerType_> > output;
1708 output.value.real(first.value);
1709 output.value.imag(second.value);
1710 output.remaining = second.remaining;
1714 std::string my_temporary;
1731 template<
typename Type_ =
double,
class Store_>
1734 static_assert(std::is_floating_point<Type_>::value);
1736 typedef std::complex<Type_> FullType;
1737 auto store_comp = [&](Index_ r, Index_ c, FullType val) ->
bool {
1738 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, FullType>::type,
bool>::value) {
1739 return store(r, c, val);
1746 if (my_details.
format == Format::COORDINATE) {
1747 if (my_details.
object == Object::MATRIX) {
1748 return scan_matrix_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1750 return scan_vector_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1753 if (my_details.
object == Object::MATRIX) {
1754 return scan_matrix_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1756 return scan_vector_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1776 template<
typename Type_ =
bool,
class Store_>
1779 if (my_details.
format != Format::COORDINATE) {
1780 throw std::runtime_error(
"'array' format for 'pattern' field is not supported");
1783 auto store_pat = [&](Index_ r, Index_ c) ->
bool {
1784 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, bool>::type,
bool>::value) {
1785 return store(r, c,
true);
1792 if (my_details.
object == Object::MATRIX) {
1793 return scan_matrix_coordinate_pattern(std::move(store_pat));
1795 return scan_vector_coordinate_pattern(std::move(store_pat));