OR-Tools  9.6
base/file.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 
17 #include "absl/strings/str_cat.h"
18 #if defined(_MSC_VER)
19 #include <io.h>
20 #define access _access
21 #define F_OK 0
22 #else
23 #include <unistd.h>
24 #endif
25 
26 #include <cstring>
27 #include <memory>
28 #include <string>
29 
30 #include "absl/log/check.h"
31 #include "ortools/base/file.h"
32 #include "ortools/base/logging.h"
33 
34 File::File(FILE* const f_des, const absl::string_view& name)
35  : f_(f_des), name_(name) {}
36 
37 bool File::Delete(const char* const name) { return remove(name) == 0; }
38 
39 bool File::Exists(const char* const name) { return access(name, F_OK) == 0; }
40 
41 size_t File::Size() {
42  struct stat f_stat;
43  stat(name_.data(), &f_stat);
44  return f_stat.st_size;
45 }
46 
47 bool File::Flush() { return fflush(f_) == 0; }
48 
49 bool File::Close() {
50  if (fclose(f_) == 0) {
51  f_ = nullptr;
52  return true;
53  } else {
54  return false;
55  }
56 }
57 
58 absl::Status File::Close(int flags) {
59  if (flags != file::Defaults())
60  return absl::Status(absl::StatusCode::kInvalidArgument, "Wrong flags");
61  return Close()
62  ? absl::OkStatus()
63  : absl::Status(absl::StatusCode::kInvalidArgument,
64  absl::StrCat("Could not close file '", name_, "'"));
65 }
66 
67 void File::ReadOrDie(void* const buf, size_t size) {
68  CHECK_EQ(fread(buf, 1, size, f_), size);
69 }
70 
71 size_t File::Read(void* const buf, size_t size) {
72  return fread(buf, 1, size, f_);
73 }
74 
75 void File::WriteOrDie(const void* const buf, size_t size) {
76  CHECK_EQ(fwrite(buf, 1, size, f_), size);
77 }
78 size_t File::Write(const void* const buf, size_t size) {
79  return fwrite(buf, 1, size, f_);
80 }
81 
82 File* File::OpenOrDie(const char* const name, const char* const flag) {
83  FILE* const f_des = fopen(name, flag);
84  if (f_des == nullptr) {
85  std::cerr << "Cannot open " << name;
86  exit(1);
87  }
88  File* const f = new File(f_des, name);
89  return f;
90 }
91 
92 File* File::Open(const char* const name, const char* const flag) {
93  FILE* const f_des = fopen(name, flag);
94  if (f_des == nullptr) return nullptr;
95  File* const f = new File(f_des, name);
96  return f;
97 }
98 
99 char* File::ReadLine(char* const output, uint64_t max_length) {
100  return fgets(output, max_length, f_);
101 }
102 
103 int64_t File::ReadToString(std::string* const output, uint64_t max_length) {
104  CHECK(output != nullptr);
105  output->clear();
106 
107  if (max_length == 0) return 0;
108 
109  int64_t needed = max_length;
110  int bufsize = (needed < (2 << 20) ? needed : (2 << 20));
111 
112  std::unique_ptr<char[]> buf(new char[bufsize]);
113 
114  int64_t nread = 0;
115  while (needed > 0) {
116  nread = Read(buf.get(), (bufsize < needed ? bufsize : needed));
117  if (nread > 0) {
118  output->append(buf.get(), nread);
119  needed -= nread;
120  } else {
121  break;
122  }
123  }
124  return (nread >= 0 ? static_cast<int64_t>(output->size()) : -1);
125 }
126 
127 size_t File::WriteString(const std::string& line) {
128  return Write(line.c_str(), line.size());
129 }
130 
131 bool File::WriteLine(const std::string& line) {
132  if (Write(line.c_str(), line.size()) != line.size()) return false;
133  return Write("\n", 1) == 1;
134 }
135 
136 absl::string_view File::filename() const { return name_; }
137 
138 bool File::Open() const { return f_ != nullptr; }
139 
140 void File::Init() {}
141 
142 namespace file {
143 absl::Status Open(const absl::string_view& filename,
144  const absl::string_view& mode, File** f, int flags) {
145  if (flags == Defaults()) {
146  *f = File::Open(filename, mode.data());
147  if (*f != nullptr) {
148  return absl::OkStatus();
149  }
150  }
151  return absl::Status(absl::StatusCode::kInvalidArgument,
152  absl::StrCat("Could not open '", filename, "'"));
153 }
154 
155 File* OpenOrDie(const absl::string_view& filename,
156  const absl::string_view& mode, int flags) {
157  File* f;
158  CHECK_EQ(flags, Defaults());
159  f = File::Open(filename, mode.data());
160  CHECK(f != nullptr) << absl::StrCat("Could not open '", filename, "'");
161  return f;
162 }
163 
164 absl::Status GetContents(const absl::string_view& filename, std::string* output,
165  int flags) {
166  File* file;
167  auto status = file::Open(filename, "r", &file, flags);
168  if (!status.ok()) return status;
169 
170  const int64_t size = file->Size();
171  if (file->ReadToString(output, size) == size) {
172  status.Update(file->Close(flags));
173  return status;
174  }
175 #if defined(_MSC_VER)
176  // On windows, binary files needs to be opened with the "rb" flags.
177  file->Close();
178  // Retry in binary mode.
179  status = file::Open(filename, "rb", &file, flags);
180  if (!status.ok()) return status;
181 
182  const int64_t b_size = file->Size();
183  if (file->ReadToString(output, b_size) == b_size) {
184  status.Update(file->Close(flags));
185  return status;
186  }
187 #endif // _MSC_VER
188 
189  file->Close(flags); // Even if ReadToString() fails!
190  return absl::Status(absl::StatusCode::kInvalidArgument,
191  absl::StrCat("Could not read from '", filename, "'."));
192 }
193 
194 absl::Status WriteString(File* file, const absl::string_view& contents,
195  int flags) {
196  if (flags == Defaults() && file != nullptr &&
197  file->Write(contents.data(), contents.size()) == contents.size()) {
198  return absl::OkStatus();
199  }
200  return absl::Status(
201  absl::StatusCode::kInvalidArgument,
202  absl::StrCat("Could not write ", contents.size(), " bytes"));
203 }
204 
205 absl::Status SetContents(const absl::string_view& filename,
206  const absl::string_view& contents, int flags) {
207  File* file;
208  auto status = file::Open(filename, "w", &file, flags);
209  if (!status.ok()) return status;
210  status = file::WriteString(file, contents, flags);
211  status.Update(file->Close(flags)); // Even if WriteString() fails!
212  return status;
213 }
214 
215 bool ReadFileToString(const absl::string_view& file_name, std::string* output) {
216  return GetContents(file_name, output, file::Defaults()).ok();
217 }
218 
219 bool WriteStringToFile(const std::string& data,
220  const absl::string_view& file_name) {
221  return SetContents(file_name, data, file::Defaults()).ok();
222 }
223 
224 namespace {
225 class NoOpErrorCollector : public google::protobuf::io::ErrorCollector {
226  public:
227  void AddError(int line, int column, const std::string& message) override {}
228 };
229 } // namespace
230 
231 bool ReadFileToProto(const absl::string_view& file_name,
232  google::protobuf::Message* proto) {
233  std::string str;
234  if (!ReadFileToString(file_name, &str)) {
235  LOG(INFO) << "Could not read " << file_name;
236  return false;
237  }
238  // Attempt to decode ASCII before deciding binary. Do it in this order because
239  // it is much harder for a binary encoding to happen to be a valid ASCII
240  // encoding than the other way around. For instance "index: 1\n" is a valid
241  // (but nonsensical) binary encoding. We want to avoid printing errors for
242  // valid binary encodings if the ASCII parsing fails, and so specify a no-op
243  // error collector.
244  NoOpErrorCollector error_collector;
245  google::protobuf::TextFormat::Parser parser;
246  parser.RecordErrorsTo(&error_collector);
247  if (parser.ParseFromString(str, proto)) {
248  return true;
249  }
250  if (proto->ParseFromString(str)) {
251  return true;
252  }
253  // Re-parse the ASCII, just to show the diagnostics (we could also get them
254  // out of the ErrorCollector but this way is easier).
255  google::protobuf::TextFormat::ParseFromString(str, proto);
256  LOG(INFO) << "Could not parse contents of " << file_name;
257  return false;
258 }
259 
260 void ReadFileToProtoOrDie(const absl::string_view& file_name,
261  google::protobuf::Message* proto) {
262  CHECK(ReadFileToProto(file_name, proto)) << "file_name: " << file_name;
263 }
264 
265 bool WriteProtoToASCIIFile(const google::protobuf::Message& proto,
266  const absl::string_view& file_name) {
267  std::string proto_string;
268  return google::protobuf::TextFormat::PrintToString(proto, &proto_string) &&
269  WriteStringToFile(proto_string, file_name);
270 }
271 
272 void WriteProtoToASCIIFileOrDie(const google::protobuf::Message& proto,
273  const absl::string_view& file_name) {
274  CHECK(WriteProtoToASCIIFile(proto, file_name)) << "file_name: " << file_name;
275 }
276 
277 bool WriteProtoToFile(const google::protobuf::Message& proto,
278  const absl::string_view& file_name) {
279  std::string proto_string;
280  return proto.AppendToString(&proto_string) &&
281  WriteStringToFile(proto_string, file_name);
282 }
283 
284 void WriteProtoToFileOrDie(const google::protobuf::Message& proto,
285  const absl::string_view& file_name) {
286  CHECK(WriteProtoToFile(proto, file_name)) << "file_name: " << file_name;
287 }
288 
289 absl::Status GetTextProto(const absl::string_view& filename,
290  google::protobuf::Message* proto, int flags) {
291  if (flags == Defaults()) {
292  if (ReadFileToProto(filename, proto)) return absl::OkStatus();
293  }
294  return absl::Status(
295  absl::StatusCode::kInvalidArgument,
296  absl::StrCat("Could not read proto from '", filename, "'."));
297 }
298 
299 absl::Status SetTextProto(const absl::string_view& filename,
300  const google::protobuf::Message& proto, int flags) {
301  if (flags == Defaults()) {
302  if (WriteProtoToASCIIFile(proto, filename)) return absl::OkStatus();
303  }
304  return absl::Status(
305  absl::StatusCode::kInvalidArgument,
306  absl::StrCat("Could not write proto to '", filename, "'."));
307 }
308 
309 absl::Status GetBinaryProto(const absl::string_view filename,
310  google::protobuf::Message* const proto,
311  const int flags) {
312  std::string str;
313  if (flags == Defaults() && ReadFileToString(filename, &str) &&
314  proto->ParseFromString(str)) {
315  return absl::OkStatus();
316  }
317  return absl::Status(
318  absl::StatusCode::kInvalidArgument,
319  absl::StrCat("Could not read proto from '", filename, "'."));
320 }
321 
322 absl::Status SetBinaryProto(const absl::string_view& filename,
323  const google::protobuf::Message& proto, int flags) {
324  if (flags == Defaults()) {
325  if (WriteProtoToFile(proto, filename)) return absl::OkStatus();
326  }
327  return absl::Status(
328  absl::StatusCode::kInvalidArgument,
329  absl::StrCat("Could not write proto to '", filename, "'."));
330 }
331 
332 absl::Status Delete(const absl::string_view& path, int flags) {
333  if (flags == Defaults()) {
334  if (remove(path.data()) == 0) return absl::OkStatus();
335  }
336  return absl::Status(absl::StatusCode::kInvalidArgument,
337  absl::StrCat("Could not delete '", path, "'."));
338 }
339 
340 absl::Status Exists(const absl::string_view& path, int flags) {
341  if (flags == Defaults()) {
342  if (access(path.data(), F_OK) == 0) return absl::OkStatus();
343  }
344  return absl::Status(absl::StatusCode::kInvalidArgument,
345  absl::StrCat("File '", path, "' does not exist."));
346 }
347 } // namespace file
Definition: base/file.h:33
bool Open() const
Definition: base/file.cc:138
static File * OpenOrDie(const char *const name, const char *const flag)
Definition: base/file.cc:82
static void Init()
Definition: base/file.cc:140
int64_t ReadToString(std::string *const line, uint64_t max_length)
Definition: base/file.cc:103
char * ReadLine(char *const output, uint64_t max_length)
Definition: base/file.cc:99
static bool Exists(const char *const name)
Definition: base/file.cc:39
void WriteOrDie(const void *const buff, size_t size)
Definition: base/file.cc:75
bool Flush()
Definition: base/file.cc:47
void ReadOrDie(void *const buff, size_t size)
Definition: base/file.cc:67
size_t Write(const void *const buff, size_t size)
Definition: base/file.cc:78
size_t Size()
Definition: base/file.cc:41
size_t Read(void *const buff, size_t size)
Definition: base/file.cc:71
bool WriteLine(const std::string &line)
Definition: base/file.cc:131
static bool Delete(const char *const name)
Definition: base/file.cc:37
absl::string_view filename() const
Definition: base/file.cc:136
size_t WriteString(const std::string &line)
Definition: base/file.cc:127
bool Close()
Definition: base/file.cc:49
CpModelProto proto
const std::string name
absl::Status status
Definition: g_gurobi.cc:41
absl::Status WriteString(File *file, const absl::string_view &contents, int flags)
Definition: base/file.cc:194
absl::Status GetContents(const absl::string_view &filename, std::string *output, int flags)
Definition: base/file.cc:164
absl::Status GetBinaryProto(const absl::string_view filename, google::protobuf::Message *const proto, const int flags)
Definition: base/file.cc:309
bool ReadFileToString(const absl::string_view &file_name, std::string *output)
Definition: base/file.cc:215
absl::Status Delete(const absl::string_view &path, int flags)
Definition: base/file.cc:332
File * OpenOrDie(const absl::string_view &filename, const absl::string_view &mode, int flags)
Definition: base/file.cc:155
void WriteProtoToASCIIFileOrDie(const google::protobuf::Message &proto, const absl::string_view &file_name)
Definition: base/file.cc:272
bool WriteProtoToFile(const google::protobuf::Message &proto, const absl::string_view &file_name)
Definition: base/file.cc:277
absl::Status GetTextProto(const absl::string_view &filename, google::protobuf::Message *proto, int flags)
Definition: base/file.cc:289
void WriteProtoToFileOrDie(const google::protobuf::Message &proto, const absl::string_view &file_name)
Definition: base/file.cc:284
bool WriteProtoToASCIIFile(const google::protobuf::Message &proto, const absl::string_view &file_name)
Definition: base/file.cc:265
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: base/file.cc:299
Options Defaults()
Definition: base/file.h:123
bool WriteStringToFile(const std::string &data, const absl::string_view &file_name)
Definition: base/file.cc:219
absl::Status SetBinaryProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: base/file.cc:322
bool ReadFileToProto(const absl::string_view &file_name, google::protobuf::Message *proto)
Definition: base/file.cc:231
absl::Status Exists(const absl::string_view &path, int flags)
Definition: base/file.cc:340
absl::Status Open(const absl::string_view &filename, const absl::string_view &mode, File **f, int flags)
Definition: base/file.cc:143
absl::Status SetContents(const absl::string_view &filename, const absl::string_view &contents, int flags)
Definition: base/file.cc:205
void ReadFileToProtoOrDie(const absl::string_view &file_name, google::protobuf::Message *proto)
Definition: base/file.cc:260
int line
Definition: parse_proto.cc:31
int column
Definition: parse_proto.cc:32
std::string message
Definition: trace.cc:399