Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpp: add build-time flags to make compression dependencies optional #1013

Merged
merged 7 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cpp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ bench-host:

.PHONY: test
test: dev-image
docker run -t --rm -v $(CURDIR):/src mcap_cpp_dev ./test/build/Debug/bin/unit-tests
docker run -t --rm -v $(CURDIR):/src mcap_cpp_dev make test-host

.PHONY: test-host
test-host:
./test/build/Debug/bin/unit-tests
./test/build/Debug/bin/unit-tests-nocompress

.PHONY: hdoc-build
hdoc-build:
Expand Down
3 changes: 3 additions & 0 deletions cpp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ following dependencies:
- [lz4](https://lz4.github.io/lz4/) (tested with [lz4/1.9.3](https://conan.io/center/lz4))
- [zstd](https://facebook.github.io/zstd/) (tested with [zstd/1.5.2](https://conan.io/center/zstd))

If your project does not need `lz4` or `zstd` support, you can optionally disable these by defining
`MCAP_COMPRESSION_NO_LZ4` or `MCAP_COMPRESSION_NO_ZSTD` respectively.

To simplify installation of dependencies, the [Conan](https://conan.io/) package
manager can be used with the included
[conanfile.py](https://github.com/foxglove/mcap/blob/main/cpp/mcap/conanfile.py).
Expand Down
2 changes: 1 addition & 1 deletion cpp/bench/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class McapBenchmarksConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "cmake"
requires = "benchmark/1.7.0", "mcap/1.2.1"
requires = "benchmark/1.7.0", "mcap/1.3.0"

def build(self):
cmake = CMake(self)
Expand Down
2 changes: 1 addition & 1 deletion cpp/build-docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -e

conan config init

conan editable add ./mcap mcap/1.2.1
conan editable add ./mcap mcap/1.3.0
conan install docs --install-folder docs/build/Release \
-s compiler.cppstd=17 -s build_type=Release --build missing

Expand Down
2 changes: 1 addition & 1 deletion cpp/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -e

conan config init

conan editable add ./mcap mcap/1.2.1
conan editable add ./mcap mcap/1.3.0
conan install test --install-folder test/build/Debug \
-s compiler.cppstd=17 -s build_type=Debug --build missing

Expand Down
2 changes: 1 addition & 1 deletion cpp/docs/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class McapDocsConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "cmake"
requires = "mcap/1.2.1"
requires = "mcap/1.3.0"

def build(self):
cmake = CMake(self)
Expand Down
2 changes: 1 addition & 1 deletion cpp/examples/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class McapExamplesConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "cmake"
requires = [
"mcap/1.2.1",
"mcap/1.3.0",
"protobuf/3.21.1",
"nlohmann_json/3.10.5",
"catch2/2.13.8",
Expand Down
2 changes: 1 addition & 1 deletion cpp/mcap/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class McapConan(ConanFile):
name = "mcap"
version = "1.2.1"
version = "1.3.0"
url = "https://github.com/foxglove/mcap"
homepage = "https://github.com/foxglove/mcap"
description = "A C++ implementation of the MCAP file format"
Expand Down
4 changes: 4 additions & 0 deletions cpp/mcap/include/mcap/errors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum class StatusCode {
DecompressionFailed,
DecompressionSizeMismatch,
UnrecognizedCompression,
UnsupportedCompression,
james-rms marked this conversation as resolved.
Show resolved Hide resolved
OpenFailed,
MissingStatistics,
InvalidMessageReadOptions,
Expand Down Expand Up @@ -86,6 +87,9 @@ struct [[nodiscard]] Status {
case StatusCode::UnrecognizedCompression:
message = "unrecognized compression";
break;
case StatusCode::UnsupportedCompression:
message = "unsupported compression";
break;
case StatusCode::OpenFailed:
message = "open failed";
break;
Expand Down
10 changes: 10 additions & 0 deletions cpp/mcap/include/mcap/reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class MCAP_PUBLIC BufferReader final : public ICompressedReader {
uint64_t size_;
};

#ifndef MCAP_COMPRESSION_NO_ZSTD
/**
* @brief ICompressedReader implementation that decompresses Zstandard
* (https://facebook.github.io/zstd/) data.
Expand Down Expand Up @@ -183,7 +184,9 @@ class MCAP_PUBLIC ZStdReader final : public ICompressedReader {
Status status_;
ByteArray uncompressedData_;
};
#endif

#ifndef MCAP_COMPRESSION_NO_LZ4
/**
* @brief ICompressedReader implementation that decompresses LZ4
* (https://lz4.github.io/lz4/) data.
Expand Down Expand Up @@ -222,6 +225,7 @@ class MCAP_PUBLIC LZ4Reader final : public ICompressedReader {
uint64_t compressedSize_;
uint64_t uncompressedSize_;
};
#endif

struct LinearMessageView;

Expand Down Expand Up @@ -539,8 +543,12 @@ struct MCAP_PUBLIC TypedChunkReader {
RecordReader reader_;
Status status_;
BufferReader uncompressedReader_;
#ifndef MCAP_COMPRESSION_NO_LZ4
LZ4Reader lz4Reader_;
#endif
#ifndef MCAP_COMPRESSION_NO_ZSTD
ZStdReader zstdReader_;
#endif
};

/**
Expand Down Expand Up @@ -627,7 +635,9 @@ struct MCAP_PUBLIC IndexedMessageReader {
Status status_;
McapReader& mcapReader_;
RecordReader recordReader_;
#ifndef MCAP_COMPRESSION_NO_LZ4
LZ4Reader lz4Reader_;
#endif
ReadMessageOptions options_;
std::unordered_set<ChannelId> selectedChannels_;
std::function<void(const Message&, RecordOffset)> onMessage_;
Expand Down
67 changes: 51 additions & 16 deletions cpp/mcap/include/mcap/reader.inl
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#include "internal.hpp"
#include <algorithm>
#include <cassert>
#include <lz4frame.h>
#include <zstd.h>
#include <zstd_errors.h>
#ifndef MCAP_COMPRESSION_NO_LZ4
# include <lz4frame.h>
#endif
#ifndef MCAP_COMPRESSION_NO_ZSTD
# include <zstd.h>
# include <zstd_errors.h>
#endif

namespace mcap {

Expand Down Expand Up @@ -119,6 +123,7 @@ uint64_t FileStreamReader::read(std::byte** output, uint64_t offset, uint64_t si

// LZ4Reader ///////////////////////////////////////////////////////////////////

#ifndef MCAP_COMPRESSION_NO_LZ4
LZ4Reader::LZ4Reader() {
const LZ4F_errorCode_t err =
LZ4F_createDecompressionContext((LZ4F_dctx**)&decompressionContext_, LZ4F_VERSION);
Expand Down Expand Up @@ -206,9 +211,11 @@ Status LZ4Reader::decompressAll(const std::byte* data, uint64_t compressedSize,
}
return result;
}
#endif

// ZStdReader //////////////////////////////////////////////////////////////////

#ifndef MCAP_COMPRESSION_NO_ZSTD
void ZStdReader::reset(const std::byte* data, uint64_t size, uint64_t uncompressedSize) {
status_ = DecompressAll(data, size, uncompressedSize, &uncompressedData_);
}
Expand Down Expand Up @@ -255,6 +262,7 @@ Status ZStdReader::DecompressAll(const std::byte* data, uint64_t compressedSize,
}
return result;
}
#endif

// McapReader //////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -1251,10 +1259,27 @@ TypedChunkReader::TypedChunkReader()
, status_{StatusCode::Success} {}

void TypedChunkReader::reset(const Chunk& chunk, Compression compression) {
ICompressedReader* decompressor =
(compression == Compression::None) ? static_cast<ICompressedReader*>(&uncompressedReader_)
: (compression == Compression::Lz4) ? static_cast<ICompressedReader*>(&lz4Reader_)
: static_cast<ICompressedReader*>(&zstdReader_);
ICompressedReader* decompressor;

switch (compression) {
#ifndef MCAP_COMPRESSION_NO_LZ4
case Compression::Lz4:
decompressor = static_cast<ICompressedReader*>(&lz4Reader_);
break;
#endif
#ifndef MCAP_COMPRESSION_NO_ZSTD
case Compression::Zstd:
decompressor = static_cast<ICompressedReader*>(&zstdReader_);
break;
#endif
case Compression::None:
decompressor = static_cast<ICompressedReader*>(&uncompressedReader_);
break;
default:
status_ = Status(StatusCode::UnsupportedCompression,
internal::StrCat("unsupported compression: ", chunk.compression));
return;
}
decompressor->reset(chunk.records, chunk.compressedSize, chunk.uncompressedSize);
reader_.reset(*decompressor, 0, decompressor->size());
status_ = decompressor->status();
Expand Down Expand Up @@ -1604,7 +1629,8 @@ LinearMessageView::Iterator::Iterator(LinearMessageView& view)
}
}

LinearMessageView::Iterator::Impl::Impl(LinearMessageView& view) : view_(view) {
LinearMessageView::Iterator::Impl::Impl(LinearMessageView& view)
: view_(view) {
auto dataStart = view.dataStart_;
auto dataEnd = view.dataEnd_;
auto readMessageOptions = view.readMessageOptions_;
Expand Down Expand Up @@ -1658,16 +1684,18 @@ void LinearMessageView::Iterator::Impl::onMessage(const Message& message, Record

auto& channel = *maybeChannel;
// make sure the message is on the right topic
if (view_.readMessageOptions_.topicFilter && !view_.readMessageOptions_.topicFilter(channel.topic)) {
if (view_.readMessageOptions_.topicFilter &&
!view_.readMessageOptions_.topicFilter(channel.topic)) {
return;
}
SchemaPtr maybeSchema;
if (channel.schemaId != 0) {
maybeSchema = view_.mcapReader_.schema(channel.schemaId);
if (!maybeSchema) {
view_.onProblem_(Status{StatusCode::InvalidSchemaId,
internal::StrCat("channel ", channel.id, " (", channel.topic,
") references missing schema id ", channel.schemaId)});
view_.onProblem_(
Status{StatusCode::InvalidSchemaId,
internal::StrCat("channel ", channel.id, " (", channel.topic,
") references missing schema id ", channel.schemaId)});
return;
}
}
Expand Down Expand Up @@ -1840,14 +1868,21 @@ void IndexedMessageReader::decompressChunk(const Chunk& chunk,
if (*compression == Compression::None) {
slot.decompressedChunk.insert(slot.decompressedChunk.end(), &chunk.records[0],
&chunk.records[chunk.uncompressedSize]);
} else if (*compression == Compression::Lz4) {
}
#ifndef MCAP_COMPRESSION_NO_LZ4
else if (*compression == Compression::Lz4) {
status_ = lz4Reader_.decompressAll(chunk.records, chunk.compressedSize, chunk.uncompressedSize,
&slot.decompressedChunk);
} else if (*compression == Compression::Zstd) {
}
#endif
#ifndef MCAP_COMPRESSION_NO_ZSTD
else if (*compression == Compression::Zstd) {
status_ = ZStdReader::DecompressAll(chunk.records, chunk.compressedSize, chunk.uncompressedSize,
&slot.decompressedChunk);
} else {
status_ = Status(StatusCode::UnrecognizedCompression,
}
#endif
else {
status_ = Status(StatusCode::UnsupportedCompression,
internal::StrCat("unhandled compression: ", chunk.compression));
}
}
Expand Down
2 changes: 1 addition & 1 deletion cpp/mcap/include/mcap/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace mcap {

#define MCAP_LIBRARY_VERSION "1.2.1"
#define MCAP_LIBRARY_VERSION "1.3.0"

using SchemaId = uint16_t;
using ChannelId = uint16_t;
Expand Down
10 changes: 10 additions & 0 deletions cpp/mcap/include/mcap/writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include <vector>

// Forward declaration
#ifndef MCAP_COMPRESSION_NO_ZSTD
struct ZSTD_CCtx_s;
#endif

namespace mcap {

Expand Down Expand Up @@ -251,6 +253,7 @@ class MCAP_PUBLIC BufferWriter final : public IChunkWriter {
std::vector<std::byte> buffer_;
};

#ifndef MCAP_COMPRESSION_NO_LZ4
/**
* @brief An in-memory IChunkWriter implementation that holds data in a
* temporary buffer before flushing to an LZ4-compressed buffer.
Expand All @@ -273,7 +276,9 @@ class MCAP_PUBLIC LZ4Writer final : public IChunkWriter {
std::vector<std::byte> compressedBuffer_;
CompressionLevel compressionLevel_;
};
#endif

#ifndef MCAP_COMPRESSION_NO_ZSTD
/**
* @brief An in-memory IChunkWriter implementation that holds data in a
* temporary buffer before flushing to an ZStandard-compressed buffer.
Expand All @@ -297,6 +302,7 @@ class MCAP_PUBLIC ZStdWriter final : public IChunkWriter {
std::vector<std::byte> compressedBuffer_;
ZSTD_CCtx_s* zstdContext_ = nullptr;
};
#endif

/**
* @brief Provides a write interface to an MCAP file.
Expand Down Expand Up @@ -445,8 +451,12 @@ class MCAP_PUBLIC McapWriter final {
std::unique_ptr<FileWriter> fileOutput_;
std::unique_ptr<StreamWriter> streamOutput_;
std::unique_ptr<BufferWriter> uncompressedChunk_;
#ifndef MCAP_COMPRESSION_NO_LZ4
std::unique_ptr<LZ4Writer> lz4Chunk_;
#endif
#ifndef MCAP_COMPRESSION_NO_ZSTD
std::unique_ptr<ZStdWriter> zstdChunk_;
#endif
std::vector<Schema> schemas_;
std::vector<Channel> channels_;
std::vector<AttachmentIndex> attachmentIndex_;
Expand Down
Loading
Loading