Files
thehub/libs/utils/streaming/ProtoBuilder.cpp
tomFlowee 10510fcb99 Add ProtoBuilder / ProtoParser classes
Protocol Buffers interaction is just another serialization standard,
while its widespread it has fortunately mostly been kept out of
anything relevant or important. Mostly due to the fact that is
really quite bad from a technical perspective.

This adds simple and basic support for creating and parsing
protocol buffer messages, mostly to allow interoperability.
If you want quality: use the MessagBuilder/MessageParser ones instead.
2023-07-14 11:45:47 +02:00

152 lines
4.7 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (C) 2016,2018-2019 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ProtoBuilder.h"
#include "ProtoParser.h"
#include "BufferPool.h"
namespace {
int serializeProto(char *data, uint64_t value)
{
int pos = 0;
while (true) {
data[pos] = (value & 0x7F) | (value >= 0x80 ? 0x80 : 0x00);
if (value <= 0x7F)
break;
value = value >> 7;
pos++;
}
return pos + 1;
}
int write(char *data, uint32_t tag, Streaming::ProtoParser::WireType type) {
assert(tag < 32);
uint8_t byte = tag;
byte = byte << 3;
byte += type;
data[0] = byte;
return 1;
}
}
Streaming::ProtoBuilder::ProtoBuilder(Streaming::BufferPool &pool)
: m_buffer(&pool)
{
}
void Streaming::ProtoBuilder::add(uint32_t tag, uint64_t value)
{
handleNestedBuilder();
int tagSize = write(m_buffer->data(), tag, Streaming::ProtoParser::VarInt);
m_buffer->markUsed(tagSize);
tagSize = ::serializeProto(m_buffer->data(), value);
m_buffer->markUsed(tagSize);
}
void Streaming::ProtoBuilder::add(uint32_t tag, const std::string &value)
{
handleNestedBuilder();
int tagSize = write(m_buffer->data(), tag, Streaming::ProtoParser::Data);
const unsigned int size = value.size();
tagSize += ::serializeProto(m_buffer->data() + tagSize, size);
m_buffer->markUsed(tagSize);
memcpy(m_buffer->data(), value.c_str(), value.size());
m_buffer->markUsed(value.size());
}
void Streaming::ProtoBuilder::add(uint32_t tag, const std::vector<char> &data)
{
addByteArray(tag, data.data(), data.size());
}
void Streaming::ProtoBuilder::addByteArray(uint32_t tag, const void *data, int bytes)
{
handleNestedBuilder();
int tagSize = write(m_buffer->data(), tag, Streaming::ProtoParser::Data);
tagSize += ::serializeProto(m_buffer->data() + tagSize, bytes);
m_buffer->markUsed(tagSize);
memcpy(m_buffer->data(), data, bytes);
m_buffer->markUsed(bytes);
}
void Streaming::ProtoBuilder::add(uint32_t tag, const Streaming::ConstBuffer &data)
{
handleNestedBuilder();
int tagSize = write(m_buffer->data(), tag, Streaming::ProtoParser::Data);
tagSize += ::serializeProto(m_buffer->data() + tagSize, data.size());
m_buffer->markUsed(tagSize);
memcpy(m_buffer->data(), data.begin(), data.size());
m_buffer->markUsed(data.size());
}
void Streaming::ProtoBuilder::add(uint32_t tag, bool value)
{
return add(tag, (int32_t) (value ? 1 : 0));
}
void Streaming::ProtoBuilder::add(uint32_t tag, int32_t value)
{
handleNestedBuilder();
int tagSize = write(m_buffer->data(), tag, Streaming::ProtoParser::VarInt);
m_buffer->markUsed(tagSize);
tagSize = ::serializeProto(m_buffer->data(), value);
m_buffer->markUsed(tagSize);
}
Streaming::ProtoBuilder *Streaming::ProtoBuilder::addNestedMessage(uint32_t tag)
{
handleNestedBuilder();
write(m_buffer->data(), tag, Streaming::ProtoParser::Data);
m_buffer->markUsed(1);
m_nestedBufferPool = new BufferPool();
m_nestedBuilder = new Streaming::ProtoBuilder(*m_nestedBufferPool);
return m_nestedBuilder;
}
void Streaming::ProtoBuilder::add(uint32_t tag, const unsigned char *data, unsigned int length)
{
handleNestedBuilder();
int tagSize = write(m_buffer->data(), tag, Streaming::ProtoParser::Data);
tagSize += ::serializeProto(m_buffer->data() + tagSize, length);
m_buffer->markUsed(tagSize);
memcpy(m_buffer->data(), data, length);
m_buffer->markUsed(length);
}
void Streaming::ProtoBuilder::handleNestedBuilder()
{
if (m_nestedBuilder == nullptr)
return;
delete m_nestedBuilder;
m_nestedBuilder = nullptr;
auto blob = m_nestedBufferPool->commit();
delete m_nestedBufferPool;
m_nestedBufferPool = nullptr;
auto tagSize = ::serializeProto(m_buffer->data(), blob.size());
m_buffer->markUsed(tagSize);
memcpy(m_buffer->data(), blob.begin(), blob.size());
m_buffer->markUsed(blob.size());
}
Streaming::ConstBuffer Streaming::ProtoBuilder::buffer()
{
handleNestedBuilder();
return m_buffer->commit();
}