282 my_input(std::move(input)),
283 my_nthreads(options.num_threads),
284 my_block_size(options.block_size)
286 sanisizer::cast<typename std::vector<char>::size_type>(my_block_size);
292 std::size_t my_block_size;
297 template<
typename Input2_>
298 static bool chomp(Input2_& input) {
300 char x = input.get();
301 if (x !=
' ' && x !=
'\t' && x !=
'\r') {
304 if (!(input.advance())) {
311 template<
typename Input2_>
312 static bool advance_and_chomp(Input2_& input) {
315 if (!(input.advance())) {
321 template<
typename Input2_>
322 static bool skip_lines(Input2_& input,
LineIndex& current_line) {
325 char x = input.get();
328 if (!(input.advance())) {
331 }
while (input.get() !=
'\n');
332 }
else if (x !=
'\n') {
336 if (!input.advance()) {
345 bool my_passed_banner =
false;
347 struct ExpectedMatch {
348 ExpectedMatch(
bool found,
bool newline,
bool remaining) : found(found), newline(newline), remaining(remaining) {}
349 ExpectedMatch() : ExpectedMatch(false, false, false) {}
355 ExpectedMatch advance_past_expected_string() {
356 if (!(my_input->advance())) {
357 return ExpectedMatch(
true,
false,
false);
360 char next = my_input->get();
361 if (next ==
' ' || next ==
'\t' || next ==
'\r') {
362 if (!advance_and_chomp(*my_input)) {
363 return ExpectedMatch(
true,
false,
false);
365 if (my_input->get() ==
'\n') {
366 bool remaining = my_input->advance();
367 return ExpectedMatch(
true,
true, remaining);
369 return ExpectedMatch(
true,
false,
true);
371 }
else if (next ==
'\n') {
372 bool remaining = my_input->advance();
373 return ExpectedMatch(
true,
true, remaining);
377 return ExpectedMatch(
false,
true,
true);
380 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len, std::size_t start) {
386 for (std::size_t i = start; i < len; ++i) {
387 if (!my_input->advance()) {
388 return ExpectedMatch(
false,
false,
false);
390 if (my_input->get() != ptr[i]) {
391 return ExpectedMatch(
false,
false,
true);
394 return advance_past_expected_string();
397 ExpectedMatch is_expected_string(
const char* ptr, std::size_t len) {
400 return is_expected_string(ptr, len, 1);
403 bool parse_banner_object() {
406 char x = my_input->get();
408 res = is_expected_string(
"matrix", 6);
409 my_details.
object = Object::MATRIX;
410 }
else if (x ==
'v') {
411 res = is_expected_string(
"vector", 6);
412 my_details.
object = Object::VECTOR;
416 throw std::runtime_error(
"first banner field should be one of 'matrix' or 'vector'");
418 if (!res.remaining) {
419 throw std::runtime_error(
"end of file reached after the first banner field");
425 bool parse_banner_format() {
428 char x = my_input->get();
430 res = is_expected_string(
"coordinate", 10);
431 my_details.
format = Format::COORDINATE;
432 }
else if (x ==
'a') {
433 res = is_expected_string(
"array", 5);
434 my_details.
format = Format::ARRAY;
438 throw std::runtime_error(
"second banner field should be one of 'coordinate' or 'array'");
440 if (!res.remaining) {
441 throw std::runtime_error(
"end of file reached after the second banner field");
447 bool parse_banner_field() {
450 char x = my_input->get();
452 res = is_expected_string(
"integer", 7);
453 my_details.
field = Field::INTEGER;
454 }
else if (x ==
'd') {
455 res = is_expected_string(
"double", 6);
456 my_details.
field = Field::DOUBLE;
457 }
else if (x ==
'c') {
458 res = is_expected_string(
"complex", 7);
459 my_details.
field = Field::COMPLEX;
460 }
else if (x ==
'p') {
461 res = is_expected_string(
"pattern", 7);
462 my_details.
field = Field::PATTERN;
463 }
else if (x ==
'r') {
464 res = is_expected_string(
"real", 4);
465 my_details.
field = Field::REAL;
469 throw std::runtime_error(
"third banner field should be one of 'real', 'integer', 'double', 'complex' or 'pattern'");
471 if (!res.remaining) {
472 throw std::runtime_error(
"end of file reached after the third banner field");
478 bool parse_banner_symmetry() {
481 char x = my_input->get();
483 res = is_expected_string(
"general", 7);
484 my_details.
symmetry = Symmetry::GENERAL;
485 }
else if (x ==
'h') {
486 res = is_expected_string(
"hermitian", 9);
487 my_details.
symmetry = Symmetry::HERMITIAN;
488 }
else if (x ==
's') {
489 if (my_input->advance()) {
490 char x = my_input->get();
492 res = is_expected_string(
"skew-symmetric", 14, 2);
493 my_details.
symmetry = Symmetry::SKEW_SYMMETRIC;
495 res = is_expected_string(
"symmetric", 9, 2);
496 my_details.
symmetry = Symmetry::SYMMETRIC;
502 throw std::runtime_error(
"fourth banner field should be one of 'general', 'hermitian', 'skew-symmetric' or 'symmetric'");
504 if (!res.remaining) {
505 throw std::runtime_error(
"end of file reached after the fourth banner field");
512 if (my_passed_banner) {
513 throw std::runtime_error(
"banner has already been scanned");
515 if (!(my_input->valid())) {
516 throw std::runtime_error(
"failed to find banner line before end of file");
518 if (my_input->get() !=
'%') {
519 throw std::runtime_error(
"first line of the file should be the banner");
522 auto found_banner = is_expected_string(
"%%MatrixMarket", 14);
523 if (!found_banner.remaining) {
524 throw std::runtime_error(
"end of file reached before matching the banner");
526 if (!found_banner.found) {
527 throw std::runtime_error(
"first line of the file should be the banner");
529 if (found_banner.newline) {
530 throw std::runtime_error(
"end of line reached before matching the banner");
533 if (parse_banner_object()) {
534 throw std::runtime_error(
"end of line reached after the first banner field");
536 if (parse_banner_format()) {
537 throw std::runtime_error(
"end of line reached after the second banner field");
541 if (my_details.
object == Object::MATRIX) {
542 if (parse_banner_field()) {
543 throw std::runtime_error(
"end of line reached after the third banner field");
545 eol = parse_banner_symmetry();
550 my_details.
symmetry = Symmetry::GENERAL;
553 eol = parse_banner_field();
556 my_passed_banner =
true;
563 if (!(my_input->advance())) {
564 throw std::runtime_error(
"end of file reached before the end of the banner line");
566 }
while (my_input->get() !=
'\n');
582 if (!my_passed_banner) {
583 throw std::runtime_error(
"banner has not yet been scanned");
591 template<
typename Integer_>
592 struct NotLastSizeInfo {
596 template<
typename Integer_>
597 struct LastSizeInfo {
599 bool remaining =
false;
602 template<
bool last_,
typename Integer_>
603 using SizeInfo =
typename std::conditional<last_, LastSizeInfo<Integer_>, NotLastSizeInfo<Integer_> >::type;
605 template<
bool last_,
typename Integer_,
class Input2_>
606 static SizeInfo<last_, Integer_> scan_integer_field(
bool size, Input2_& input,
LineIndex overall_line_count) {
607 SizeInfo<last_, Integer_> output;
610 auto what = [&]() -> std::string {
618 constexpr Integer_ max_limit = std::numeric_limits<Integer_>::max();
619 constexpr Integer_ max_limit_before_mult = max_limit / 10;
620 constexpr Integer_ max_limit_mod = max_limit % 10;
623 char x = input.get();
625 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
627 Integer_ delta = x -
'0';
629 if (output.index >= max_limit_before_mult && !(output.index == max_limit_before_mult && delta <= max_limit_mod)) {
630 throw std::runtime_error(
"integer overflow in " + what() +
" field on line " + std::to_string(overall_line_count + 1));
633 output.index += delta;
644 throw std::runtime_error(
"empty " + what() +
" field on line " + std::to_string(overall_line_count + 1));
646 if constexpr(last_) {
647 output.remaining = input.advance();
650 throw std::runtime_error(
"unexpected newline when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
651 case ' ':
case '\t':
case '\r':
652 if (!advance_and_chomp(input)) {
653 if constexpr(last_) {
656 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
659 if constexpr(last_) {
660 if (input.get() !=
'\n') {
661 throw std::runtime_error(
"expected newline after the last " + what() +
" field on line " + std::to_string(overall_line_count + 1));
663 output.remaining = input.advance();
667 throw std::runtime_error(
"unexpected character when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
670 if (!(input.advance())) {
671 if constexpr(last_) {
674 throw std::runtime_error(
"unexpected end of file when parsing " + what() +
" field on line " + std::to_string(overall_line_count + 1));
682 template<
bool last_,
class Input2_>
683 static SizeInfo<last_, Index_> scan_size_field(Input2_& input,
LineIndex overall_line_count) {
684 return scan_integer_field<last_, Index_>(
true, input, overall_line_count);
687 template<
class Input2_>
688 static SizeInfo<true, LineIndex> scan_line_count_field(Input2_& input,
LineIndex overall_line_count) {
689 return scan_integer_field<true, LineIndex>(
true, input, overall_line_count);
692 template<
bool last_,
class Input2_>
693 static SizeInfo<last_, Index_> scan_index_field(Input2_& input,
LineIndex overall_line_count) {
694 return scan_integer_field<last_, Index_>(
false, input, overall_line_count);
698 bool my_passed_size =
false;
699 Index_ my_nrows = 0, my_ncols = 0;
703 if (!(my_input->valid())) {
704 throw std::runtime_error(
"failed to find size line before end of file");
708 if (!skip_lines(*my_input, my_current_line)) {
709 throw std::runtime_error(
"failed to find size line before end of file");
711 if (!chomp(*my_input)) {
712 throw std::runtime_error(
"expected at least one size field on line " + std::to_string(my_current_line + 1));
715 if (my_details.
object == Object::MATRIX) {
716 if (my_details.
format == Format::COORDINATE) {
717 auto first_field = scan_size_field<false>(*my_input, my_current_line);
718 my_nrows = first_field.index;
720 auto second_field = scan_size_field<false>(*my_input, my_current_line);
721 my_ncols = second_field.index;
723 auto third_field = scan_line_count_field(*my_input, my_current_line);
724 my_nlines = third_field.index;
727 auto first_field = scan_size_field<false>(*my_input, my_current_line);
728 my_nrows = first_field.index;
730 auto second_field = scan_size_field<true>(*my_input, my_current_line);
731 my_ncols = second_field.index;
732 my_nlines = sanisizer::product<LineIndex>(my_nrows, my_ncols);
736 if (my_details.
format == Format::COORDINATE) {
737 auto first_field = scan_size_field<false>(*my_input, my_current_line);
738 my_nrows = first_field.index;
740 auto second_field = scan_line_count_field(*my_input, my_current_line);
741 my_nlines = second_field.index;
744 auto first_field = scan_size_field<true>(*my_input, my_current_line);
745 my_nlines = first_field.index;
746 my_nrows = my_nlines;
752 my_passed_size =
true;
764 if (!my_passed_size) {
765 throw std::runtime_error(
"size line has not yet been scanned");
778 if (!my_passed_size) {
779 throw std::runtime_error(
"size line has not yet been scanned");
792 if (!my_passed_size) {
793 throw std::runtime_error(
"size line has not yet been scanned");
810 template<
typename Type_>
812 ParseInfo() =
default;
813 ParseInfo(Type_ value,
bool remaining) : value(value), remaining(remaining) {}
818 template<
typename Workspace_>
819 bool configure_parallel_workspace(Workspace_& work) {
820 bool available = fill_to_next_newline(*my_input, work.buffer, my_block_size);
821 work.contents.clear();
822 work.overall_line = my_current_line;
823 my_current_line += count_newlines(work.buffer);
827 void check_num_lines_loop(
LineIndex data_line_count)
const {
828 if (data_line_count >= my_nlines) {
829 throw std::runtime_error(
"more lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
833 void check_num_lines_final(
bool finished,
LineIndex data_line_count)
const {
835 if (data_line_count != my_nlines) {
837 throw std::runtime_error(
"fewer lines present than specified in the header (" + std::to_string(data_line_count) +
" versus " + std::to_string(my_nlines) +
")");
843 void check_matrix_coordinate_line(Index_ currow, Index_ curcol,
LineIndex overall_line_count)
const {
845 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
847 if (currow > my_nrows) {
848 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
851 throw std::runtime_error(
"column index must be positive on line " + std::to_string(overall_line_count + 1));
853 if (curcol > my_ncols) {
854 throw std::runtime_error(
"column index out of range on line " + std::to_string(overall_line_count + 1));
858 template<
typename Type_,
class Input2_,
typename FieldParser_,
class WrappedStore_>
859 bool scan_matrix_coordinate_non_pattern_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
860 bool valid = input.valid();
863 if (!skip_lines(input, overall_line_count)) {
867 throw std::runtime_error(
"expected at least three fields for a coordinate matrix on line " + std::to_string(overall_line_count + 1));
870 auto first_field = scan_index_field<false>(input, overall_line_count);
871 auto second_field = scan_index_field<false>(input, overall_line_count);
872 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
875 ParseInfo<Type_> res = fparser(input, overall_line_count);
876 if (!wstore(first_field.index, second_field.index, res.value)) {
879 ++overall_line_count;
880 valid = res.remaining;
886 template<
typename Type_,
class FieldParser_,
class Store_>
887 bool scan_matrix_coordinate_non_pattern(Store_ store) {
888 bool finished =
false;
891 if (my_nthreads == 1) {
892 FieldParser_ fparser;
893 finished = scan_matrix_coordinate_non_pattern_base<Type_>(
897 [&](Index_ r, Index_ c, Type_ value) ->
bool {
898 check_num_lines_loop(current_data_line);
900 return store(r, c, value);
906 std::vector<char> buffer;
907 FieldParser_ fparser;
908 std::vector<std::tuple<Index_, Index_, Type_> > contents;
912 ThreadPool<Workspace> tp(
913 [&](Workspace& work) ->
bool {
914 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
916 return scan_matrix_coordinate_non_pattern_base<Type_>(
920 [&](Index_ r, Index_ c, Type_ value) ->
bool {
921 work.contents.emplace_back(r, c, value);
930 [&](Workspace& work) ->
bool {
931 return configure_parallel_workspace(work);
933 [&](Workspace& work) ->
bool {
934 for (
const auto& con : work.contents) {
935 check_num_lines_loop(current_data_line);
936 if (!store(std::get<0>(con), std::get<1>(con), std::get<2>(con))) {
946 check_num_lines_final(finished, current_data_line);
951 template<
class Input2_,
class WrappedStore_>
952 bool scan_matrix_coordinate_pattern_base(Input2_& input,
LineIndex& overall_line_count, WrappedStore_ wstore)
const {
953 bool valid = input.valid();
956 if (!skip_lines(input, overall_line_count)) {
960 throw std::runtime_error(
"expected two fields for a pattern matrix on line " + std::to_string(overall_line_count + 1));
963 auto first_field = scan_index_field<false>(input, overall_line_count);
964 auto second_field = scan_index_field<true>(input, overall_line_count);
965 check_matrix_coordinate_line(first_field.index, second_field.index, overall_line_count);
967 if (!wstore(first_field.index, second_field.index)) {
970 ++overall_line_count;
971 valid = second_field.remaining;
977 template<
class Store_>
978 bool scan_matrix_coordinate_pattern(Store_ store) {
979 bool finished =
false;
982 if (my_nthreads == 1) {
983 finished = scan_matrix_coordinate_pattern_base(
986 [&](Index_ r, Index_ c) ->
bool {
987 check_num_lines_loop(current_data_line);
995 std::vector<char> buffer;
996 std::vector<std::tuple<Index_, Index_> > contents;
1000 ThreadPool<Workspace> tp(
1001 [&](Workspace& work) ->
bool {
1002 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1004 return scan_matrix_coordinate_pattern_base(
1007 [&](Index_ r, Index_ c) ->
bool {
1008 work.contents.emplace_back(r, c);
1017 [&](Workspace& work) ->
bool {
1018 return configure_parallel_workspace(work);
1020 [&](Workspace& work) ->
bool {
1021 for (
const auto& con : work.contents) {
1022 check_num_lines_loop(current_data_line);
1023 if (!store(std::get<0>(con), std::get<1>(con))) {
1026 ++current_data_line;
1033 check_num_lines_final(finished, current_data_line);
1038 void check_vector_coordinate_line(Index_ currow,
LineIndex overall_line_count)
const {
1040 throw std::runtime_error(
"row index must be positive on line " + std::to_string(overall_line_count + 1));
1042 if (currow > my_nrows) {
1043 throw std::runtime_error(
"row index out of range on line " + std::to_string(overall_line_count + 1));
1047 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1048 bool scan_vector_coordinate_non_pattern_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1049 bool valid = input.valid();
1052 if (!skip_lines(input, overall_line_count)) {
1055 if (!chomp(input)) {
1056 throw std::runtime_error(
"expected at least two fields for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1059 auto first_field = scan_index_field<false>(input, overall_line_count);
1060 check_vector_coordinate_line(first_field.index, overall_line_count);
1063 ParseInfo<Type_> res = fparser(input, overall_line_count);
1064 if (!wstore(first_field.index, res.value)) {
1067 ++overall_line_count;
1068 valid = res.remaining;
1074 template<
typename Type_,
class FieldParser_,
class Store_>
1075 bool scan_vector_coordinate_non_pattern(Store_ store) {
1076 bool finished =
false;
1079 if (my_nthreads == 1) {
1080 FieldParser_ fparser;
1081 finished = scan_vector_coordinate_non_pattern_base<Type_>(
1085 [&](Index_ r, Type_ value) ->
bool {
1086 check_num_lines_loop(current_data_line);
1087 ++current_data_line;
1088 return store(r, 1, value);
1094 std::vector<char> buffer;
1095 FieldParser_ fparser;
1096 std::vector<std::tuple<Index_, Type_> > contents;
1100 ThreadPool<Workspace> tp(
1101 [&](Workspace& work) ->
bool {
1102 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1104 return scan_vector_coordinate_non_pattern_base<Type_>(
1108 [&](Index_ r, Type_ value) ->
bool {
1109 work.contents.emplace_back(r, value);
1118 [&](Workspace& work) ->
bool {
1119 return configure_parallel_workspace(work);
1121 [&](Workspace& work) ->
bool {
1122 for (
const auto& con : work.contents) {
1123 check_num_lines_loop(current_data_line);
1124 if (!store(std::get<0>(con), 1, std::get<1>(con))) {
1127 ++current_data_line;
1134 check_num_lines_final(finished, current_data_line);
1139 template<
class Input2_,
class WrappedStore_>
1140 bool scan_vector_coordinate_pattern_base(Input2_& input,
LineIndex& overall_line_count, WrappedStore_ wstore)
const {
1141 bool valid = input.valid();
1144 if (!skip_lines(input, overall_line_count)) {
1147 if (!chomp(input)) {
1148 throw std::runtime_error(
"expected one field for a coordinate vector on line " + std::to_string(overall_line_count + 1));
1151 auto first_field = scan_index_field<true>(input, overall_line_count);
1152 check_vector_coordinate_line(first_field.index, overall_line_count);
1154 if (!wstore(first_field.index)) {
1157 ++overall_line_count;
1158 valid = first_field.remaining;
1164 template<
class Store_>
1165 bool scan_vector_coordinate_pattern(Store_ store) {
1166 bool finished =
false;
1169 if (my_nthreads == 1) {
1170 finished = scan_vector_coordinate_pattern_base(
1173 [&](Index_ r) ->
bool {
1174 check_num_lines_loop(current_data_line);
1175 ++current_data_line;
1182 std::vector<char> buffer;
1183 std::vector<Index_> contents;
1187 ThreadPool<Workspace> tp(
1188 [&](Workspace& work) ->
bool {
1189 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1191 return scan_vector_coordinate_pattern_base(
1194 [&](Index_ r) ->
bool {
1195 work.contents.emplace_back(r);
1204 [&](Workspace& work) ->
bool {
1205 return configure_parallel_workspace(work);
1207 [&](Workspace& work) ->
bool {
1208 for (
const auto& r : work.contents) {
1209 check_num_lines_loop(current_data_line);
1213 ++current_data_line;
1220 check_num_lines_final(finished, current_data_line);
1225 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1226 bool scan_matrix_array_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1227 bool valid = input.valid();
1230 if (!skip_lines(input, overall_line_count)) {
1233 if (!chomp(input)) {
1234 throw std::runtime_error(
"expected at least one field for an array matrix on line " + std::to_string(overall_line_count + 1));
1238 ParseInfo<Type_> res = fparser(input, overall_line_count);
1239 if (!wstore(res.value)) {
1242 ++overall_line_count;
1243 valid = res.remaining;
1249 template<
typename Type_,
class FieldParser_,
class Store_>
1250 bool scan_matrix_array(Store_ store) {
1251 bool finished =
false;
1254 Index_ currow = 1, curcol = 1;
1255 auto increment = [&]() {
1257 if (currow > my_nrows) {
1263 if (my_nthreads == 1) {
1264 FieldParser_ fparser;
1265 finished = scan_matrix_array_base<Type_>(
1269 [&](Type_ value) ->
bool {
1270 check_num_lines_loop(current_data_line);
1271 if (!store(currow, curcol, value)) {
1274 ++current_data_line;
1282 std::vector<char> buffer;
1283 FieldParser_ fparser;
1284 std::vector<Type_> contents;
1288 ThreadPool<Workspace> tp(
1289 [&](Workspace& work) ->
bool {
1290 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1292 return scan_matrix_array_base<Type_>(
1296 [&](Type_ value) ->
bool {
1297 work.contents.emplace_back(value);
1306 [&](Workspace& work) ->
bool {
1307 return configure_parallel_workspace(work);
1309 [&](Workspace& work) ->
bool {
1310 for (
const auto& val : work.contents) {
1311 check_num_lines_loop(current_data_line);
1312 if (!store(currow, curcol, val)) {
1315 ++current_data_line;
1323 check_num_lines_final(finished, current_data_line);
1328 template<
typename Type_,
class Input2_,
class FieldParser_,
class WrappedStore_>
1329 bool scan_vector_array_base(Input2_& input,
LineIndex& overall_line_count, FieldParser_& fparser, WrappedStore_ wstore)
const {
1330 bool valid = input.valid();
1333 if (!skip_lines(input, overall_line_count)) {
1336 if (!chomp(input)) {
1337 throw std::runtime_error(
"expected at least one field for an array vector on line " + std::to_string(overall_line_count + 1));
1341 ParseInfo<Type_> res = fparser(input, overall_line_count);
1342 if (!wstore(res.value)) {
1345 ++overall_line_count;
1346 valid = res.remaining;
1352 template<
typename Type_,
class FieldParser_,
class Store_>
1353 bool scan_vector_array(Store_ store) {
1354 bool finished =
false;
1356 if (my_nthreads == 1) {
1357 FieldParser_ fparser;
1358 finished = scan_vector_array_base<Type_>(
1362 [&](Type_ value) ->
bool {
1363 check_num_lines_loop(current_data_line);
1364 ++current_data_line;
1365 return store(current_data_line, 1, value);
1371 std::vector<char> buffer;
1372 FieldParser_ fparser;
1373 std::vector<Type_> contents;
1377 ThreadPool<Workspace> tp(
1378 [&](Workspace& work) ->
bool {
1379 byteme::RawBufferReader reader(
reinterpret_cast<const unsigned char*
>(work.buffer.data()), work.buffer.size());
1381 return scan_vector_array_base<Type_>(
1385 [&](Type_ value) ->
bool {
1386 work.contents.emplace_back(value);
1395 [&](Workspace& work) ->
bool {
1396 return configure_parallel_workspace(work);
1398 [&](Workspace& work) ->
bool {
1399 for (
const auto& val : work.contents) {
1400 check_num_lines_loop(current_data_line);
1401 ++current_data_line;
1402 if (!store(current_data_line, 1, val)) {
1411 check_num_lines_final(finished, current_data_line);
1416 void check_preamble()
const {
1417 if (!my_passed_banner || !my_passed_size) {
1418 throw std::runtime_error(
"banner or size lines have not yet been parsed");
1422 template<
typename Type_>
1423 class IntegerFieldParser {
1425 template<
class Input2_>
1426 ParseInfo<Type_> operator()(Input2_& input,
LineIndex overall_line_count) {
1427 char firstchar = input.get();
1428 bool negative = (firstchar ==
'-');
1429 if (negative || firstchar ==
'+') {
1430 if (!(input.advance())) {
1431 throw std::runtime_error(
"premature termination of an integer on line " + std::to_string(overall_line_count + 1));
1435 constexpr Type_ upper_limit = std::numeric_limits<Type_>::max();
1436 constexpr Type_ upper_limit_before_mult = upper_limit / 10;
1437 constexpr Type_ upper_limit_mod = upper_limit % 10;
1438 constexpr Type_ lower_limit = std::numeric_limits<Type_>::lowest();
1439 constexpr Type_ lower_limit_before_mult = lower_limit / 10;
1440 constexpr Type_ lower_limit_mod = -(lower_limit % 10);
1445 char x = input.get();
1447 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
1449 Type_ delta = x -
'0';
1453 if (val <= lower_limit_before_mult && !(val == lower_limit_before_mult && delta <= lower_limit_mod)) {
1454 throw std::runtime_error(
"integer underflow on line " + std::to_string(overall_line_count + 1));
1459 if (val >= upper_limit_before_mult && !(val == upper_limit_before_mult && delta <= upper_limit_mod)) {
1460 throw std::runtime_error(
"integer overflow on line " + std::to_string(overall_line_count + 1));
1468 case ' ':
case '\t':
case '\r':
1469 if (!advance_and_chomp(input)) {
1470 return ParseInfo<Type_>(val,
false);
1472 if (input.get() !=
'\n') {
1473 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1475 return ParseInfo<Type_>(val, input.advance());
1483 throw std::runtime_error(
"empty integer field on line " + std::to_string(overall_line_count + 1));
1485 return ParseInfo<Type_>(val, input.advance());
1487 throw std::runtime_error(
"expected an integer value on line " + std::to_string(overall_line_count + 1));
1490 if (!(input.advance())) {
1495 return ParseInfo<Type_>(val,
false);
1513 template<
typename Type_ =
int,
class Store_>
1516 static_assert(std::is_integral<Type_>::value);
1518 auto wrapped_store = [&](Index_ r, Index_ c, Type_ val) ->
bool {
1519 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, Type_>::type,
bool>::value) {
1520 return store(r, c, val);
1527 if (my_details.
format == Format::COORDINATE) {
1528 if (my_details.
object == Object::MATRIX) {
1529 return scan_matrix_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1531 return scan_vector_coordinate_non_pattern<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1534 if (my_details.
object == Object::MATRIX) {
1535 return scan_matrix_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1537 return scan_vector_array<Type_, IntegerFieldParser<Type_> >(std::move(wrapped_store));
1543 template<
bool last_,
typename Type_,
typename Input2_>
1544 static ParseInfo<Type_> parse_real(Input2_& input, std::string& temporary, Index overall_line_count) {
1546 ParseInfo<Type_> output(0,
true);
1549 char x = input.get();
1552 if constexpr(last_) {
1556 if (temporary.empty()) {
1557 throw std::runtime_error(
"empty number field on line " + std::to_string(overall_line_count + 1));
1559 output.remaining = input.advance();
1561 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1563 goto final_processing;
1565 case ' ':
case '\t':
case '\r':
1566 if constexpr(last_) {
1567 if (!advance_and_chomp(input)) {
1568 output.remaining =
false;
1570 if (input.get() !=
'\n') {
1571 throw std::runtime_error(
"more fields than expected on line " + std::to_string(overall_line_count + 1));
1573 output.remaining = input.advance();
1576 if (!advance_and_chomp(input)) {
1577 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1579 if (input.get() ==
'\n') {
1580 throw std::runtime_error(
"unexpected newline on line " + std::to_string(overall_line_count + 1));
1583 goto final_processing;
1590 if (!(input.advance())) {
1591 if constexpr(last_) {
1592 output.remaining =
false;
1593 goto final_processing;
1595 throw std::runtime_error(
"unexpected end of file on line " + std::to_string(overall_line_count + 1));
1600 if (temporary.size() >= 2 && temporary[0] ==
'0' && (temporary[1] ==
'x' || temporary[1] ==
'X')) {
1601 throw std::runtime_error(
"hexadecimal numbers are not allowed on line " + std::to_string(overall_line_count + 1));
1606 if constexpr(std::is_same<Type_, float>::value) {
1607 output.value = std::stof(temporary, &n);
1608 }
else if constexpr(std::is_same<Type_, long double>::value) {
1609 output.value = std::stold(temporary, &n);
1611 output.value = std::stod(temporary, &n);
1613 }
catch (std::invalid_argument& e) {
1614 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1617 if (n != temporary.size()) {
1618 throw std::runtime_error(
"failed to convert value to a real number on line " + std::to_string(overall_line_count + 1));
1623 template<
typename Type_>
1624 class RealFieldParser {
1626 template<
class Input2_>
1627 ParseInfo<Type_> operator()(Input2_& input,
LineIndex overall_line_count) {
1628 return parse_real<true, Type_>(input, my_temporary, overall_line_count);
1631 std::string my_temporary;
1648 template<
typename Type_ =
double,
class Store_>
1651 static_assert(std::is_floating_point<Type_>::value);
1653 auto store_real = [&](Index_ r, Index_ c, Type_ val) ->
bool {
1654 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, Type_>::type,
bool>::value) {
1655 return store(r, c, val);
1662 if (my_details.
format == Format::COORDINATE) {
1663 if (my_details.
object == Object::MATRIX) {
1664 return scan_matrix_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1666 return scan_vector_coordinate_non_pattern<Type_, RealFieldParser<Type_> >(std::move(store_real));
1669 if (my_details.
object == Object::MATRIX) {
1670 return scan_matrix_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1672 return scan_vector_array<Type_, RealFieldParser<Type_> >(std::move(store_real));
1691 template<
typename Type_ =
double,
class Store_>
1697 template<
typename InnerType_>
1698 class ComplexFieldParser {
1700 template<
typename Input2_>
1701 ParseInfo<std::complex<InnerType_> > operator()(Input2_& input,
LineIndex overall_line_count) {
1702 auto first = parse_real<false, InnerType_>(input, my_temporary, overall_line_count);
1703 auto second = parse_real<true, InnerType_>(input, my_temporary, overall_line_count);
1704 ParseInfo<std::complex<InnerType_> > output;
1705 output.value.real(first.value);
1706 output.value.imag(second.value);
1707 output.remaining = second.remaining;
1711 std::string my_temporary;
1728 template<
typename Type_ =
double,
class Store_>
1731 static_assert(std::is_floating_point<Type_>::value);
1733 typedef std::complex<Type_> FullType;
1734 auto store_comp = [&](Index_ r, Index_ c, FullType val) ->
bool {
1735 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, FullType>::type,
bool>::value) {
1736 return store(r, c, val);
1743 if (my_details.
format == Format::COORDINATE) {
1744 if (my_details.
object == Object::MATRIX) {
1745 return scan_matrix_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1747 return scan_vector_coordinate_non_pattern<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1750 if (my_details.
object == Object::MATRIX) {
1751 return scan_matrix_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1753 return scan_vector_array<FullType, ComplexFieldParser<Type_> >(std::move(store_comp));
1773 template<
typename Type_ =
bool,
class Store_>
1776 if (my_details.
format != Format::COORDINATE) {
1777 throw std::runtime_error(
"'array' format for 'pattern' field is not supported");
1780 auto store_pat = [&](Index_ r, Index_ c) ->
bool {
1781 if constexpr(std::is_same<typename std::invoke_result<Store_, Index_, Index_, bool>::type,
bool>::value) {
1782 return store(r, c,
true);
1789 if (my_details.
object == Object::MATRIX) {
1790 return scan_matrix_coordinate_pattern(std::move(store_pat));
1792 return scan_vector_coordinate_pattern(std::move(store_pat));