Files
thehub/libs/utils/streaming/ProtoParser.cpp
2023-10-17 22:01:20 +03:00

255 lines
6.6 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (C) 2016-2023 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 "ProtoParser.h"
#include "compat/endian.h"
#include <cassert>
namespace {
bool unserializeProto(const char *data, int dataSize, int &position, uint64_t &result)
{
assert(data);
assert(result == 0);
assert(position >= 0);
int pos = position;
int shift = 0;
while (pos - position < 10 && pos < dataSize) {
unsigned char byte = data[pos++];
result += static_cast<uint64_t>(byte & 0x7F) << shift;
shift += 7;
if ((byte & 0x80) == 0) {
position = pos;
return true;
}
}
return false;
}
}
// static method
void Streaming::ProtoParser::debug(const Streaming::ConstBuffer &buf)
{
Streaming::ProtoParser parser(buf);
while (parser.next() == Streaming::FoundTag) {
auto log = logFatal() << " +" << parser.tag();
switch (parser.m_wireType) {
case VarInt:
log << "=[VarInt]" << parser.longData(); break;
case Int64:
log << "=[Int64]" << parser.longData(); break;
case Data:
log << "=[data]" << parser.m_dataLength;
if (parser.m_dataLength < 50)
log << parser.stringData();
break;
case Int32:
log << "=[Int32]" << parser.intData();
break;
default:
log << "=[unknown]";
break;
}
}
}
Streaming::ProtoParser::ProtoParser(const Streaming::ConstBuffer &buffer)
: m_privData(buffer.begin()),
m_length(buffer.size()),
m_constBuffer(buffer)
{
}
Streaming::ParsedType Streaming::ProtoParser::next()
{
if (m_nestedParser)
return m_nestedParser->next();
if (m_length <= m_position)
return EndOfDocument;
uint8_t byte = m_privData[m_position];
m_wireType = static_cast<WireType>(byte & 0x07);
m_tag = byte >> 3;
m_value = 0;
switch (m_wireType) {
case VarInt: {
bool ok = unserializeProto(m_privData, m_length, ++m_position, m_value);
if (!ok) {
--m_position;
return Error;
}
break;
}
case Data: {
uint64_t value = 0;
int newPos = m_position + 1;
bool ok = unserializeProto(m_privData, m_length, newPos, value);
if (!ok)
return Error;
if (newPos + value > (unsigned int) m_length) // need more bytes
return Error;
m_dataStart = newPos;
m_dataLength = value;
m_position = newPos + value;
break;
}
case Int64: {
if (m_position + 9 > m_length) // need more bytes
return Error;
m_value = htobe64(*reinterpret_cast<const uint32_t*>(m_privData + m_position + 1));
m_position += 9;
break;
}
case Int32: {
if (m_position + 5 > m_length) // need more bytes
return Error;
m_value = htobe32(*reinterpret_cast<const uint32_t*>(m_privData + m_position + 1));
m_position += 5;
break;
}
default:
return Error;
}
return FoundTag;
}
uint32_t Streaming::ProtoParser::tag() const
{
if (m_nestedParser)
return m_nestedParser->tag();
return m_tag;
}
bool Streaming::ProtoParser::isInt() const
{
if (m_nestedParser)
return m_nestedParser->isInt();
return m_wireType == VarInt || m_wireType == Int32;
}
int32_t Streaming::ProtoParser::intData() const
{
if (m_nestedParser)
return m_nestedParser->intData();
return static_cast<int32_t>(m_value);
}
bool Streaming::ProtoParser::isLong() const
{
if (m_nestedParser)
return m_nestedParser->isLong();
return m_wireType == Int64 || isInt();
}
uint64_t Streaming::ProtoParser::longData() const
{
if (m_nestedParser)
return m_nestedParser->longData();
return m_value;
}
bool Streaming::ProtoParser::isByteArray() const
{
if (m_nestedParser)
return m_nestedParser->isByteArray();
return m_wireType == Data;
}
std::string Streaming::ProtoParser::stringData()
{
if (m_nestedParser)
return m_nestedParser->stringData();
if (m_wireType != Data)
return std::string();
return std::string(m_privData + m_dataStart, m_dataLength);
}
bool Streaming::ProtoParser::boolData() const
{
if (m_nestedParser)
return m_nestedParser->boolData();
return intData() == 1;
}
std::vector<char> Streaming::ProtoParser::bytesData() const
{
if (m_nestedParser)
return m_nestedParser->bytesData();
if (!isByteArray())
return std::vector<char>();
assert(m_wireType == Data);
std::vector<char> data;
data.resize(m_dataLength);
memcpy(&data[0], m_privData + m_dataStart, m_dataLength);
return data;
}
Streaming::ConstBuffer Streaming::ProtoParser::bytesDataBuffer() const
{
if (m_nestedParser)
return m_nestedParser->bytesDataBuffer();
if (m_wireType != Data)
return Streaming::ConstBuffer();
return Streaming::ConstBuffer(m_constBuffer.internal_buffer(), m_privData + m_dataStart, m_privData + m_dataStart + m_dataLength);
}
int Streaming::ProtoParser::dataLength() const
{
if (m_nestedParser)
return m_nestedParser->dataLength();
if (m_wireType != Data)
return 0;
return m_dataLength;
}
void Streaming::ProtoParser::enterData()
{
if (m_nestedParser)
m_nestedParser->enterData();
else
m_nestedParser.reset(new ProtoParser(bytesDataBuffer()));
}
void Streaming::ProtoParser::closeData()
{
if (m_nestedParser && m_nestedParser->m_nestedParser)
m_nestedParser->closeData();
else
m_nestedParser.reset(nullptr);
}
int Streaming::ProtoParser::depth() const
{
if (m_nestedParser)
return m_nestedParser->depth() + 1;
return 1;
}
uint256 Streaming::ProtoParser::uint256Data() const
{
if (m_nestedParser)
return m_nestedParser->uint256Data();
if (!isByteArray() || m_dataLength < 32)
return uint256();
assert (m_wireType == Data);
return uint256(m_privData + m_dataStart);
}