osrm-backend/include/storage/tar.hpp
2018-03-26 11:02:03 +00:00

196 lines
5.0 KiB
C++

#ifndef OSRM_STORAGE_TAR_HPP
#define OSRM_STORAGE_TAR_HPP
#include "util/exception.hpp"
#include "util/exception_utils.hpp"
#include "util/fingerprint.hpp"
#include "util/version.hpp"
#include <boost/filesystem/path.hpp>
extern "C" {
#include "microtar.h"
}
namespace osrm
{
namespace storage
{
namespace tar
{
class FileReader
{
public:
enum FingerprintFlag
{
VerifyFingerprint,
HasNoFingerprint
};
FileReader(const boost::filesystem::path &path, FingerprintFlag flag) : path(path)
{
auto ret = mtar_open(&handle, path.c_str(), "r");
if (ret != MTAR_ESUCCESS)
{
throw util::exception(mtar_strerror(ret));
}
if (flag == VerifyFingerprint)
{
ReadAndCheckFingerprint();
}
}
~FileReader() { mtar_close(&handle); }
std::uint64_t ReadElementCount64(const std::string &name)
{
return ReadOne<std::uint64_t>(name + ".meta");
}
template <typename T> T ReadOne(const std::string &name)
{
T tmp;
ReadInto(name, &tmp, 1);
return tmp;
}
template <typename T>
void ReadInto(const std::string &name, T *data, const std::size_t number_of_elements)
{
mtar_header_t header;
auto ret = mtar_find(&handle, name.c_str(), &header);
if (ret != MTAR_ESUCCESS)
{
throw util::exception(name + ": " + mtar_strerror(ret));
}
auto expected_size = sizeof(T) * number_of_elements;
if (header.size != expected_size)
{
throw util::exception(name + ": Datatype size does not match file size.");
}
ret = mtar_read_data(&handle, reinterpret_cast<char *>(data), header.size);
if (ret != MTAR_ESUCCESS)
{
throw util::exception(name + ": Failed reading data: " + mtar_strerror(ret));
}
}
using TarEntry = std::tuple<std::string, std::size_t>;
template <typename OutIter> void List(OutIter out)
{
mtar_header_t header;
while (mtar_read_header(&handle, &header) != MTAR_ENULLRECORD)
{
if (header.type == MTAR_TREG)
{
*out++ = TarEntry{header.name, header.size};
}
mtar_next(&handle);
}
}
private:
bool ReadAndCheckFingerprint()
{
auto loaded_fingerprint = ReadOne<util::FingerPrint>("osrm_fingerprint.meta");
const auto expected_fingerprint = util::FingerPrint::GetValid();
if (!loaded_fingerprint.IsValid())
{
throw util::RuntimeError(path.string(), ErrorCode::InvalidFingerprint, SOURCE_REF);
}
if (!expected_fingerprint.IsDataCompatible(loaded_fingerprint))
{
const std::string fileversion =
std::to_string(loaded_fingerprint.GetMajorVersion()) + "." +
std::to_string(loaded_fingerprint.GetMinorVersion()) + "." +
std::to_string(loaded_fingerprint.GetPatchVersion());
throw util::RuntimeError(std::string(path.string()) + " prepared with OSRM " +
fileversion + " but this is " + OSRM_VERSION,
ErrorCode::IncompatibleFileVersion,
SOURCE_REF);
}
return true;
}
boost::filesystem::path path;
mtar_t handle;
};
class FileWriter
{
public:
enum FingerprintFlag
{
GenerateFingerprint,
HasNoFingerprint
};
FileWriter(const boost::filesystem::path &path, FingerprintFlag flag) : path(path)
{
auto ret = mtar_open(&handle, path.c_str(), "w");
if (ret != MTAR_ESUCCESS)
throw util::exception(mtar_strerror(ret));
if (flag == GenerateFingerprint)
{
WriteFingerprint();
}
}
~FileWriter()
{
mtar_finalize(&handle);
mtar_close(&handle);
}
void WriteElementCount64(const std::string &name, const std::uint64_t count)
{
WriteOne(name + ".meta", count);
}
template <typename T> void WriteOne(const std::string &name, const T &data)
{
WriteFrom(name, &data, 1);
}
template <typename T>
void WriteFrom(const std::string &name, const T *data, const std::size_t number_of_elements)
{
auto number_of_bytes = number_of_elements * sizeof(T);
auto ret = mtar_write_file_header(&handle, name.c_str(), number_of_bytes);
if (ret != MTAR_ESUCCESS)
{
throw util::exception(name + ": Error reading header: " + mtar_strerror(ret));
}
ret = mtar_write_data(&handle, reinterpret_cast<const char *>(data), number_of_bytes);
if (ret != MTAR_ESUCCESS)
{
throw util::exception(name + ": Error reading data : " + mtar_strerror(ret));
}
}
private:
void WriteFingerprint()
{
const auto fingerprint = util::FingerPrint::GetValid();
WriteOne("osrm_fingerprint.meta", fingerprint);
}
boost::filesystem::path path;
mtar_t handle;
};
}
}
}
#endif