Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
file.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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 "ortools/base/file.h"
15
16#include <sys/stat.h>
17#include <sys/types.h>
18
19#include <cstdint>
20#if defined(_MSC_VER)
21#include <io.h>
22#define access _access
23#define F_OK 0
24#else
25#include <unistd.h>
26#endif
27
28#include <cstdio>
29#include <cstdlib>
30#include <cstring>
31#include <iostream> // NOLINT
32#include <memory>
33#include <string>
34
35#include "absl/log/check.h"
36#include "absl/status/status.h"
37#include "absl/strings/str_cat.h"
38#include "absl/strings/string_view.h"
39#include "google/protobuf/io/tokenizer.h"
40#include "google/protobuf/message.h"
41#include "google/protobuf/text_format.h"
43
44File::File(FILE* descriptor, absl::string_view name)
45 : f_(descriptor), name_(name) {}
46
47bool File::Delete(absl::string_view filename) {
48 std::string null_terminated_name = std::string(filename);
49 return remove(null_terminated_name.c_str()) == 0;
50}
51
52bool File::Exists(absl::string_view filename) {
53 std::string null_terminated_name = std::string(filename);
54 return access(null_terminated_name.c_str(), F_OK) == 0;
55}
56
57size_t File::Size() {
58 struct stat f_stat;
59 stat(name_.c_str(), &f_stat);
60 return f_stat.st_size;
61}
62
63bool File::Flush() { return fflush(f_) == 0; }
64
65// Deletes "this" on closing.
67 bool ok = true;
68 if (f_ == nullptr) {
69 return ok;
70 }
71 if (fclose(f_) == 0) {
72 f_ = nullptr;
73 } else {
74 ok = false;
75 }
76 delete this;
77 return ok;
78}
79
80// Deletes "this" on closing.
81absl::Status File::Close(int /*flags*/) {
82 absl::Status status;
83 if (f_ == nullptr) {
84 return status;
85 }
86 if (fclose(f_) == 0) {
87 f_ = nullptr;
88 } else {
89 status.Update(
90 absl::Status(absl::StatusCode::kInvalidArgument,
91 absl::StrCat("Could not close file '", name_, "'")));
92 }
93 delete this;
94 return status;
95}
96
97void File::ReadOrDie(void* buf, size_t size) {
98 CHECK_EQ(fread(buf, 1, size, f_), size);
99}
100
101size_t File::Read(void* buf, size_t size) { return fread(buf, 1, size, f_); }
102
103void File::WriteOrDie(const void* buf, size_t size) {
104 CHECK_EQ(fwrite(buf, 1, size, f_), size);
105}
106size_t File::Write(const void* buf, size_t size) {
107 return fwrite(buf, 1, size, f_);
108}
109
110File* File::OpenOrDie(absl::string_view filename, absl::string_view mode) {
111 File* f = File::Open(filename, mode);
112 CHECK(f != nullptr) << absl::StrCat("Could not open '", filename, "'");
113 return f;
114}
115
116File* File::Open(absl::string_view filename, absl::string_view mode) {
117 std::string null_terminated_name = std::string(filename);
118 std::string null_terminated_mode = std::string(mode);
119 FILE* f_des =
120 fopen(null_terminated_name.c_str(), null_terminated_mode.c_str());
121 if (f_des == nullptr) return nullptr;
122 File* f = new File(f_des, filename);
123 return f;
124}
125
126char* File::ReadLine(char* output, uint64_t max_length) {
127 return fgets(output, max_length, f_);
128}
129
130int64_t File::ReadToString(std::string* line, uint64_t max_length) {
131 CHECK(line != nullptr);
132 line->clear();
133
134 if (max_length == 0) return 0;
135
136 int64_t needed = max_length;
137 int bufsize = (needed < (2 << 20) ? needed : (2 << 20));
138
139 std::unique_ptr<char[]> buf(new char[bufsize]);
140
141 int64_t nread = 0;
142 while (needed > 0) {
143 nread = Read(buf.get(), (bufsize < needed ? bufsize : needed));
144 if (nread > 0) {
145 line->append(buf.get(), nread);
146 needed -= nread;
147 } else {
148 break;
149 }
150 }
151 return (nread >= 0 ? static_cast<int64_t>(line->size()) : -1);
152}
153
154size_t File::WriteString(absl::string_view str) {
155 return Write(str.data(), str.size());
156}
157
158bool File::WriteLine(absl::string_view line) {
159 if (Write(line.data(), line.size()) != line.size()) return false;
160 return Write("\n", 1) == 1;
161}
162
163absl::string_view File::filename() const { return name_; }
164
165bool File::Open() const { return f_ != nullptr; }
166
167void File::Init() {}
168
169namespace file {
170absl::Status Open(absl::string_view filename, absl::string_view mode, File** f,
171 Options options) {
172 if (options == Defaults()) {
173 *f = File::Open(filename, mode);
174 if (*f != nullptr) {
175 return absl::OkStatus();
176 }
177 }
178 return absl::Status(absl::StatusCode::kInvalidArgument,
179 absl::StrCat("Could not open '", filename, "'"));
180}
181
182File* OpenOrDie(absl::string_view filename, absl::string_view mode,
183 Options options) {
184 File* f;
185 CHECK_EQ(options, Defaults());
186 f = File::Open(filename, mode);
187 CHECK(f != nullptr) << absl::StrCat("Could not open '", filename, "'");
188 return f;
189}
190
191absl::StatusOr<std::string> GetContents(absl::string_view path,
192 Options options) {
193 std::string contents;
194 absl::Status status = GetContents(path, &contents, options);
195 if (!status.ok()) {
196 return status;
197 }
198 return contents;
199}
200
201absl::Status GetContents(absl::string_view filename, std::string* output,
202 Options options) {
203 File* file;
204 auto status = file::Open(filename, "r", &file, options);
205 if (!status.ok()) return status;
206
207 const int64_t size = file->Size();
208 if (file->ReadToString(output, size) == size) {
209 status.Update(file->Close(options));
210 return status;
211 }
212#if defined(_MSC_VER)
213 // On windows, binary files needs to be opened with the "rb" flags.
214 file->Close();
215 // Retry in binary mode.
216 status = file::Open(filename, "rb", &file, options);
217 if (!status.ok()) return status;
218
219 const int64_t b_size = file->Size();
220 if (file->ReadToString(output, b_size) == b_size) {
221 status.Update(file->Close(options));
222 return status;
223 }
224#endif // _MSC_VER
225
226 file->Close(options).IgnoreError(); // Even if ReadToString() fails!
227 return absl::Status(absl::StatusCode::kInvalidArgument,
228 absl::StrCat("Could not read from '", filename, "'."));
229}
230
231absl::Status WriteString(File* file, absl::string_view contents,
232 Options options) {
233 if (options == Defaults() && file != nullptr &&
234 file->Write(contents.data(), contents.size()) == contents.size()) {
235 return absl::OkStatus();
236 }
237 return absl::Status(
238 absl::StatusCode::kInvalidArgument,
239 absl::StrCat("Could not write ", contents.size(), " bytes"));
240}
241
242absl::Status SetContents(absl::string_view filename, absl::string_view contents,
243 Options options) {
244 File* file;
245 auto status = file::Open(filename, "w", &file, options);
246 if (!status.ok()) return status;
247 status = file::WriteString(file, contents, options);
248 status.Update(file->Close(options)); // Even if WriteString() fails!
249 return status;
250}
251
252bool ReadFileToString(absl::string_view file_name, std::string* output) {
253 return GetContents(file_name, output, file::Defaults()).ok();
254}
255
256bool WriteStringToFile(absl::string_view data, absl::string_view file_name) {
257 return SetContents(file_name, data, file::Defaults()).ok();
258}
259
260namespace {
261class NoOpErrorCollector : public google::protobuf::io::ErrorCollector {
262 public:
263 ~NoOpErrorCollector() override = default;
264 void RecordError(int /*line*/, int /*column*/,
265 absl::string_view /*message*/) override {}
266};
267} // namespace
268
269bool ReadFileToProto(absl::string_view file_name,
270 google::protobuf::Message* proto) {
271 std::string str;
272 if (!ReadFileToString(file_name, &str)) {
273 LOG(INFO) << "Could not read " << file_name;
274 return false;
275 }
276 // Attempt to decode ASCII before deciding binary. Do it in this order because
277 // it is much harder for a binary encoding to happen to be a valid ASCII
278 // encoding than the other way around. For instance "index: 1\n" is a valid
279 // (but nonsensical) binary encoding. We want to avoid printing errors for
280 // valid binary encodings if the ASCII parsing fails, and so specify a no-op
281 // error collector.
282 NoOpErrorCollector error_collector;
283 google::protobuf::TextFormat::Parser parser;
284 parser.RecordErrorsTo(&error_collector);
285 if (parser.ParseFromString(str, proto)) {
286 return true;
287 }
288 if (proto->ParseFromString(str)) {
289 return true;
290 }
291 // Re-parse the ASCII, just to show the diagnostics (we could also get them
292 // out of the ErrorCollector but this way is easier).
293 google::protobuf::TextFormat::ParseFromString(str, proto);
294 LOG(INFO) << "Could not parse contents of " << file_name;
295 return false;
296}
297
298void ReadFileToProtoOrDie(absl::string_view file_name,
299 google::protobuf::Message* proto) {
300 CHECK(ReadFileToProto(file_name, proto)) << "file_name: " << file_name;
301}
302
303bool WriteProtoToASCIIFile(const google::protobuf::Message& proto,
304 absl::string_view file_name) {
305 std::string proto_string;
306 return google::protobuf::TextFormat::PrintToString(proto, &proto_string) &&
307 WriteStringToFile(proto_string, file_name);
308}
309
310void WriteProtoToASCIIFileOrDie(const google::protobuf::Message& proto,
311 absl::string_view file_name) {
312 CHECK(WriteProtoToASCIIFile(proto, file_name)) << "file_name: " << file_name;
313}
314
315bool WriteProtoToFile(const google::protobuf::Message& proto,
316 absl::string_view file_name) {
317 std::string proto_string;
318 return proto.AppendToString(&proto_string) &&
319 WriteStringToFile(proto_string, file_name);
320}
321
322void WriteProtoToFileOrDie(const google::protobuf::Message& proto,
323 absl::string_view file_name) {
324 CHECK(WriteProtoToFile(proto, file_name)) << "file_name: " << file_name;
325}
326
327absl::Status GetTextProto(absl::string_view filename,
328 google::protobuf::Message* proto, Options options) {
329 if (options == Defaults()) {
330 if (ReadFileToProto(filename, proto)) return absl::OkStatus();
331 }
332 return absl::Status(
333 absl::StatusCode::kInvalidArgument,
334 absl::StrCat("Could not read proto from '", filename, "'."));
335}
336
337absl::Status SetTextProto(absl::string_view filename,
338 const google::protobuf::Message& proto,
339 Options options) {
340 if (options == Defaults()) {
341 if (WriteProtoToASCIIFile(proto, filename)) return absl::OkStatus();
342 }
343 return absl::Status(
344 absl::StatusCode::kInvalidArgument,
345 absl::StrCat("Could not write proto to '", filename, "'."));
346}
347
348absl::Status GetBinaryProto(const absl::string_view filename,
349 google::protobuf::Message* proto, Options options) {
350 std::string str;
351 if (options == Defaults() && ReadFileToString(filename, &str) &&
352 proto->ParseFromString(str)) {
353 return absl::OkStatus();
354 }
355 return absl::Status(
356 absl::StatusCode::kInvalidArgument,
357 absl::StrCat("Could not read proto from '", filename, "'."));
358}
359
360absl::Status SetBinaryProto(absl::string_view filename,
361 const google::protobuf::Message& proto,
362 Options options) {
363 if (options == Defaults()) {
364 if (WriteProtoToFile(proto, filename)) return absl::OkStatus();
365 }
366 return absl::Status(
367 absl::StatusCode::kInvalidArgument,
368 absl::StrCat("Could not write proto to '", filename, "'."));
369}
370
371absl::Status Delete(absl::string_view path, Options options) {
372 if (options == Defaults()) {
373 std::string null_terminated_path = std::string(path);
374 if (remove(null_terminated_path.c_str()) == 0) return absl::OkStatus();
375 }
376 return absl::Status(absl::StatusCode::kInvalidArgument,
377 absl::StrCat("Could not delete '", path, "'."));
378}
379
380absl::Status Exists(absl::string_view path, Options options) {
381 if (options == Defaults()) {
382 std::string null_terminated_path = std::string(path);
383 if (access(null_terminated_path.c_str(), F_OK) == 0) {
384 return absl::OkStatus();
385 }
386 }
387 return absl::Status(absl::StatusCode::kInvalidArgument,
388 absl::StrCat("File '", path, "' does not exist."));
389}
390} // namespace file
IntegerValue size
Definition file.h:30
absl::string_view filename() const
Returns the file name.
Definition file.cc:163
static bool Delete(absl::string_view filename)
Deletes a file.
Definition file.cc:47
bool WriteLine(absl::string_view line)
Writes a string to file and append a "\n".
Definition file.cc:158
size_t Write(const void *buff, size_t size)
Writes "size" bytes of buff to file, buff should be pre-allocated.
Definition file.cc:106
size_t Read(void *buff, size_t size)
Reads "size" bytes to buff from file, buff should be pre-allocated.
Definition file.cc:101
static File * OpenOrDie(absl::string_view filename, absl::string_view mode)
Definition file.cc:110
void WriteOrDie(const void *buff, size_t size)
Definition file.cc:103
static void Init()
Inits internal data structures.
Definition file.cc:167
size_t Size()
Returns file size.
Definition file.cc:57
char * ReadLine(char *output, uint64_t max_length)
Definition file.cc:126
static bool Exists(absl::string_view filename)
Tests if a file exists.
Definition file.cc:52
size_t WriteString(absl::string_view str)
Writes a string to file.
Definition file.cc:154
bool Flush()
Flushes buffer.
Definition file.cc:63
bool Close()
Closes the file.
Definition file.cc:66
int64_t ReadToString(std::string *line, uint64_t max_length)
Definition file.cc:130
void ReadOrDie(void *buff, size_t size)
Definition file.cc:97
bool Open() const
Definition file.cc:165
CpModelProto proto
The output proto.
const std::string name
A name for logging purposes.
absl::Status status
Definition g_gurobi.cc:44
Definition file.cc:169
absl::Status SetTextProto(absl::string_view filename, const google::protobuf::Message &proto, Options options)
Definition file.cc:337
int Options
Definition file.h:107
absl::StatusOr< std::string > GetContents(absl::string_view path, Options options)
Definition file.cc:191
bool WriteProtoToASCIIFile(const google::protobuf::Message &proto, absl::string_view file_name)
Definition file.cc:303
absl::Status Exists(absl::string_view path, Options options)
Definition file.cc:380
absl::Status Open(absl::string_view filename, absl::string_view mode, File **f, Options options)
As of 2016-01, these methods can only be used with flags = file::Defaults().
Definition file.cc:170
absl::Status SetBinaryProto(absl::string_view filename, const google::protobuf::Message &proto, Options options)
Definition file.cc:360
bool ReadFileToString(absl::string_view file_name, std::string *output)
Definition file.cc:252
bool ReadFileToProto(absl::string_view file_name, google::protobuf::Message *proto)
Definition file.cc:269
absl::Status SetContents(absl::string_view filename, absl::string_view contents, Options options)
Definition file.cc:242
bool WriteStringToFile(absl::string_view data, absl::string_view file_name)
Definition file.cc:256
absl::Status WriteString(File *file, absl::string_view contents, Options options)
Definition file.cc:231
void WriteProtoToASCIIFileOrDie(const google::protobuf::Message &proto, absl::string_view file_name)
Definition file.cc:310
bool WriteProtoToFile(const google::protobuf::Message &proto, absl::string_view file_name)
Definition file.cc:315
void ReadFileToProtoOrDie(absl::string_view file_name, google::protobuf::Message *proto)
Definition file.cc:298
void WriteProtoToFileOrDie(const google::protobuf::Message &proto, absl::string_view file_name)
Definition file.cc:322
Options Defaults()
Definition file.h:109
absl::Status Delete(absl::string_view path, Options options)
Definition file.cc:371
absl::Status GetTextProto(absl::string_view filename, google::protobuf::Message *proto, Options options)
Definition file.cc:327
absl::Status GetBinaryProto(const absl::string_view filename, google::protobuf::Message *proto, Options options)
Definition file.cc:348
File * OpenOrDie(absl::string_view filename, absl::string_view mode, Options options)
Definition file.cc:182
int line