cmvr-es/cmvr-es/common/io/proto_file_io.h

147 lines
4.7 KiB
C++

#pragma once
#include "common/base/logging/logger.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/text_format.h"
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>
class ProtoMessageIo
{
public:
// -------------------- Binary (protobuf wire format) --------------------
static bool setProtoToBinaryFile(const google::protobuf::Message& message,
const std::string& fileName)
{
std::ofstream output(fileName, std::ios::out | std::ios::trunc | std::ios::binary);
if (!output.good())
{
CMVR_LOG(WARNING) << "Failed to open file for binary write: " << fileName;
return false;
}
if (!message.SerializePartialToOstream(&output))
{
CMVR_LOG(WARNING) << "Failed to serialize proto to binary file: " << fileName;
return false;
}
CMVR_LOG(INFO) << "Successfully wrote binary proto file: " << fileName;
return true;
}
static bool getProtoFromBinaryFile(const std::string& fileName,
google::protobuf::Message* message)
{
if (message == nullptr)
{
CMVR_LOG(ERROR) << "Null message pointer when reading binary proto file: " << fileName;
return false;
}
std::ifstream input(fileName, std::ios::in | std::ios::binary);
if (!input.good())
{
CMVR_LOG(WARNING) << "Failed to open file for binary read: " << fileName;
return false;
}
if (!message->ParseFromIstream(&input))
{
CMVR_LOG(WARNING) << "Failed to parse binary proto file: " << fileName;
return false;
}
return true;
}
// -------------------- ASCII (TextFormat) --------------------
static bool setProtoToAsciiFile(const google::protobuf::Message& message,
const std::string& fileName)
{
// 0600: owner read/write
const int fd = ::open(fileName.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0)
{
CMVR_LOG(WARNING) << "Failed to open file for ASCII write: " << fileName
<< ": " << std::strerror(errno);
return false;
}
const bool ok = setProtoToAsciiFile(message, fd);
if (ok)
CMVR_LOG(INFO) << "Successfully wrote ASCII proto file: " << fileName;
else
CMVR_LOG(WARNING) << "Failed to write ASCII proto file: " << fileName;
return ok;
}
static bool setProtoToAsciiFile(const google::protobuf::Message& message,
int fileDescriptor)
{
using google::protobuf::TextFormat;
using google::protobuf::io::FileOutputStream;
if (fileDescriptor < 0)
{
CMVR_LOG(WARNING) << "Invalid file descriptor for ASCII write.";
return false;
}
google::protobuf::io::FileOutputStream output(fileDescriptor);
output.SetCloseOnDelete(true); // avoid fd leak; also avoid double-close elsewhere
bool ok = TextFormat::Print(message, &output);
ok = ok && output.Flush();
return ok;
}
static bool getProtoFromAsciiFile(const std::string& fileName,
google::protobuf::Message* message,
bool isOptional = false)
{
using google::protobuf::TextFormat;
using google::protobuf::io::FileInputStream;
if (message == nullptr)
{
CMVR_LOG(ERROR) << "Null message pointer when reading ASCII proto file: " << fileName;
return false;
}
const int fd = ::open(fileName.c_str(), O_RDONLY);
if (fd < 0)
{
if (isOptional) {
CMVR_LOG(INFO) << "Optional ASCII proto file not found/openable: " << fileName
<< ": " << std::strerror(errno);
} else {
CMVR_LOG(ERROR) << "Failed to open ASCII proto file: " << fileName
<< ": " << std::strerror(errno);
}
return false;
}
FileInputStream input(fd);
input.SetCloseOnDelete(true);
const bool ok = TextFormat::Parse(&input, message);
if (!ok)
{
if (isOptional)
CMVR_LOG(INFO) << "Failed to parse optional ASCII proto file: " << fileName;
else
CMVR_LOG(ERROR) << "Failed to parse ASCII proto file: " << fileName;
}
return ok;
}
};