/* * This file is part of the Flowee project * Copyright (C) 2016-2023 Tom Zander * * 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 . */ #include "ProtoParser.h" #include "compat/endian.h" #include 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(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(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(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(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(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 Streaming::ProtoParser::bytesData() const { if (m_nestedParser) return m_nestedParser->bytesData(); if (!isByteArray()) return std::vector(); assert(m_wireType == Data); std::vector 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); }