编码的 avro 消息的大小,而不对其进行编码

Size of encoded avro message without encoding it

本文关键字:编码 avro 消息      更新时间:2023-10-16

有没有办法在不实际编码的情况下获取编码的 avro 消息的大小?

我正在使用 Avro 1.8.1 进行C++。

我习惯于谷歌协议缓冲区,您可以在其中调用 protobuf 上的ByteSize()以获取编码大小,所以这是我正在寻找的类似的东西。

由于消息本质上是一个原始结构,因此我得到无法从消息本身检索大小,但也许有一个我不知道的辅助方法?

(下面的编辑显示了一种在用BinaryEncoder写入OutputStream后缩小以适应它的黑客方法)

遗憾的是,avro::encode()没有在编码后OutputStream上使用backup来释放未使用的内存。Martin G 的回答仅使用 avro 提供的工具给出了最佳解决方案,但如果序列化对象的大小为 N 个字节,它会发出 N 个内存分配,每个内存分配 1 个字节。

您可以实现一个自定义avro::OutputStream,该仅计算和丢弃所有写入的字节。这将摆脱内存分配。这仍然不是一个很好的方法,因为实际的编码器必须"询问"每个字节:

(代码未经测试,仅用于演示目的)

#include <avro/Encoder.hh>
#include <cstdint>
class ByteCountOutputStream : public avro::OutputStream {
public:
size_t byteCount_ = 0;
uint8_t dummyWriteLocation_;
explicit ByteCountOutputStream() {};
bool next(uint8_t **data, size_t *len) final {
byteCount_ += 1;
*data = &dummyWriteLocation_;
*len = 1;
return true;
}
void backup(size_t len) final {
byteCount_ -= len;
}
uint64_t byteCount() const final {
return byteCount_;
}
void flush() final {}
};

然后可以将其用作:

MyAvroStruct obj;
avro::EncoderPtr encoder = avro::binaryEncoder();
ByteCountOutputStream out();
encoder->init(out);
avro::encode(*encoder, obj);
size_t bufferSize = out.byteCount();
<小时 />

编辑: 偶然发现这一点时,我的第一个问题是:我如何知道需要多少字节的OutputStream(用于存储/传输)?或者,等效地,如果OutputStream.byteCount()返回编码器到目前为止分配的字节数,如何使编码器"备份"/释放它不使用的字节数?好吧,有一个黑客方法:

Encoder抽象类提供了一个init方法。对于 BinaryEncoder,这目前实现为:

void BinaryEncoder::init(OutputStream &os) {
out_.reset(os);
}

out_是编码器的内部StreamWriter

现在,StreamWriter实现reset为:

void reset(OutputStream &os) {
if (out_ != nullptr && end_ != next_) {
out_->backup(end_ - next_);
}
out_ = &os;
next_ = end_;
}

这会在切换到新内存之前将未使用的内存返回到"旧"输出流。

因此,您可以像这样滥用编码器的 init 方法:

// setup as always
MyAvroStruct obj;
avro::EncoderPtr encoder = avro::binaryEncoder();
std::auto_ptr<avro::OutputStream> out = avro::memoryOutputStream();
// actual serialization
encoder->init(*out);
avro::encode(*encoder, obj);
// re-init on the same OutputStream. Happens to shrink the stream to fit
encoder->init(*out);
size_t bufferSize = out->byteCount();

但是,此行为未记录在案,因此将来可能会中断。

不幸的是,没有办法绕过它...

下面是一个示例,显示了如何通过对对象进行编码来计算大小:

MyAvroStruct obj;
avro::EncoderPtr encoder = avro::binaryEncoder();
std::auto_ptr<avro::OutputStream> out = avro::memoryOutputStream(1);
encoder->init(*out);
avro::encode(*encoder, obj);
out->flush();
uint32_t bufferSize = out->byteCount();