255 lines
6.6 KiB
C++
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);
|
|
}
|