From ffe0d66fcd0881225fbc8adc26201f037c3c69de Mon Sep 17 00:00:00 2001 From: Pawel Kurowski Date: Thu, 12 Sep 2019 13:37:52 +0200 Subject: [PATCH] Allow retriving N bytes of data without coping using variadic ByteView::Read --- .../C3/Interfaces/Connectors/TeamServer.cpp | 2 +- .../MWR/C3/Interfaces/Peripherals/Beacon.cpp | 2 +- Src/Common/MWR/CppTools/ByteView.h | 46 ++++++++++++++++++- Src/Core/GateRelay.cpp | 24 +++++----- Src/Core/NodeRelay.cpp | 8 ++-- Src/Core/Relay.cpp | 2 +- 6 files changed, 63 insertions(+), 21 deletions(-) diff --git a/Src/Common/MWR/C3/Interfaces/Connectors/TeamServer.cpp b/Src/Common/MWR/C3/Interfaces/Connectors/TeamServer.cpp index b1c1938..b056f5c 100644 --- a/Src/Common/MWR/C3/Interfaces/Connectors/TeamServer.cpp +++ b/Src/Common/MWR/C3/Interfaces/Connectors/TeamServer.cpp @@ -385,7 +385,7 @@ bool MWR::C3::Interfaces::Connectors::TeamServer::Connection::SecondThreadStarte MWR::ByteVector MWR::C3::Interfaces::Connectors::TeamServer::PeripheralCreationCommand(ByteView connectionId, ByteView data, bool isX64) { - auto [pipeName, maxConnectionTrials, delayBetweenConnectionTrials/*, payload*/] = data.Read(); + auto [pipeName, maxConnectionTrials, delayBetweenConnectionTrials/*, payload*/] = data.Read(); // custom payload is removed from release. //if (!payload.empty()) diff --git a/Src/Common/MWR/C3/Interfaces/Peripherals/Beacon.cpp b/Src/Common/MWR/C3/Interfaces/Peripherals/Beacon.cpp index 16c6bbd..e5882da 100644 --- a/Src/Common/MWR/C3/Interfaces/Peripherals/Beacon.cpp +++ b/Src/Common/MWR/C3/Interfaces/Peripherals/Beacon.cpp @@ -4,7 +4,7 @@ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// MWR::C3::Interfaces::Peripherals::Beacon::Beacon(ByteView arguments) { - auto [pipeName, maxConnectionTrials, delayBetweenConnectionTrials, payload] = arguments.Read(); + auto [pipeName, maxConnectionTrials, delayBetweenConnectionTrials, payload] = arguments.Read(); // Arguments validation. if (payload.empty()) diff --git a/Src/Common/MWR/CppTools/ByteView.h b/Src/Common/MWR/CppTools/ByteView.h index 7d4f201..0f17626 100644 --- a/Src/Common/MWR/CppTools/ByteView.h +++ b/Src/Common/MWR/CppTools/ByteView.h @@ -40,6 +40,10 @@ namespace MWR /// @returns std::vector tokenized string. std::vector SplitAndCopy(std::string_view stringToBeSplitted, std::string_view delimiter); + /// Idiom for detecting ByteView::Get. + template + constexpr bool IsGetter = false; + /// Non owning container. class ByteView : std::basic_string_view { @@ -136,6 +140,7 @@ namespace MWR using Super::npos; using Super::value_type; + /// @returns ByteVector. Owning container with the read bytes. /// @param byteCount. How many bytes should be read. /// @remarks Read is not compatible with Read. @@ -187,7 +192,7 @@ namespace MWR /// Read bytes and remove them from ByteView. /// @returns ByteArray. Owning container with the read bytes. /// @throws std::out_of_range. If ByteView is too short to hold size of object to return. - template, int> = 0> + template std::enable_if_t, T> Read() { if (std::tuple_size::value > size()) @@ -223,6 +228,35 @@ namespace MWR return TupleGenerator::Generate(*this); } + /// Class allowing reading N bytes without coping data like in ByeView::Read> or ByteView::Reed(size_t). + template + class Get + { + public: + /// Define size marker. + static constexpr size_t size = N; + static constexpr size_t Size = size; + + private: + /// Private constructor. + /// This class should never be instantiated. + Get() = default; + }; + + /// Read bytes and remove them from ByteView. + /// @returns ByteView. Non owning container with the read bytes. + /// @throws std::out_of_range. If ByteView is too short to hold size of object to return. + template + std::enable_if_t, ByteView> Read() + { + if (T::Size > size()) + throw std::out_of_range{ OBF(": Cannot read data from ByteView") }; + + auto retVal = SubString(0, T::Size); + remove_prefix(T::Size); + return retVal; + } + /// Read tuple and remove bytes from ByteView. /// @tparam T. Parameter pack of types to be stored in tuple. Must consist more then one type. Each of types must be parsable by Reed template for one type. /// @returns std::tuple. Tuple will store types provided in parameter pack. @@ -273,7 +307,7 @@ namespace MWR { auto current = std::make_tuple(self.Read()); auto rest = VariadicTupleGenerator::Generate(self); - return std::tuple_cat(current, rest); + return std::tuple_cat(std::move(current), std::move(rest)); } }; @@ -325,6 +359,14 @@ namespace MWR }; }; + /// Specialization of idiom for detecting ByteView::Get. + template + constexpr bool IsGetter> = true; + + /// Alias for simpler use of ByteView::Get. + template + using Bytes = ByteView::Get; + namespace Literals { /// Create ByteView with syntax ""_bvec diff --git a/Src/Core/GateRelay.cpp b/Src/Core/GateRelay.cpp index 0ae470e..91dae24 100644 --- a/Src/Core/GateRelay.cpp +++ b/Src/Core/GateRelay.cpp @@ -47,7 +47,7 @@ void MWR::C3::Core::GateRelay::OnProtocolS2G(ByteView packet0, std::shared_ptr, int32_t>(); + auto [procedure, rid, timestamp] = ByteView{ decrypted }.Read, int32_t>(); if (!m_Profiler->Get().m_Gateway.ConnectionExist(RouteId::FromByteView(rid).GetAgentId())) throw std::runtime_error{ "S2G packet received from not connected source." }; @@ -274,10 +274,10 @@ void MWR::C3::Core::GateRelay::On(ProceduresN2N::InitializeRouteQuery&& query) auto readView = ByteView{ decryptedPacket }; auto newRelayBuildId = BuildId{ readView.Read() }; // todo prepending fixed-size key with 4 byte length is unnecessary, (remember to change writing, not only reading) - auto newRelayPublicKey = Crypto::PublicKey{ readView.Read() }; + auto newRelayPublicKey = Crypto::PublicKey{ readView.Read() }; auto hash = readView.Read(); auto lastSeen = readView.Read(); - HostInfo hostInfo(ByteView{ readView.Read() }); + HostInfo hostInfo(readView.Read()); auto receivedFrom = query.GetSenderChannel().lock(); if (!receivedFrom) @@ -299,17 +299,17 @@ void MWR::C3::Core::GateRelay::On(ProceduresS2G::InitializeRouteQuery&& query) auto readView = ByteView{ decryptedPacket }; // part of message created by parent Node - auto [procedureID, parentRid, timestamp, childRid, childSideDid] = readView.Read, int32_t, ByteArray, ByteArray>(); + auto [procedureID, parentRid, timestamp, childRid, childSideDid] = readView.Read, int32_t, Bytes, Bytes>(); // part of message from new relay. encrypted once again because it was blob for parent relay. auto childPacket = Crypto::DecryptFromAnonymous(readView, m_AuthenticationKey, m_DecryptionKey); readView = ByteView{ childPacket }; auto newRelayBuildId = BuildId{ readView.Read() }; // todo blocking - auto newRelayPublicKey = Crypto::PublicKey{ readView.Read() }; + auto newRelayPublicKey = Crypto::PublicKey{ readView.Read() }; auto hash = readView.Read(); auto lastSeen = readView.Read(); - HostInfo hostInfo(ByteView{ readView.Read() }); + HostInfo hostInfo(readView.Read()); auto receivedFrom = query.GetSenderChannel().lock(); if (!receivedFrom) @@ -337,9 +337,9 @@ void MWR::C3::Core::GateRelay::On(ProceduresS2G::DeliverToBinder query) auto readView = ByteView{ decryptedPacket }; auto unusedProtocolId = readView.Read(); - auto senderRid = RouteId::FromByteView(readView.Read>()); + auto senderRid = RouteId::FromByteView(readView.Read>()); auto timestamp = readView.Read(); - auto deviceId = readView.Read>(); + auto deviceId = readView.Read>(); auto connectorHash = readView.Read(); auto connector = m_Connectors.Find([&](auto const& e){ return e->GetNameHash() == connectorHash; }); @@ -372,7 +372,7 @@ void MWR::C3::Core::GateRelay::On(ProceduresS2G::AddDeviceResponse response) { auto decryptedPacket = response.GetQueryPacket(m_AuthenticationKey, m_DecryptionKey); auto readView = ByteView{ decryptedPacket }; - auto [unsusedProcedureNo, unusedSenderRouteId, timestamp, deviceId, deviceTypeHash, flags] = readView.Read, int32_t, DeviceId::UnderlyingIntegerType, HashT, std::uint8_t>(); + auto [unsusedProcedureNo, unusedSenderRouteId, timestamp, deviceId, deviceTypeHash, flags] = readView.Read, int32_t, DeviceId::UnderlyingIntegerType, HashT, std::uint8_t>(); bool isChannel = flags & 1; bool isNegotiationChannel = flags & (1 << 1); @@ -393,7 +393,7 @@ void MWR::C3::Core::GateRelay::On(ProceduresS2G::AddDeviceResponse response) void MWR::C3::Core::GateRelay::On(ProceduresN2N::ChannelIdExchangeStep1 query) { auto readView = ByteView{ query.GetQueryPacket() }; - auto newOutputId = readView.Read(); + auto newOutputId = readView.Read(); auto newInputId = MWR::Utils::GenerateRandomString(newOutputId.size()); auto sender = query.GetSenderChannel().lock(); @@ -426,7 +426,7 @@ void MWR::C3::Core::GateRelay::On(ProceduresS2G::NewNegotiatedChannelNotificatio { auto decryptedPacket = query.GetQueryPacket(m_AuthenticationKey, m_DecryptionKey); auto readView = ByteView{ decryptedPacket }; - auto [unsusedProcedureNo, unusedSenderRouteId, timestamp, newDeviceId, negotiatorId, inId, outId] = readView.Read, int32_t, DeviceId::UnderlyingIntegerType, DeviceId::UnderlyingIntegerType, std::string, std::string>(); + auto [unsusedProcedureNo, unusedSenderRouteId, timestamp, newDeviceId, negotiatorId, inId, outId] = readView.Read, int32_t, DeviceId::UnderlyingIntegerType, DeviceId::UnderlyingIntegerType, std::string, std::string>(); auto agent = m_Profiler->Get().m_Gateway.m_Agents.Find(query.GetSenderRouteId().GetAgentId()); if (!agent) @@ -448,7 +448,7 @@ void MWR::C3::Core::GateRelay::On(ProceduresS2G::Notification query) { auto decryptedPacket = query.GetQueryPacket(m_AuthenticationKey, m_DecryptionKey); auto readView = ByteView{ decryptedPacket }; - auto [unsusedProcedureNo, unusedSenderRouteId, timestamp, blob] = readView.Read, int32_t, ByteVector>(); + auto [unsusedProcedureNo, unusedSenderRouteId, timestamp, blob] = readView.Read, int32_t, ByteView>(); // blob has not defined structure. Currently Notification is used as ping response. auto agent = m_Profiler->Get().m_Gateway.m_Agents.Find(query.GetSenderRouteId().GetAgentId()); diff --git a/Src/Core/NodeRelay.cpp b/Src/Core/NodeRelay.cpp index beb0522..b3b7802 100644 --- a/Src/Core/NodeRelay.cpp +++ b/Src/Core/NodeRelay.cpp @@ -247,7 +247,7 @@ void MWR::C3::Core::NodeRelay::On(ProceduresG2X::RunCommandOnAgentQuery query) void MWR::C3::Core::NodeRelay::On(ProceduresG2X::AddRoute query) { auto recipient = query.GetRecipientRouteId(); - auto [newRouteBA, directionDidBA] = ByteView{ query.GetPacketBody() }.Read, ByteArray>(); + auto [newRouteBA, directionDidBA] = ByteView{ query.GetPacketBody() }.Read, Bytes>(); auto newRoute = RouteId::FromByteView(newRouteBA); auto directionDid = DeviceId(directionDidBA); std::shared_ptr bridge; @@ -301,7 +301,7 @@ void MWR::C3::Core::NodeRelay::On(ProceduresG2X::DeliverToBinder query) void MWR::C3::Core::NodeRelay::On(ProceduresN2N::ChannelIdExchangeStep1 query) { auto readView = ByteView{ query.GetQueryPacket() }; - auto newOutputId = readView.Read(); + auto newOutputId = readView.Read(); auto newInputId = MWR::Utils::GenerateRandomString(newOutputId.size()); auto sender = query.GetSenderChannel().lock(); @@ -324,7 +324,7 @@ void MWR::C3::Core::NodeRelay::On(ProceduresN2N::ChannelIdExchangeStep1 query) void MWR::C3::Core::NodeRelay::On(ProceduresN2N::ChannelIdExchangeStep2 query) { auto readView = ByteView{ query.GetQueryPacket() }; - auto newOutputId = readView.Read(); + auto newOutputId = readView.Read(); auto sender = query.GetSenderChannel().lock(); @@ -334,7 +334,7 @@ void MWR::C3::Core::NodeRelay::On(ProceduresN2N::ChannelIdExchangeStep2 query) ByteVector args = sender->GetChannelParameters(); auto hash = sender->GetTypeNameHash(); - auto newInputId = ByteVector{ sender->GetInputId() }; + auto newInputId = sender->GetInputId() ; DetachDevice(sender->GetDid()); sender.reset(); // Don't use sender below; diff --git a/Src/Core/Relay.cpp b/Src/Core/Relay.cpp index 7df9a82..ab6774c 100644 --- a/Src/Core/Relay.cpp +++ b/Src/Core/Relay.cpp @@ -41,7 +41,7 @@ std::shared_ptr MWR::C3::Core::Relay::CreateAndAtta if (isNegotiationChannel) { auto readView = commandLine; - auto negotiationId = readView.Read(); + auto negotiationId = readView.Read(); helper.reserve(commandLine.size() + negotiationId.size() + sizeof(std::uint32_t)); auto generatedId = MWR::Utils::GenerateRandomString(negotiationId.size()); if (negotiationClient)