36#include "absl/log/check.h"
37#include "absl/log/log.h"
38#include "absl/status/status.h"
39#include "absl/strings/str_cat.h"
40#include "absl/strings/string_view.h"
42#include "google/protobuf/io/tokenizer.h"
43#include "google/protobuf/message.h"
44#include "google/protobuf/text_format.h"
48enum class Format { NORMAL_FILE, GZIP_FILE, BZIP2_FILE };
50static Format GetFormatFromName(absl::string_view name) {
51 const int size = name.size();
52 if (size > 4 && name.substr(size - 3) ==
".gz") {
53 return Format::GZIP_FILE;
54 }
else if (size > 5 && name.substr(size - 4) ==
".bz2") {
55 return Format::BZIP2_FILE;
57 return Format::NORMAL_FILE;
61class CFile :
public File {
63 CFile(FILE* c_file, absl::string_view name) : File(name), f_(c_file) {}
64 ~CFile()
override =
default;
67 size_t Read(
void* buf,
size_t size)
override {
68 return fread(buf, 1, size, f_);
72 size_t Write(
const void* buf,
size_t size)
override {
73 return fwrite(buf, 1, size, f_);
77 absl::Status Close(
int )
override {
82 if (fclose(f_) == 0) {
86 absl::Status(absl::StatusCode::kInvalidArgument,
87 absl::StrCat(
"Could not close file '", name_,
"'")));
94 bool Flush()
override {
return fflush(f_) == 0; }
97 size_t Size()
override {
99 stat(name_.c_str(), &f_stat);
100 return f_stat.st_size;
103 bool Open()
const override {
return f_ !=
nullptr; }
109class GzFile :
public File {
111 GzFile(gzFile gz_file, absl::string_view name) : File(name), f_(gz_file) {}
112 ~GzFile()
override =
default;
115 size_t Read(
void* buf,
size_t size)
override {
return gzread(f_, buf, size); }
118 size_t Write(
const void* buf,
size_t size)
override {
119 return gzwrite(f_, buf, size);
123 absl::Status Close(
int )
override {
128 if (gzclose(f_) == 0) {
132 absl::Status(absl::StatusCode::kInvalidArgument,
133 absl::StrCat(
"Could not close file '", name_,
"'")));
140 bool Flush()
override {
return gzflush(f_, Z_FINISH) == Z_OK; }
143 size_t Size()
override {
145 std::string null_terminated_name = std::string(name_);
147 file = gzopen(null_terminated_name.c_str(),
"rb");
149 file = gzopen(null_terminated_name.c_str(),
"r");
152 LOG(FATAL) <<
"Cannot get the size of '" << name_
153 <<
"': " << strerror(errno);
156 const int kLength = 5 * 1024;
157 unsigned char buffer[kLength];
158 size_t uncompressed_size = 0;
162 bytes_read = gzread(file, buffer, kLength - 1);
163 uncompressed_size += bytes_read;
164 if (bytes_read < kLength - 1) {
168 const char* error_string;
169 error_string = gzerror(file, &err);
171 LOG(FATAL) <<
"Error " << error_string;
177 return uncompressed_size;
180 bool Open()
const override {
return f_ !=
nullptr; }
186class Bz2File :
public File {
188 Bz2File(BZFILE* bz_file, absl::string_view name) : File(name), f_(bz_file) {}
189 ~Bz2File()
override =
default;
192 size_t Read(
void* buf,
size_t size)
override {
193 return BZ2_bzread(f_, buf, size);
197 size_t Write(
const void* buf,
size_t size)
override {
198 return BZ2_bzwrite(f_,
const_cast<void*
>(buf), size);
202 absl::Status Close(
int )
override {
205 return absl::OkStatus();
210 return absl::OkStatus();
214 bool Flush()
override {
return BZ2_bzflush(f_) == 0; }
217 size_t Size()
override {
219 std::string null_terminated_name = std::string(name_);
221 file = BZ2_bzopen(null_terminated_name.c_str(),
"rb");
223 file = BZ2_bzopen(null_terminated_name.c_str(),
"r");
226 LOG(FATAL) <<
"Cannot get the size of '" << name_
227 <<
"': " << strerror(errno);
230 const int kLength = 5 * 1024;
231 unsigned char buffer[kLength];
232 size_t uncompressed_size = 0;
235 bytes_read = BZ2_bzread(file, buffer, kLength - 1);
236 uncompressed_size += bytes_read;
237 if (bytes_read < kLength - 1)
break;
240 return uncompressed_size;
243 bool Open()
const override {
return f_ !=
nullptr; }
255 CHECK(f !=
nullptr) << absl::StrCat(
"Could not open '", file_name,
"'");
260 std::string null_terminated_name = std::string(file_name);
261 std::string null_terminated_mode = std::string(mode);
263 if (null_terminated_mode ==
"r") {
264 null_terminated_mode =
"rb";
265 }
else if (null_terminated_mode ==
"w") {
266 null_terminated_mode =
"wb";
269 const Format format = GetFormatFromName(file_name);
271 case Format::NORMAL_FILE: {
273 fopen(null_terminated_name.c_str(), null_terminated_mode.c_str());
274 if (c_file ==
nullptr)
return nullptr;
275 return new CFile(c_file, file_name);
277 case Format::GZIP_FILE: {
279 gzopen(null_terminated_name.c_str(), null_terminated_mode.c_str());
280 if (!gz_file)
return nullptr;
281 return new GzFile(gz_file, file_name);
283 case Format::BZIP2_FILE: {
284 BZFILE* bz_file = BZ2_bzopen(null_terminated_name.c_str(),
285 null_terminated_mode.c_str());
286 if (!bz_file)
return nullptr;
287 return new Bz2File(bz_file, file_name);
295 CHECK(line !=
nullptr);
298 if (max_length == 0)
return 0;
300 int64_t needed = max_length;
301 int bufsize = (needed < (2 << 20) ? needed : (2 << 20));
303 std::unique_ptr<char[]> buf(
new char[bufsize]);
307 nread =
Read(buf.get(), (bufsize < needed ? bufsize : needed));
309 line->append(buf.get(), nread);
315 return (nread >= 0 ?
static_cast<int64_t
>(line->size()) : -1);
319 return Write(str.data(), str.size());
327absl::Status
Open(absl::string_view file_name, absl::string_view mode,
File** f,
332 return absl::OkStatus();
335 return absl::Status(absl::StatusCode::kInvalidArgument,
336 absl::StrCat(
"Could not open '", file_name,
"'"));
344 CHECK(f !=
nullptr) << absl::StrCat(
"Could not open '", file_name,
"'");
350 std::string contents;
351 absl::Status status =
GetContents(path, &contents, options);
358absl::Status
GetContents(absl::string_view file_name, std::string* output,
363 if (!status.ok())
return status;
365 const int64_t size =
file->Size();
366 if (
file->ReadToString(output, size) == size) {
367 status.Update(
file->Close(options));
371 file->Close(options).IgnoreError();
373 return absl::Status(absl::StatusCode::kInvalidArgument,
374 absl::StrCat(
"Could not read from '", file_name,
"'."));
380 file->Write(contents.data(), contents.size()) == contents.size()) {
381 return absl::OkStatus();
384 absl::StatusCode::kInvalidArgument,
385 absl::StrCat(
"Could not write ", contents.size(),
" bytes"));
389 absl::string_view contents,
Options options) {
393 if (!status.ok())
return status;
395 status.Update(
file->Close(options));
400class NoOpErrorCollector :
public google::protobuf::io::ErrorCollector {
402 ~NoOpErrorCollector()
override =
default;
403 void RecordError(
int ,
int ,
404 absl::string_view )
override {}
409 google::protobuf::Message* proto,
Options options) {
413 VLOG(1) <<
"Could not read '" << file_name <<
"'";
415 absl::StatusCode::kInvalidArgument,
416 absl::StrCat(
"Could not read proto from '", file_name,
"'."));
425 NoOpErrorCollector error_collector;
426 google::protobuf::TextFormat::Parser parser;
427 parser.RecordErrorsTo(&error_collector);
429 if (parser.ParseFromString(str, proto)) {
430 return absl::OkStatus();
433 if (proto->ParseFromString(str)) {
434 return absl::OkStatus();
439 google::protobuf::TextFormat::ParseFromString(str, proto);
440 VLOG(1) <<
"Could not parse contents of '" << file_name <<
"'";
443 absl::StatusCode::kInvalidArgument,
444 absl::StrCat(
"Could not read proto from '", file_name,
"'."));
448 const google::protobuf::Message& proto,
451 std::string proto_string;
452 if (google::protobuf::TextFormat::PrintToString(proto, &proto_string) &&
454 return absl::OkStatus();
458 absl::StatusCode::kInvalidArgument,
459 absl::StrCat(
"Could not write proto to '", file_name,
"'."));
463 google::protobuf::Message* proto,
Options options) {
467 proto->ParseFromString(str)) {
468 return absl::OkStatus();
471 absl::StatusCode::kInvalidArgument,
472 absl::StrCat(
"Could not read proto from '", file_name,
"'."));
476 const google::protobuf::Message& proto,
479 std::string proto_string;
480 if (proto.AppendToString(&proto_string) &&
482 return absl::OkStatus();
486 absl::StatusCode::kInvalidArgument,
487 absl::StrCat(
"Could not write proto to '", file_name,
"'."));
492 std::string null_terminated_path = std::string(path);
493 if (remove(null_terminated_path.c_str()) == 0)
return absl::OkStatus();
495 return absl::Status(absl::StatusCode::kInvalidArgument,
496 absl::StrCat(
"Could not delete '", path,
"'."));
501 std::string null_terminated_path = std::string(path);
502 if (access(null_terminated_path.c_str(), F_OK) == 0) {
503 return absl::OkStatus();
506 return absl::Status(absl::StatusCode::kInvalidArgument,
507 absl::StrCat(
"File '", path,
"' does not exist."));
absl::string_view filename() const
Returns the file name.
virtual size_t Write(const void *buf, size_t size)=0
Writes "size" bytes of buf to file, buff should be pre-allocated.
virtual size_t Read(void *buf, size_t size)=0
Reads "size" bytes to buf from file, buff should be pre-allocated.
static void Init()
Inits internal data structures.
static File * OpenOrDie(absl::string_view file_name, absl::string_view mode)
size_t WriteString(absl::string_view str)
Writes a string to file.
File(absl::string_view name)
virtual bool Open() const =0
Returns whether the file is currently open.
int64_t ReadToString(std::string *line, uint64_t max_length)
absl::StatusOr< std::string > GetContents(absl::string_view path, Options options)
-— Content API -—
absl::Status Exists(absl::string_view path, Options options)
absl::Status SetBinaryProto(absl::string_view file_name, const google::protobuf::Message &proto, Options options)
absl::Status SetTextProto(absl::string_view file_name, const google::protobuf::Message &proto, Options options)
absl::Status GetTextProto(absl::string_view file_name, google::protobuf::Message *proto, Options options)
-— Protobuf API -—
File * OpenOrDie(absl::string_view file_name, absl::string_view mode, Options options)
absl::Status WriteString(File *file, absl::string_view contents, Options options)
absl::Status GetBinaryProto(const absl::string_view file_name, google::protobuf::Message *proto, Options options)
absl::Status Delete(absl::string_view path, Options options)
absl::Status Open(absl::string_view file_name, absl::string_view mode, File **f, Options options)
As of 2016-01, these methods can only be used with flags = file::Defaults().
absl::Status SetContents(absl::string_view file_name, absl::string_view contents, Options options)