Program Listing for File internal.hpp
↰ Return to documentation for file (include/3rdparty/mcap/internal.hpp)
#pragma once
#include <cstring>
#include "types.hpp"
// Do not compile on systems with non-8-bit bytes
static_assert(std::numeric_limits<unsigned char>::digits == 8);
namespace mcap {
namespace internal {
constexpr uint64_t MinHeaderLength = /* magic bytes */ sizeof(Magic) +
/* opcode */ 1 +
/* record length */ 8 +
/* profile length */ 4 +
/* library length */ 4;
constexpr uint64_t FooterLength = /* opcode */ 1 +
/* record length */ 8 +
/* summary start */ 8 +
/* summary offset start */ 8 +
/* summary crc */ 4 +
/* magic bytes */ sizeof(Magic);
inline std::string ToHex(uint8_t byte) {
std::string result{2, '\0'};
result[0] = "0123456789ABCDEF"[(uint8_t(byte) >> 4) & 0x0F];
result[1] = "0123456789ABCDEF"[uint8_t(byte) & 0x0F];
return result;
}
inline std::string ToHex(std::byte byte) {
return ToHex(uint8_t(byte));
}
inline std::string to_string(const std::string& arg) {
return arg;
}
inline std::string to_string(std::string_view arg) {
return std::string(arg);
}
inline std::string to_string(const char* arg) {
return std::string(arg);
}
template <typename... T>
[[nodiscard]] inline std::string StrCat(T&&... args) {
using mcap::internal::to_string;
using std::to_string;
return ("" + ... + to_string(std::forward<T>(args)));
}
inline uint32_t KeyValueMapSize(const KeyValueMap& map) {
size_t size = 0;
for(const auto& [key, value] : map) {
size += 4 + key.size() + 4 + value.size();
}
return (uint32_t)(size);
}
inline const std::string CompressionString(Compression compression) {
switch(compression) {
case Compression::None:
default:
return std::string{};
case Compression::Lz4:
return "lz4";
case Compression::Zstd:
return "zstd";
}
}
inline uint16_t ParseUint16(const std::byte* data) {
return uint16_t(data[0]) | (uint16_t(data[1]) << 8);
}
inline uint32_t ParseUint32(const std::byte* data) {
return uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16) | (uint32_t(data[3]) << 24);
}
inline Status ParseUint32(const std::byte* data, uint64_t maxSize, uint32_t* output) {
if(maxSize < 4) {
const auto msg = StrCat("cannot read uint32 from ", maxSize, " bytes");
return Status{StatusCode::InvalidRecord, msg};
}
*output = ParseUint32(data);
return StatusCode::Success;
}
inline uint64_t ParseUint64(const std::byte* data) {
return uint64_t(data[0]) | (uint64_t(data[1]) << 8) | (uint64_t(data[2]) << 16) | (uint64_t(data[3]) << 24) | (uint64_t(data[4]) << 32)
| (uint64_t(data[5]) << 40) | (uint64_t(data[6]) << 48) | (uint64_t(data[7]) << 56);
}
inline Status ParseUint64(const std::byte* data, uint64_t maxSize, uint64_t* output) {
if(maxSize < 8) {
const auto msg = StrCat("cannot read uint64 from ", maxSize, " bytes");
return Status{StatusCode::InvalidRecord, msg};
}
*output = ParseUint64(data);
return StatusCode::Success;
}
inline Status ParseStringView(const std::byte* data, uint64_t maxSize, std::string_view* output) {
uint32_t size = 0;
if(auto status = ParseUint32(data, maxSize, &size); !status.ok()) {
const auto msg = StrCat("cannot read string size: ", status.message);
return Status{StatusCode::InvalidRecord, msg};
}
if(uint64_t(size) > (maxSize - 4)) {
const auto msg = StrCat("string size ", size, " exceeds remaining bytes ", (maxSize - 4));
return Status(StatusCode::InvalidRecord, msg);
}
*output = std::string_view(reinterpret_cast<const char*>(data + 4), size);
return StatusCode::Success;
}
inline Status ParseString(const std::byte* data, uint64_t maxSize, std::string* output) {
uint32_t size = 0;
if(auto status = ParseUint32(data, maxSize, &size); !status.ok()) {
return status;
}
if(uint64_t(size) > (maxSize - 4)) {
const auto msg = StrCat("string size ", size, " exceeds remaining bytes ", (maxSize - 4));
return Status(StatusCode::InvalidRecord, msg);
}
*output = std::string(reinterpret_cast<const char*>(data + 4), size);
return StatusCode::Success;
}
inline Status ParseByteArray(const std::byte* data, uint64_t maxSize, ByteArray* output) {
uint32_t size = 0;
if(auto status = ParseUint32(data, maxSize, &size); !status.ok()) {
return status;
}
if(uint64_t(size) > (maxSize - 4)) {
const auto msg = StrCat("byte array size ", size, " exceeds remaining bytes ", (maxSize - 4));
return Status(StatusCode::InvalidRecord, msg);
}
output->resize(size);
std::memcpy(output->data(), data + 4, size);
return StatusCode::Success;
}
inline Status ParseKeyValueMap(const std::byte* data, uint64_t maxSize, KeyValueMap* output) {
uint32_t sizeInBytes = 0;
if(auto status = ParseUint32(data, maxSize, &sizeInBytes); !status.ok()) {
return status;
}
if(sizeInBytes > (maxSize - 4)) {
const auto msg = StrCat("key-value map size ", sizeInBytes, " exceeds remaining bytes ", (maxSize - 4));
return Status(StatusCode::InvalidRecord, msg);
}
// Account for the byte size prefix in sizeInBytes to make the bounds checking
// below simpler
sizeInBytes += 4;
output->clear();
uint64_t pos = 4;
while(pos < sizeInBytes) {
std::string_view key;
if(auto status = ParseStringView(data + pos, sizeInBytes - pos, &key); !status.ok()) {
const auto msg = StrCat("cannot read key-value map key at pos ", pos, ": ", status.message);
return Status{StatusCode::InvalidRecord, msg};
}
pos += 4 + key.size();
std::string_view value;
if(auto status = ParseStringView(data + pos, sizeInBytes - pos, &value); !status.ok()) {
const auto msg = StrCat("cannot read key-value map value for key \"", key, "\" at pos ", pos, ": ", status.message);
return Status{StatusCode::InvalidRecord, msg};
}
pos += 4 + value.size();
output->emplace(key, value);
}
return StatusCode::Success;
}
inline std::string MagicToHex(const std::byte* data) {
return internal::ToHex(data[0]) + internal::ToHex(data[1]) + internal::ToHex(data[2]) + internal::ToHex(data[3]) + internal::ToHex(data[4])
+ internal::ToHex(data[5]) + internal::ToHex(data[6]) + internal::ToHex(data[7]);
}
} // namespace internal
} // namespace mcap