From c010e16847586074f88f55f520b0e6131c09f782 Mon Sep 17 00:00:00 2001 From: Nicolas 'Pixel' Noble Date: Thu, 14 Jan 2021 18:15:51 -0800 Subject: [PATCH 1/3] Stub of gadp server. --- src/core/gadp-server.cc | 215 +++++++++++++++++++++++++++ src/core/gadp-server.h | 124 +++++++++++++++ src/core/psxemulator.cc | 1 + src/core/psxemulator.h | 10 +- src/gui/gui.cc | 14 ++ vsprojects/core/core.vcxproj | 2 + vsprojects/core/core.vcxproj.filters | 6 + 7 files changed, 369 insertions(+), 3 deletions(-) create mode 100644 src/core/gadp-server.cc create mode 100644 src/core/gadp-server.h diff --git a/src/core/gadp-server.cc b/src/core/gadp-server.cc new file mode 100644 index 000000000..42f799e6d --- /dev/null +++ b/src/core/gadp-server.cc @@ -0,0 +1,215 @@ +/*************************************************************************** + * Copyright (C) 2021 PCSX-Redux authors * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + ***************************************************************************/ + +#include "core/gadp-server.h" + +#include + +#include "core/debug.h" +#include "core/misc.h" +#include "core/psxemulator.h" +#include "core/psxmem.h" +#include "core/r3000a.h" +#include "core/system.h" +#include "support/protobuf.h" +#include "uvw.hpp" + +enum class ErrorCode { + EC_UNKNOWN = 0, + EC_BAD_REQUEST = 1, + EC_NO_VERSION = 2, + EC_NO_OBJECT = 3, + EC_NO_INTERFACE = 4, + EC_BAD_ARGUMENT = 5, + EC_BAD_ADDRESS = 6, + EC_NOT_SUPPORTED = 7, + EC_MEMORY_ACCESS = 8, + EC_REGISTER_ACCESS = 9, + EC_USER_ERROR = 10, + EC_MODEL_ACCESS = 11, +}; + +enum class StepKind { + SK_INTO = 0, + SK_ADVANCE = 1, + SK_FINISH = 2, + SK_LINE = 3, + SK_OVER = 4, + SK_OVER_LINE = 5, + SK_SKIP = 6, + SK_RETURN = 7, + SK_UNTIL = 8, +}; + +enum class AttachKind { + AK_BY_OBJECT_REF = 0, + AK_BY_ID = 1, +}; + +enum class ExecutionState { + ES_INACTIVE = 0, + ES_ACTIVE = 1, + ES_STOPPED = 2, + ES_RUNNING = 3, + ES_TERMINATED = 4, +}; + +enum class PrimitiveKind { + PK_UNDEFINED = 0, + PK_VOID = 1, + PK_UINT = 2, + PK_SINT = 3, + PK_FLOAT = 4, + PK_COMPLEX = 5, +}; + +enum class UpdateMode { + UM_UNSOLICITED = 0, + UM_SOLICITED = 1, + UM_FIXED = 2, +}; + +enum class ValueType { + VT_VOID = 0, + VT_BOOL = 1, + VT_INT = 2, + VT_LONG = 3, + VT_FLOAT = 4, + VT_DOUBLE = 5, + VT_BYTES = 6, + VT_STRING = 7, + VT_STRING_LIST = 8, + VT_ADDRESS = 9, + VT_RANGE = 10, + VT_BREAK_KIND_SET = 11, + VT_EXECUTION_STATE = 12, + VT_STEP_KIND_SET = 13, + VT_PRIMITIVE_KIND = 14, + VT_DATA_TYPE = 15, + VT_UPDATE_MODE = 16, + VT_PATH = 17, + VT_PATH_LIST = 18, + VT_TYPE = 19, + VT_ATTACH_KIND_SET = 20, +}; + +enum class TargetEventType { + TET_STOPPED = 0, + TET_RUNNING = 1, + TET_PROCESS_CREATED = 2, + TET_PROCESS_EXITED = 3, + TET_THREAD_CREATED = 4, + TET_THREAD_EXITED = 5, + TET_MODULE_LOADED = 6, + TET_MODULE_UNLOADED = 7, + TET_BREAKPOINT_HIT = 8, + TET_STEP_COMPLETED = 9, + TET_EXCEPTION = 10, + TET_SIGNAL = 11, +}; + +PCSX::GadpServer::GadpServer() : m_listener(g_system->m_eventBus) { + m_listener.listen([this](const auto& event) { + if (g_emulator->settings.get()) { + startServer(g_emulator->settings.get()); + } + }); + m_listener.listen([this](const auto& event) { + if (m_serverStatus == SERVER_STARTED) stopServer(); + }); +} + +void PCSX::GadpServer::stopServer() { + assert(m_serverStatus == SERVER_STARTED); + for (auto& client : m_clients) client.close(); + m_server->close(); +} + +void PCSX::GadpServer::startServer(int port) { + assert(m_serverStatus == SERVER_STOPPED); + m_server = g_emulator->m_loop->resource(); + m_server->on([this](const uvw::ListenEvent&, uvw::TCPHandle& srv) { onNewConnection(); }); + m_server->on( + [this](const uvw::CloseEvent&, uvw::TCPHandle& srv) { m_serverStatus = SERVER_STOPPED; }); + m_server->on( + [this](const uvw::ErrorEvent& event, uvw::TCPHandle& srv) { m_gotError = event.what(); }); + m_gotError = ""; + m_server->bind("0.0.0.0", port); + if (!m_gotError.empty()) { + g_system->printf("Error while trying to bind to port %i: %s\n", port, m_gotError.c_str()); + m_server->close(); + return; + } + m_server->listen(); + if (!m_gotError.empty()) { + g_system->printf("Error while trying to listen to port %i: %s\n", port, m_gotError.c_str()); + m_server->close(); + return; + } + + m_serverStatus = SERVER_STARTED; +} + +void PCSX::GadpServer::onNewConnection() { + GadpClient* client = new GadpClient(m_server); + m_clients.push_back(client); + client->accept(m_server); +} + +PCSX::GadpClient::GadpClient(std::shared_ptr srv) + : m_tcp(srv->loop().resource()), m_listener(g_system->m_eventBus) { + m_listener.listen([this](const auto& event) {}); + m_listener.listen([this](const auto& event) {}); +} + +void PCSX::GadpClient::processData(const Slice& slice) { + const uint8_t* ptr = reinterpret_cast(slice.data()); + auto size = slice.size(); + int v = 0; + + while (size) { + switch (m_state) { + case WAIT_FOR_LEN: + m_lenBuffer[m_length++] = *ptr++; + size--; + if (m_length == 4) { + m_length = m_lenBuffer[0] | (m_lenBuffer[1] << 8) | (m_lenBuffer[2] << 16) | (m_lenBuffer[3] << 24); + m_protoBuffer.clear(); + if (m_length != 0) { + m_state = READING_DATA; + } else { + // process empty proto + } + } + break; + case READING_DATA: { + auto copySize = std::min(size, m_length); + m_protoBuffer += std::string((const char*)ptr, copySize); + ptr += copySize; + size -= copySize; + m_length -= copySize; + + if (m_length == 0) { + m_state = WAIT_FOR_LEN; + // process proto + } + } + } + } +} diff --git a/src/core/gadp-server.h b/src/core/gadp-server.h new file mode 100644 index 000000000..e5a088a12 --- /dev/null +++ b/src/core/gadp-server.h @@ -0,0 +1,124 @@ +/*************************************************************************** + * Copyright (C) 2021 PCSX-Redux authors * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + ***************************************************************************/ + +#pragma once + +#include + +#include "support/eventbus.h" +#include "support/list.h" +#include "support/slice.h" +#include "uvw.hpp" + +namespace PCSX { + +class GadpClient : public Intrusive::List::Node { + public: + GadpClient(std::shared_ptr srv); + ~GadpClient() { assert(m_requests.size() == 0); } + typedef Intrusive::List ListType; + + void accept(std::shared_ptr srv) { + assert(m_status == CLOSED); + m_tcp->on([this](const uvw::CloseEvent&, uvw::TCPHandle&) { delete this; }); + m_tcp->on([this](const uvw::EndEvent&, uvw::TCPHandle&) { close(); }); + m_tcp->on([this](const uvw::ErrorEvent&, uvw::TCPHandle&) { close(); }); + m_tcp->on([this](const uvw::DataEvent& event, uvw::TCPHandle&) { read(event); }); + m_tcp->on([this](const uvw::WriteEvent&, uvw::TCPHandle&) { + auto top = m_requests.begin(); + if (top == m_requests.end()) return; + top->gotWriteEvent(); + }); + srv->accept(*m_tcp); + m_tcp->read(); + m_status = OPEN; + } + void close() { + if (m_status != OPEN) return; + m_status = CLOSING; + m_tcp->close(); + m_requests.destroyAll(); + } + + private: + void write(const Slice& slice) { + auto* req = new WriteRequest(); + req->m_slice = slice; + req->enqueue(this); + } + + struct WriteRequest : public Intrusive::List::Node { + void enqueue(GadpClient* client) { + m_outstanding = 1; + client->m_requests.push_back(this); + client->m_tcp->write(static_cast(const_cast(m_slice.data())), m_slice.size()); + } + void gotWriteEvent() { + if (--m_outstanding == 0) delete this; + } + uv_write_t m_req; + Slice m_slice; + unsigned m_outstanding; + }; + friend struct WriteRequest; + Intrusive::List m_requests; + void read(const uvw::DataEvent& event) { + Slice slice; + slice.borrow(event.data.get(), event.length); + + processData(slice); + } + void processData(const Slice& slice); + + std::shared_ptr m_tcp; + enum { CLOSED, OPEN, CLOSING } m_status = CLOSED; + + EventBus::Listener m_listener; + + std::string m_protoBuffer; + enum { + WAIT_FOR_LEN, + READING_DATA, + } m_state = WAIT_FOR_LEN; + uint8_t m_lenBuffer[4]; + uint32_t m_length = 0; +}; + +class GadpServer { + public: + GadpServer(); + enum GadpServerStatus { + SERVER_STOPPED, + SERVER_STARTED, + }; + GadpServerStatus getServerStatus() { return m_serverStatus; } + + void startServer(int port = 15432); + void stopServer(); + + private: + void onNewConnection(); + GadpServerStatus m_serverStatus = SERVER_STOPPED; + std::shared_ptr m_server; + GadpClient::ListType m_clients; + EventBus::Listener m_listener; + std::string m_gotError; +}; + +} // namespace PCSX diff --git a/src/core/psxemulator.cc b/src/core/psxemulator.cc index cddcc7a4c..3ed4704a6 100644 --- a/src/core/psxemulator.cc +++ b/src/core/psxemulator.cc @@ -22,6 +22,7 @@ #include "core/cdrom.h" #include "core/cheat.h" #include "core/debug.h" +#include "core/gadp-server.h" #include "core/gdb-server.h" #include "core/gpu.h" #include "core/gte.h" diff --git a/src/core/psxemulator.h b/src/core/psxemulator.h index 38a607bf9..62c775e8d 100644 --- a/src/core/psxemulator.h +++ b/src/core/psxemulator.h @@ -85,6 +85,7 @@ class CDRom; class Cheats; class Counters; class Debug; +class GadpServer; class GdbServer; class WebServer; class GPU; @@ -141,6 +142,8 @@ class Emulator { typedef SettingString SettingLocale; typedef Setting SettingMcd1Inserted; typedef Setting SettingMcd2Inserted; + typedef Setting SettingGadpServer; + typedef Setting SettingGadpServerPort; typedef Setting SettingGdbServer; typedef Setting SettingGdbManifest; typedef Setting SettingGdbServerPort; @@ -152,9 +155,9 @@ class Emulator { Settings + SettingMcd1Inserted, SettingMcd2Inserted, SettingBiosOverlay, SettingGadpServer, SettingGadpServerPort, + SettingGdbServer, SettingGdbManifest, SettingGdbServerPort, SettingGdbServerTrace, SettingWebServer, + SettingWebServerPort, SettingDynarec, Setting8MB> settings; class PcsxConfig { public: @@ -207,6 +210,7 @@ class Emulator { std::unique_ptr m_cheats; std::unique_ptr m_mdec; std::unique_ptr m_gpu; + std::unique_ptr m_gadpServer; std::unique_ptr m_gdbServer; std::unique_ptr m_webServer; std::unique_ptr m_debug; diff --git a/src/gui/gui.cc b/src/gui/gui.cc index b8b8cc6b4..c6ccf714c 100644 --- a/src/gui/gui.cc +++ b/src/gui/gui.cc @@ -32,6 +32,7 @@ #include "core/binloader.h" #include "core/cdrom.h" +#include "core/gadp-server.h" #include "core/gdb-server.h" #include "core/gpu.h" #include "core/pad.h" @@ -840,6 +841,19 @@ faster by not displaying the logo.)")); ShowHelpMarker(_(R"(This will enable the usage of various breakpoints throughout the execution of mips code. Enabling this can slow down emulation to a noticable extend.)")); + if (ImGui::Checkbox(_("Enable GADP Server"), &settings.get().value)) { + changed = true; + if (settings.get()) { + g_emulator->m_gadpServer->startServer(settings.get()); + } else { + g_emulator->m_gadpServer->stopServer(); + } + } + ShowHelpMarker(_(R"(This will activate a gadp-server that you can +connect to with any gadp-remote compliant client, which +should be ghidra 9.3+ at the moment. +You also need to enable the debugger.)")); + changed |= ImGui::InputInt(_("GADP Server Port"), &settings.get().value); if (ImGui::Checkbox(_("Enable GDB Server"), &settings.get().value)) { changed = true; if (settings.get()) { diff --git a/vsprojects/core/core.vcxproj b/vsprojects/core/core.vcxproj index f5e6ddc6b..417f79f5f 100644 --- a/vsprojects/core/core.vcxproj +++ b/vsprojects/core/core.vcxproj @@ -125,6 +125,7 @@ + @@ -164,6 +165,7 @@ + diff --git a/vsprojects/core/core.vcxproj.filters b/vsprojects/core/core.vcxproj.filters index e3cbdf7f7..e1b4ff389 100644 --- a/vsprojects/core/core.vcxproj.filters +++ b/vsprojects/core/core.vcxproj.filters @@ -129,6 +129,9 @@ Source Files + + Source Files + @@ -245,6 +248,9 @@ Header Files + + Header Files + From fdb01ea3d42d9c6695e34bf4efbd57b15f02d9ba Mon Sep 17 00:00:00 2001 From: Nicolas 'Pixel' Noble Date: Thu, 14 Jan 2021 23:19:52 -0800 Subject: [PATCH 2/3] Some more fields. --- src/core/gadp-server.cc | 48 ++++++++++++++++++++++++++++++ src/support/protobuf.h | 65 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/src/core/gadp-server.cc b/src/core/gadp-server.cc index 42f799e6d..02d927b45 100644 --- a/src/core/gadp-server.cc +++ b/src/core/gadp-server.cc @@ -124,6 +124,54 @@ enum class TargetEventType { TET_SIGNAL = 11, }; +namespace PCSX { + +namespace { + +typedef Protobuf::Message ErrorRequest; + +typedef Protobuf::Field ErrorReplyCode; +typedef Protobuf::Field ErrorReplyMessage; +typedef Protobuf::Message ErrorReply; + +typedef Protobuf::RepeatedFieldVariable ConnectRequestVersion; +typedef Protobuf::Message ConnectRequest; + +typedef Protobuf::Field ConnectReplyVersion; +typedef Protobuf::Field ConnectReplySchemaContext; +typedef Protobuf::Field ConnectReplyRootSchema; +typedef Protobuf::Message + ConnectReply; + +typedef Protobuf::Field PingContent; +typedef Protobuf::Message PingRequest; +typedef Protobuf::Message PingReply; + +typedef Protobuf::Field AddressSpace; +typedef Protobuf::Field AddressOffset; +typedef Protobuf::Field AddressExtend; +typedef Protobuf::Message Address; +typedef Protobuf::Message AddressRange; + +typedef Protobuf::RepeatedFieldVariable PathElement; +typedef Protobuf::Message Path; +typedef Protobuf::RepeatedFieldVariable PathListPath; +typedef Protobuf::Message PathList; + +typedef Protobuf::RepeatedFieldVariable KindSet; +typedef Protobuf::Message BreakKindSet; +typedef Protobuf::Message StepKindsSet; + +typedef Protobuf::RepeatedFieldVariable StringListField; +typedef Protobuf::Message StringList; + +typedef Protobuf::Message AttachKindSet; + +} // namespace + +} // namespace PCSX + PCSX::GadpServer::GadpServer() : m_listener(g_system->m_eventBus) { m_listener.listen([this](const auto& event) { if (g_emulator->settings.get()) { diff --git a/src/support/protobuf.h b/src/support/protobuf.h index b1814a5a1..268d11482 100644 --- a/src/support/protobuf.h +++ b/src/support/protobuf.h @@ -549,6 +549,71 @@ struct RepeatedField, fieldNumberValu } }; +template +struct RepeatedFieldVariable; +template +struct RepeatedFieldVariable, fieldNumberValue> { + RepeatedFieldVariable() { reset(); } + static constexpr uint64_t fieldNumber = fieldNumberValue; + static constexpr unsigned wireType = 2; + typedef irqus::typestring fieldName; + static constexpr void dumpSchema(std::ostream &stream) { + stream << " repeated " << FieldType::typeName << " " << fieldName::data() << " = " << fieldNumberValue << ";" + << std::endl; + } + std::vector value; + size_t count = 0; + constexpr void reset() { + value.clear(); + count = 0; + } + static constexpr bool matches(unsigned wireType) { return wireType == 2 || FieldType::matches(wireType); } + static constexpr bool needsToSerializeHeader() { return FieldType::wireType == 2; } + void serialize(OutSlice *slice) const { + if (FieldType::wireType == 2) { + for (const auto &v : value) { + OutSlice subSlice; + v.serialize(&subSlice); + std::string subSliceData = subSlice.finalize(); + slice->putVarInt((fieldNumber << 3) | FieldType::wireType); + slice->putVarInt(subSliceData.size()); + slice->putBytes(subSliceData); + } + } else { + OutSlice subSlice; + for (const auto &v : value) { + v.serialize(&subSlice); + } + std::string subSliceData = subSlice.finalize(); + slice->putVarInt(subSliceData.size()); + slice->putBytes(subSliceData); + } + } + void deserialize(InSlice *slice, unsigned wireType) { + if (FieldType::wireType != wireType) { + InSlice subSlice = slice->getSubSlice(slice->getVarInt()); + while (subSlice.bytesLeft()) { + deserializeOne(&subSlice, wireType); + } + } else { + deserializeOne(slice, wireType); + } + } + constexpr bool hasData() const { return !value.empty(); } + constexpr void commit() {} + + private: + void deserializeOne(InSlice *slice, unsigned wireType) { + value.resize(count + 1); + if (FieldType::wireType == 2) { + InSlice subSlice = slice->getSubSlice(slice->getVarInt()); + value[count++].deserialize(&subSlice, FieldType::wireType); + } else { + value[count++].deserialize(slice, wireType); + } + } +}; + template struct RepeatedFieldRef; template From 34caf626bfe3d7a463b3d6eae3c10eb2ca019f80 Mon Sep 17 00:00:00 2001 From: Nicolas 'Pixel' Noble Date: Sun, 17 Jan 2021 21:44:59 -0800 Subject: [PATCH 3/3] Some more fields. --- src/core/gadp-server.cc | 54 +++++++++++++++++++++++++-------- src/support/protobuf.h | 67 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/src/core/gadp-server.cc b/src/core/gadp-server.cc index 02d927b45..46fd97ecf 100644 --- a/src/core/gadp-server.cc +++ b/src/core/gadp-server.cc @@ -45,6 +45,13 @@ enum class ErrorCode { EC_MODEL_ACCESS = 11, }; +enum class BreakKind { + BK_READ = 0, + BK_WRITE = 1, + BK_EXECUTE = 2, + BK_SOFTWARE = 3, +}; + enum class StepKind { SK_INTO = 0, SK_ADVANCE = 1, @@ -110,18 +117,18 @@ enum class ValueType { }; enum class TargetEventType { - TET_STOPPED = 0, - TET_RUNNING = 1, - TET_PROCESS_CREATED = 2, - TET_PROCESS_EXITED = 3, - TET_THREAD_CREATED = 4, - TET_THREAD_EXITED = 5, - TET_MODULE_LOADED = 6, - TET_MODULE_UNLOADED = 7, - TET_BREAKPOINT_HIT = 8, - TET_STEP_COMPLETED = 9, - TET_EXCEPTION = 10, - TET_SIGNAL = 11, + EV_STOPPED = 0, + EV_RUNNING = 1, + EV_PROCESS_CREATED = 2, + EV_PROCESS_EXITED = 3, + EV_THREAD_CREATED = 4, + EV_THREAD_EXITED = 5, + EV_MODULE_LOADED = 6, + EV_MODULE_UNLOADED = 7, + EV_BREAKPOINT_HIT = 8, + EV_STEP_COMPLETED = 9, + EV_EXCEPTION = 10, + EV_SIGNAL = 11, }; namespace PCSX { @@ -160,7 +167,7 @@ typedef Protobuf::RepeatedFieldVariable PathListPat typedef Protobuf::Message PathList; typedef Protobuf::RepeatedFieldVariable KindSet; -typedef Protobuf::Message BreakKindSet; +typedef Protobuf::Message BreakKindsSet; typedef Protobuf::Message StepKindsSet; typedef Protobuf::RepeatedFieldVariable StringListField; @@ -168,6 +175,27 @@ typedef Protobuf::Message StringList; typedef Protobuf::Message AttachKindSet; +typedef Protobuf::Message DataType; + +typedef Protobuf::Field BoolValue; +typedef Protobuf::Field IntValue; +typedef Protobuf::Field LongValue; +typedef Protobuf::Field FloatValue; +typedef Protobuf::Field DoubleValue; +typedef Protobuf::Field BytesValue; +typedef Protobuf::Field StringValue; +typedef Protobuf::MessageField StringListValue; +typedef Protobuf::MessageField AddressValue; +typedef Protobuf::MessageField RangeValue; +typedef Protobuf::MessageField BreakKindsValue; +typedef Protobuf::Field ExecStateValue; +typedef Protobuf::MessageField StepKindsValue; +typedef Protobuf::Field PrimitiveKindValue; +typedef Protobuf::MessageField DataTypeValue; +typedef Protobuf::Field UpdateModeValue; +typedef Protobuf::MessageField PathValue; +typedef Protobuf::MessageField PathListValue; + } // namespace } // namespace PCSX diff --git a/src/support/protobuf.h b/src/support/protobuf.h index 268d11482..d97969bf1 100644 --- a/src/support/protobuf.h +++ b/src/support/protobuf.h @@ -483,6 +483,73 @@ struct FieldPtr, fieldNumberValue> { FieldType copy = FieldType(); }; +template +struct RepeatedFieldPtr; +template +struct RepeatedFieldPtr, fieldNumberValue> { + RepeatedFieldPtr() {} + ~RepeatedFieldPtr() { reset(); } + static constexpr uint64_t fieldNumber = fieldNumberValue; + static constexpr unsigned wireType = 2; + typedef irqus::typestring fieldName; + static constexpr void dumpSchema(std::ostream &stream) { + stream << " repeated " << FieldType::typeName << " " << fieldName::data() << " = " << fieldNumberValue << ";" + << std::endl; + } + FieldType *value = nullptr; + size_t count = 0; + constexpr void reset() { + free(value); + value = nullptr; + count = 0; + } + static constexpr bool matches(unsigned wireType) { return wireType == 2 || FieldType::matches(wireType); } + static constexpr bool needsToSerializeHeader() { return FieldType::wireType == 2; } + void serialize(OutSlice *slice) const { + if (FieldType::wireType == 2) { + for (unsigned i = 0; i < count; i++) { + OutSlice subSlice; + value[i].serialize(&subSlice); + std::string subSliceData = subSlice.finalize(); + slice->putVarInt((fieldNumber << 3) | FieldType::wireType); + slice->putVarInt(subSliceData.size()); + slice->putBytes(subSliceData); + } + } else { + OutSlice subSlice; + for (unsigned i = 0; i < count; i++) { + value[i].serialize(&subSlice); + } + std::string subSliceData = subSlice.finalize(); + slice->putVarInt(subSliceData.size()); + slice->putBytes(subSliceData); + } + } + void deserialize(InSlice *slice, unsigned wireType) { + if (FieldType::wireType != wireType) { + InSlice subSlice = slice->getSubSlice(slice->getVarInt()); + while (subSlice.bytesLeft()) { + deserializeOne(&subSlice, wireType); + } + } else { + deserializeOne(slice, wireType); + } + } + constexpr bool hasData() const { return !value.empty(); } + constexpr void commit() {} + + private: + void deserializeOne(InSlice *slice, unsigned wireType) { + value = (FieldType *)realloc(sizeof(FieldType) * (count + 1)); + if (FieldType::wireType == 2) { + InSlice subSlice = slice->getSubSlice(slice->getVarInt()); + value[count++].deserialize(&subSlice, FieldType::wireType); + } else { + value[count++].deserialize(slice, wireType); + } + } +}; + template struct RepeatedField; template