39#include "absl/log/check.h"
40#include "absl/log/log.h"
41#include "absl/status/status.h"
42#include "absl/strings/str_cat.h"
43#include "absl/strings/string_view.h"
44#include "google/protobuf/io/tokenizer.h"
45#include "google/protobuf/message.h"
46#include "google/protobuf/text_format.h"
49enum class Format { NORMAL_FILE, GZIP_FILE, BZIP2_FILE };
51static Format GetFormatFromName(absl::string_view name) {
52 const int size = name.size();
53 if (size > 4 && name.substr(size - 3) ==
".gz") {
54 return Format::GZIP_FILE;
55 }
else if (size > 5 && name.substr(size - 4) ==
".bz2") {
56 return Format::BZIP2_FILE;
58 return Format::NORMAL_FILE;
62class CFile :
public File {
64 CFile(FILE* c_file, absl::string_view name) : File(name), f_(c_file) {}
65 ~CFile()
override =
default;
68 size_t Read(
void* buf,
size_t size)
override {
69 return fread(buf, 1, size, f_);
73 size_t Write(
const void* buf,
size_t size)
override {
74 return fwrite(buf, 1, size, f_);
78 absl::Status Close(
int )
override {
83 if (fclose(f_) == 0) {
87 absl::Status(absl::StatusCode::kInvalidArgument,
88 absl::StrCat(
"Could not close file '", name_,
"'")));
95 bool Flush()
override {
return fflush(f_) == 0; }
98 size_t Size()
override {
100 stat(name_.c_str(), &f_stat);
101 return f_stat.st_size;
104 bool Open()
const override {
return f_ !=
nullptr; }
110class GzFile :
public File {
112 GzFile(gzFile gz_file, absl::string_view name) : File(name), f_(gz_file) {}
113 ~GzFile()
override =
default;
116 size_t Read(
void* buf,
size_t size)
override {
return gzread(f_, buf, size); }
119 size_t Write(
const void* buf,
size_t size)
override {
120 return gzwrite(f_, buf, size);
124 absl::Status Close(
int )
override {
129 if (gzclose(f_) == 0) {
133 absl::Status(absl::StatusCode::kInvalidArgument,
134 absl::StrCat(
"Could not close file '", name_,
"'")));
141 bool Flush()
override {
return gzflush(f_, Z_FINISH) == Z_OK; }
144 size_t Size()
override {
146 std::string null_terminated_name = std::string(name_);
148 file = gzopen(null_terminated_name.c_str(),
"rb");
150 file = gzopen(null_terminated_name.c_str(),
"r");
153 LOG(FATAL) <<
"Cannot get the size of '" << name_
154 <<
"': " << strerror(errno);
157 const int kLength = 5 * 1024;
158 unsigned char buffer[kLength];
159 size_t uncompressed_size = 0;
163 bytes_read = gzread(file, buffer, kLength - 1);
164 uncompressed_size += bytes_read;
165 if (bytes_read < kLength - 1) {
169 const char* error_string;
170 error_string = gzerror(file, &err);
172 LOG(FATAL) <<
"Error " << error_string;
178 return uncompressed_size;
181 bool Open()
const override {
return f_ !=
nullptr; }
187class Bz2File :
public File {
189 Bz2File(BZFILE* bz_file, absl::string_view name) : File(name), f_(bz_file) {}
190 ~Bz2File()
override =
default;
193 size_t Read(
void* buf,
size_t size)
override {
194 return BZ2_bzread(f_, buf, size);
198 size_t Write(
const void* buf,
size_t size)
override {
199 return BZ2_bzwrite(f_,
const_cast<void*
>(buf), size);
203 absl::Status Close(
int )
override {
206 return absl::OkStatus();
211 return absl::OkStatus();
215 bool Flush()
override {
return BZ2_bzflush(f_) == 0; }
218 size_t Size()
override {
220 std::string null_terminated_name = std::string(name_);
222 file = BZ2_bzopen(null_terminated_name.c_str(),
"rb");
224 file = BZ2_bzopen(null_terminated_name.c_str(),
"r");
227 LOG(FATAL) <<
"Cannot get the size of '" << name_
228 <<
"': " << strerror(errno);
231 const int kLength = 5 * 1024;
232 unsigned char buffer[kLength];
233 size_t uncompressed_size = 0;
236 bytes_read = BZ2_bzread(file, buffer, kLength - 1);
237 uncompressed_size += bytes_read;
238 if (bytes_read < kLength - 1)
break;
241 return uncompressed_size;
244 bool Open()
const override {
return f_ !=
nullptr; }
256 CHECK(f !=
nullptr) << absl::StrCat(
"Could not open '", file_name,
"'");
261 std::string null_terminated_name = std::string(file_name);
262 std::string null_terminated_mode = std::string(mode);
264 if (null_terminated_mode ==
"r") {
265 null_terminated_mode =
"rb";
266 }
else if (null_terminated_mode ==
"w") {
267 null_terminated_mode =
"wb";
270 const Format format = GetFormatFromName(file_name);
272 case Format::NORMAL_FILE: {
274 fopen(null_terminated_name.c_str(), null_terminated_mode.c_str());
275 if (c_file ==
nullptr)
return nullptr;
276 return new CFile(c_file, file_name);
278 case Format::GZIP_FILE: {
280 gzopen(null_terminated_name.c_str(), null_terminated_mode.c_str());
281 if (!gz_file)
return nullptr;
282 return new GzFile(gz_file, file_name);
284 case Format::BZIP2_FILE: {
285 BZFILE* bz_file = BZ2_bzopen(null_terminated_name.c_str(),
286 null_terminated_mode.c_str());
287 if (!bz_file)
return nullptr;
288 return new Bz2File(bz_file, file_name);
296 CHECK(line !=
nullptr);
299 if (max_length == 0)
return 0;
301 int64_t needed = max_length;
302 int bufsize = (needed < (2 << 20) ? needed : (2 << 20));
304 std::unique_ptr<char[]> buf(
new char[bufsize]);
308 nread =
Read(buf.get(), (bufsize < needed ? bufsize : needed));
310 line->append(buf.get(), nread);
316 return (nread >= 0 ?
static_cast<int64_t
>(line->size()) : -1);
320 return Write(str.data(), str.size());
328absl::Status
Open(absl::string_view file_name, absl::string_view mode,
File** f,
333 return absl::OkStatus();
336 return absl::Status(absl::StatusCode::kInvalidArgument,
337 absl::StrCat(
"Could not open '", file_name,
"'"));
345 CHECK(f !=
nullptr) << absl::StrCat(
"Could not open '", file_name,
"'");
351 std::string contents;
352 absl::Status status =
GetContents(path, &contents, options);
359absl::Status
GetContents(absl::string_view file_name, std::string* output,
364 if (!status.ok())
return status;
366 const int64_t size =
file->Size();
367 if (
file->ReadToString(output, size) == size) {
368 status.Update(
file->Close(options));
372 file->Close(options).IgnoreError();
374 return absl::Status(absl::StatusCode::kInvalidArgument,
375 absl::StrCat(
"Could not read from '", file_name,
"'."));
381 file->Write(contents.data(), contents.size()) == contents.size()) {
382 return absl::OkStatus();
385 absl::StatusCode::kInvalidArgument,
386 absl::StrCat(
"Could not write ", contents.size(),
" bytes"));
390 absl::string_view contents,
Options options) {
394 if (!status.ok())
return status;
396 status.Update(
file->Close(options));
401class NoOpErrorCollector :
public google::protobuf::io::ErrorCollector {
403 ~NoOpErrorCollector()
override =
default;
404 void RecordError(
int ,
int ,
405 absl::string_view )
override {}
410 google::protobuf::Message* proto,
Options options) {
414 VLOG(1) <<
"Could not read '" << file_name <<
"'";
416 absl::StatusCode::kInvalidArgument,
417 absl::StrCat(
"Could not read proto from '", file_name,
"'."));
426 NoOpErrorCollector error_collector;
427 google::protobuf::TextFormat::Parser parser;
428 parser.RecordErrorsTo(&error_collector);
430 if (parser.ParseFromString(str, proto)) {
431 return absl::OkStatus();
434 if (proto->ParseFromString(str)) {
435 return absl::OkStatus();
440 google::protobuf::TextFormat::ParseFromString(str, proto);
441 VLOG(1) <<
"Could not parse contents of '" << file_name <<
"'";
444 absl::StatusCode::kInvalidArgument,
445 absl::StrCat(
"Could not read proto from '", file_name,
"'."));
449 const google::protobuf::Message& proto,
452 std::string proto_string;
453 if (google::protobuf::TextFormat::PrintToString(proto, &proto_string) &&
455 return absl::OkStatus();
459 absl::StatusCode::kInvalidArgument,
460 absl::StrCat(
"Could not write proto to '", file_name,
"'."));
464 google::protobuf::Message* proto,
Options options) {
468 proto->ParseFromString(str)) {
469 return absl::OkStatus();
472 absl::StatusCode::kInvalidArgument,
473 absl::StrCat(
"Could not read proto from '", file_name,
"'."));
477 const google::protobuf::Message& proto,
480 std::string proto_string;
481 if (proto.AppendToString(&proto_string) &&
483 return absl::OkStatus();
487 absl::StatusCode::kInvalidArgument,
488 absl::StrCat(
"Could not write proto to '", file_name,
"'."));
493 std::string null_terminated_path = std::string(path);
494 if (remove(null_terminated_path.c_str()) == 0)
return absl::OkStatus();
496 return absl::Status(absl::StatusCode::kInvalidArgument,
497 absl::StrCat(
"Could not delete '", path,
"'."));
502 std::string null_terminated_path = std::string(path);
503 if (access(null_terminated_path.c_str(), F_OK) == 0) {
504 return absl::OkStatus();
507 return absl::Status(absl::StatusCode::kInvalidArgument,
508 absl::StrCat(
"File '", path,
"' does not exist."));
absl::string_view filename() const
virtual size_t Write(const void *buf, size_t size)=0
virtual size_t Read(void *buf, size_t size)=0
static File * OpenOrDie(absl::string_view file_name, absl::string_view mode)
size_t WriteString(absl::string_view str)
File(absl::string_view name)
virtual bool Open() const =0
int64_t ReadToString(std::string *line, uint64_t max_length)
absl::StatusOr< std::string > GetContents(absl::string_view path, Options options)
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)
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)
absl::Status SetContents(absl::string_view file_name, absl::string_view contents, Options options)