forked from google/or-tools
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathmps_reader_template.cc
139 lines (122 loc) · 5.06 KB
/
mps_reader_template.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/lp_data/mps_reader_template.h"
#include <cstdint>
#include "absl/container/inlined_vector.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "ortools/base/status_macros.h"
namespace operations_research::internal {
namespace {
// Starting positions of each of the fields for fixed format.
static constexpr int kFieldStartPos[kNumMpsFields] = {1, 4, 14, 24, 39, 49};
// Lengths of each of the fields for fixed format.
static constexpr int kFieldLength[kNumMpsFields] = {2, 8, 8, 12, 8, 12};
// Positions where there should be spaces for fixed format.
static constexpr int kSpacePos[12] = {12, 13, 22, 23, 36, 37,
38, 47, 48, 61, 62, 63};
} // namespace
// static
absl::StatusOr<MPSLineInfo> MPSLineInfo::Create(int64_t line_num,
bool free_form,
absl::string_view line) {
// Deal with windows end of line characters and trailing white space.
line = absl::StripTrailingAsciiWhitespace(line);
if (!free_form && absl::StrContains(line, '\t')) {
return absl::InvalidArgumentError("File contains tabs.");
}
MPSLineInfo line_info = MPSLineInfo(line_num, free_form, line);
if (!free_form && !line_info.IsFixedFormat()) {
return line_info.InvalidArgumentError("Line is not in fixed format.");
}
if (!line_info.IsCommentOrBlank()) {
RETURN_IF_ERROR(line_info.SplitLineIntoFields());
}
return line_info;
}
bool MPSLineInfo::IsFixedFormat() const {
if (IsCommentOrBlank()) {
return true;
}
if (IsNewSection()) {
absl::string_view first_word = GetFirstWord();
// Note: the name should also comply with the fixed format guidelines
// (maximum 8 characters) but in practice there are many problem files in
// the netlib archive that are in fixed format and have a long name. We
// choose to ignore these cases and treat them as fixed format anyway.
// Other than the NAME record, every new section label should be the only
// entry on the line.
return first_word == line_ || first_word == "NAME";
}
constexpr int kMaxLineSize =
kFieldStartPos[kNumMpsFields - 1] + kFieldLength[kNumMpsFields - 1];
// Note that `line_` already has been stripped of trailing white spaces.
const int line_size = line_.size();
if (line_size > kMaxLineSize) return false;
for (const int i : kSpacePos) {
if (i >= line_size) break;
if (line_[i] != ' ') return false;
}
return true;
}
absl::Status MPSLineInfo::SplitLineIntoFields() {
if (free_form_) {
absl::string_view remaining_line = absl::StripLeadingAsciiWhitespace(line_);
// Although using `fields_ = StrSplit(line, ByAnyChar(" \t))` is shorter to
// write, this explicit loop, and checking the `kNumMpsFields` is
// significantly faster.
while (!remaining_line.empty()) {
if (fields_.size() == kNumMpsFields) {
return InvalidArgumentError("Found too many fields.");
}
// find_first_of() returns npos for "not found". substr() interprets
// len==npos as end of string.
const int pos = remaining_line.find_first_of(" \t");
fields_.push_back(remaining_line.substr(0, pos));
if (pos == absl::string_view::npos) {
// substr() will throw an exception if the start is npos.
break;
}
remaining_line =
absl::StripLeadingAsciiWhitespace(remaining_line.substr(pos));
}
} else {
const int line_size = line_.size();
for (int i = 0; i < kNumMpsFields; ++i) {
if (kFieldStartPos[i] >= line_size) break;
fields_.push_back(absl::StripTrailingAsciiWhitespace(
line_.substr(kFieldStartPos[i], kFieldLength[i])));
}
}
return absl::OkStatus();
}
absl::string_view MPSLineInfo::GetFirstWord() const {
return line_.substr(0, line_.find(' '));
}
bool MPSLineInfo::IsCommentOrBlank() const {
// Trailing whitespace has already been trimmed, so a blank line has become
// empty.
return (line_.empty() || line_[0] == '*');
}
absl::Status MPSLineInfo::InvalidArgumentError(
absl::string_view error_message) const {
return AppendLineToError(absl::InvalidArgumentError(error_message));
}
absl::Status MPSLineInfo::AppendLineToError(const absl::Status& status) const {
return util::StatusBuilder(status).SetAppend()
<< " Line " << line_num_ << ": \"" << line_ << "\".";
}
} // namespace operations_research::internal