Commit 7b907c84c793e707f3ff7c208881260e6759c4b1
1 parent
08ec7f20
Almost works
I need to decide what to do with getPublishData and that disabled test needs repurposing.
Showing
8 changed files
with
41 additions
and
99 deletions
FlashMQTests/tst_maintests.cpp
| @@ -1148,6 +1148,7 @@ void testCopyPacketHelper(const std::string &topic, char from_qos, char to_qos, | @@ -1148,6 +1148,7 @@ void testCopyPacketHelper(const std::string &topic, char from_qos, char to_qos, | ||
| 1148 | QCOMPARE(stagingPacketOne.getTopic(), parsedPacketOne.getTopic()); | 1148 | QCOMPARE(stagingPacketOne.getTopic(), parsedPacketOne.getTopic()); |
| 1149 | QCOMPARE(stagingPacketOne.getPayloadCopy(), parsedPacketOne.getPayloadCopy()); | 1149 | QCOMPARE(stagingPacketOne.getPayloadCopy(), parsedPacketOne.getPayloadCopy()); |
| 1150 | 1150 | ||
| 1151 | + /* | ||
| 1151 | std::shared_ptr<MqttPacket> copiedPacketOne = parsedPacketOne.getCopy(to_qos); | 1152 | std::shared_ptr<MqttPacket> copiedPacketOne = parsedPacketOne.getCopy(to_qos); |
| 1152 | 1153 | ||
| 1153 | QCOMPARE(payloadOne, copiedPacketOne->getPayloadCopy()); | 1154 | QCOMPARE(payloadOne, copiedPacketOne->getPayloadCopy()); |
| @@ -1166,6 +1167,7 @@ void testCopyPacketHelper(const std::string &topic, char from_qos, char to_qos, | @@ -1166,6 +1167,7 @@ void testCopyPacketHelper(const std::string &topic, char from_qos, char to_qos, | ||
| 1166 | copiedPacketOne->readIntoBuf(bufOfCopied); | 1167 | copiedPacketOne->readIntoBuf(bufOfCopied); |
| 1167 | QVERIFY2(bufOfCopied == bufOfReference, formatString("Failure on length %d for topic %s, from qos %d to qos %d, retain: %d.", | 1168 | QVERIFY2(bufOfCopied == bufOfReference, formatString("Failure on length %d for topic %s, from qos %d to qos %d, retain: %d.", |
| 1168 | len, topic.c_str(), from_qos, to_qos, retain).c_str()); | 1169 | len, topic.c_str(), from_qos, to_qos, retain).c_str()); |
| 1170 | + */ | ||
| 1169 | } | 1171 | } |
| 1170 | } | 1172 | } |
| 1171 | 1173 |
mqttpacket.cpp
| @@ -39,77 +39,6 @@ MqttPacket::MqttPacket(CirBuf &buf, size_t packet_len, size_t fixed_header_lengt | @@ -39,77 +39,6 @@ MqttPacket::MqttPacket(CirBuf &buf, size_t packet_len, size_t fixed_header_lengt | ||
| 39 | pos += fixed_header_length; | 39 | pos += fixed_header_length; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | -/** | ||
| 43 | - * @brief MqttPacket::getCopy (using default copy constructor and resetting some selected fields) is easier than using the copy constructor | ||
| 44 | - * publically, because then I have to keep maintaining a functioning copy constructor for each new field I add. | ||
| 45 | - * @return a shared pointer because that's typically how we need it; we only need to copy it if we pass it around as shared resource. | ||
| 46 | - * | ||
| 47 | - * The idea is that because a packet with QoS is longer than one without, we just copy as much as possible if both packets have the same QoS. | ||
| 48 | - * | ||
| 49 | - * Note that there can be two types of packets: one with the fixed header (including remaining length), and one without. The latter we could be | ||
| 50 | - * more clever about, but I'm forgoing that right now. Their use is mostly for retained messages. | ||
| 51 | - * | ||
| 52 | - * Also note that some fields are undeterminstic in the copy: dup, retain and packetid for instance. Sometimes they come from the original, | ||
| 53 | - * sometimes not. The current planned usage is that those fields will either ONLY or NEVER be used in the copy, so it doesn't matter what I do | ||
| 54 | - * with them here. I may reconsider. | ||
| 55 | - */ | ||
| 56 | -std::shared_ptr<MqttPacket> MqttPacket::getCopy(char new_max_qos) const | ||
| 57 | -{ | ||
| 58 | - assert(packetType == PacketType::PUBLISH); | ||
| 59 | - | ||
| 60 | - // You're not supposed to copy a duplicate packet. The only packets that get the dup flag, should not be copied AGAIN. This | ||
| 61 | - // has to do with the Session::writePacket() and Session::sendPendingQosMessages() logic. | ||
| 62 | - assert((first_byte & 0b00001000) == 0); | ||
| 63 | - | ||
| 64 | - if (publishData.qos > 0 && new_max_qos == 0) | ||
| 65 | - { | ||
| 66 | - // if shrinking the packet doesn't alter the amount of bytes in the 'remaining length' part of the header, we can | ||
| 67 | - // just memmove+shrink the packet. This is because the packet id always is two bytes before the payload, so we just move the payload | ||
| 68 | - // over it. When testing 100M copies, it went from 21000 ms to 10000 ms. In other words, about 200 ns to 100 ns per copy. | ||
| 69 | - // There is an elaborate unit test to test this optimization. | ||
| 70 | - if ((fixed_header_length == 2 && bites.size() < 125)) | ||
| 71 | - { | ||
| 72 | - // I don't know yet if this is true, but I don't want to forget when I implemenet MQTT5. | ||
| 73 | - assert(sender && sender->getProtocolVersion() <= ProtocolVersion::Mqtt311); | ||
| 74 | - | ||
| 75 | - std::shared_ptr<MqttPacket> p(new MqttPacket(*this)); | ||
| 76 | - p->sender.reset(); | ||
| 77 | - | ||
| 78 | - if (payloadLen > 0) | ||
| 79 | - std::memmove(&p->bites[packet_id_pos], &p->bites[packet_id_pos+2], payloadLen); | ||
| 80 | - p->bites.erase(p->bites.end() - 2, p->bites.end()); | ||
| 81 | - p->packet_id_pos = 0; | ||
| 82 | - p->publishData.qos = 0; | ||
| 83 | - p->payloadStart -= 2; | ||
| 84 | - if (pos > p->bites.size()) // pos can possible be set elsewhere, so we only set it back if it was after the payload. | ||
| 85 | - p->pos -= 2; | ||
| 86 | - p->packet_id = 0; | ||
| 87 | - | ||
| 88 | - // Clear QoS bits from the header. | ||
| 89 | - p->first_byte &= 0b11111001; | ||
| 90 | - p->bites[0] = p->first_byte; | ||
| 91 | - | ||
| 92 | - assert((p->bites[1] & 0b10000000) == 0); // when there is an MSB, I musn't get rid of it. | ||
| 93 | - assert(p->bites[1] > 3); // There has to be a remaining value after subtracting 2. | ||
| 94 | - | ||
| 95 | - p->bites[1] -= 2; // Reduce the value in the 'remaining length' part of the header. | ||
| 96 | - | ||
| 97 | - return p; | ||
| 98 | - } | ||
| 99 | - | ||
| 100 | - Publish pub(publishData.topic, getPayloadCopy(), new_max_qos); | ||
| 101 | - pub.retain = getRetain(); | ||
| 102 | - std::shared_ptr<MqttPacket> copyPacket(new MqttPacket(ProtocolVersion::Mqtt311, pub)); // TODO: don't hard-code the protocol version. | ||
| 103 | - return copyPacket; | ||
| 104 | - } | ||
| 105 | - | ||
| 106 | - std::shared_ptr<MqttPacket> copyPacket(new MqttPacket(*this)); | ||
| 107 | - copyPacket->sender.reset(); | ||
| 108 | - if (publishData.qos != new_max_qos) | ||
| 109 | - copyPacket->setQos(new_max_qos); | ||
| 110 | - return copyPacket; | ||
| 111 | -} | ||
| 112 | - | ||
| 113 | MqttPacket::MqttPacket(const ConnAck &connAck) : | 42 | MqttPacket::MqttPacket(const ConnAck &connAck) : |
| 114 | bites(connAck.getLengthWithoutFixedHeader()) | 43 | bites(connAck.getLengthWithoutFixedHeader()) |
| 115 | { | 44 | { |
| @@ -174,7 +103,9 @@ MqttPacket::MqttPacket(const ProtocolVersion protocolVersion, const Publish &_pu | @@ -174,7 +103,9 @@ MqttPacket::MqttPacket(const ProtocolVersion protocolVersion, const Publish &_pu | ||
| 174 | this->protocolVersion = protocolVersion; | 103 | this->protocolVersion = protocolVersion; |
| 175 | 104 | ||
| 176 | this->publishData.topic = _publish.topic; | 105 | this->publishData.topic = _publish.topic; |
| 177 | - splitTopic(this->publishData.topic, this->publishData.subtopics); // TODO: I think I can make this conditional, because the (planned) use will already have used the subtopics. | 106 | + |
| 107 | + if (_publish.splitTopic) | ||
| 108 | + splitTopic(this->publishData.topic, this->publishData.subtopics); | ||
| 178 | 109 | ||
| 179 | packetType = PacketType::PUBLISH; | 110 | packetType = PacketType::PUBLISH; |
| 180 | this->publishData.qos = _publish.qos; | 111 | this->publishData.qos = _publish.qos; |
| @@ -1197,7 +1128,7 @@ void MqttPacket::setRetain() | @@ -1197,7 +1128,7 @@ void MqttPacket::setRetain() | ||
| 1197 | } | 1128 | } |
| 1198 | } | 1129 | } |
| 1199 | 1130 | ||
| 1200 | -Publish *MqttPacket::getPublish() | 1131 | +Publish *MqttPacket::getPublishData() |
| 1201 | { | 1132 | { |
| 1202 | if (payloadLen > 0 && publishData.payload.empty()) | 1133 | if (payloadLen > 0 && publishData.payload.empty()) |
| 1203 | publishData.payload = getPayloadCopy(); | 1134 | publishData.payload = getPayloadCopy(); |
mqttpacket.h
| @@ -78,8 +78,6 @@ public: | @@ -78,8 +78,6 @@ public: | ||
| 78 | 78 | ||
| 79 | MqttPacket(MqttPacket &&other) = default; | 79 | MqttPacket(MqttPacket &&other) = default; |
| 80 | 80 | ||
| 81 | - std::shared_ptr<MqttPacket> getCopy(char new_max_qos) const; | ||
| 82 | - | ||
| 83 | size_t getRequiredSizeForPublish(const ProtocolVersion protocolVersion, const Publish &publishData) const; | 81 | size_t getRequiredSizeForPublish(const ProtocolVersion protocolVersion, const Publish &publishData) const; |
| 84 | 82 | ||
| 85 | // Constructor for outgoing packets. These may not allocate room for the fixed header, because we don't (always) know the length in advance. | 83 | // Constructor for outgoing packets. These may not allocate room for the fixed header, because we don't (always) know the length in advance. |
| @@ -123,7 +121,7 @@ public: | @@ -123,7 +121,7 @@ public: | ||
| 123 | std::string getPayloadCopy() const; | 121 | std::string getPayloadCopy() const; |
| 124 | bool getRetain() const; | 122 | bool getRetain() const; |
| 125 | void setRetain(); | 123 | void setRetain(); |
| 126 | - Publish *getPublish(); | 124 | + Publish *getPublishData(); |
| 127 | }; | 125 | }; |
| 128 | 126 | ||
| 129 | #endif // MQTTPACKET_H | 127 | #endif // MQTTPACKET_H |
publishcopyfactory.cpp
| @@ -17,36 +17,39 @@ PublishCopyFactory::PublishCopyFactory(Publish *publish) : | @@ -17,36 +17,39 @@ PublishCopyFactory::PublishCopyFactory(Publish *publish) : | ||
| 17 | 17 | ||
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | -MqttPacket *PublishCopyFactory::getOptimumPacket(char max_qos, ProtocolVersion protocolVersion) | 20 | +MqttPacket *PublishCopyFactory::getOptimumPacket(const char max_qos, const ProtocolVersion protocolVersion) |
| 21 | { | 21 | { |
| 22 | // TODO: cache idea: a set of constructed packets per branch in this code, witn an int identifier. | 22 | // TODO: cache idea: a set of constructed packets per branch in this code, witn an int identifier. |
| 23 | 23 | ||
| 24 | if (packet) | 24 | if (packet) |
| 25 | { | 25 | { |
| 26 | - // TODO: some flag on packet 'containsClientSpecificStuff' | ||
| 27 | - if (max_qos == 0 && max_qos < packet->getQos() && packet->getProtocolVersion() == protocolVersion && protocolVersion <= ProtocolVersion::Mqtt311) | 26 | + if (packet->getProtocolVersion() == protocolVersion && orgQos == max_qos) |
| 28 | { | 27 | { |
| 29 | - // TODO: getCopy now also makes packets with Publish objects. I think I only want to do that here. | ||
| 30 | - | ||
| 31 | - if (!downgradedQos0PacketCopy) | ||
| 32 | - downgradedQos0PacketCopy = packet->getCopy(max_qos); // TODO: getCopy perhaps std::unique_ptr | ||
| 33 | - assert(downgradedQos0PacketCopy->getQos() == 0); | ||
| 34 | - return downgradedQos0PacketCopy.get(); | 28 | + assert(orgQos == packet->getQos()); |
| 29 | + return packet; | ||
| 35 | } | 30 | } |
| 36 | - else if (packet->getProtocolVersion() >= ProtocolVersion::Mqtt5 || protocolVersion >= ProtocolVersion::Mqtt5) | 31 | + |
| 32 | + const int cache_key = (static_cast<uint8_t>(protocolVersion) * 10) + max_qos; | ||
| 33 | + std::unique_ptr<MqttPacket> &cachedPack = constructedPacketCache[cache_key]; | ||
| 34 | + | ||
| 35 | + if (!cachedPack) | ||
| 37 | { | 36 | { |
| 38 | - Publish *pub = packet->getPublish(); | ||
| 39 | - pub->setClientSpecificProperties(); // TODO | ||
| 40 | - this->oneShotPacket = std::make_unique<MqttPacket>(protocolVersion, *pub); | ||
| 41 | - return this->oneShotPacket.get(); | 37 | + Publish *orgPublish = packet->getPublishData(); |
| 38 | + orgPublish->splitTopic = false; | ||
| 39 | + orgPublish->qos = max_qos; | ||
| 40 | + if (protocolVersion >= ProtocolVersion::Mqtt5) | ||
| 41 | + orgPublish->setClientSpecificProperties(); | ||
| 42 | + cachedPack = std::make_unique<MqttPacket>(protocolVersion, *orgPublish); | ||
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | - return packet; | 45 | + return cachedPack.get(); |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 48 | + // Getting a packet of a Publish object happens on will messages and SYS topics and maybe some others. It's low traffic, anyway. | ||
| 47 | assert(publish); | 49 | assert(publish); |
| 48 | 50 | ||
| 49 | - publish->setClientSpecificProperties(); | 51 | + if (protocolVersion >= ProtocolVersion::Mqtt5) |
| 52 | + publish->setClientSpecificProperties(); | ||
| 50 | this->oneShotPacket = std::make_unique<MqttPacket>(protocolVersion, *publish); | 53 | this->oneShotPacket = std::make_unique<MqttPacket>(protocolVersion, *publish); |
| 51 | return this->oneShotPacket.get(); | 54 | return this->oneShotPacket.get(); |
| 52 | } | 55 | } |
| @@ -92,11 +95,12 @@ bool PublishCopyFactory::getRetain() const | @@ -92,11 +95,12 @@ bool PublishCopyFactory::getRetain() const | ||
| 92 | 95 | ||
| 93 | Publish PublishCopyFactory::getNewPublish() const | 96 | Publish PublishCopyFactory::getNewPublish() const |
| 94 | { | 97 | { |
| 95 | - assert(packet->getQos() > 0); // We only need to construct new publishes for QoS. If you're doing it elsewhere, it's a bug. | 98 | + assert(packet->getQos() > 0); |
| 99 | + assert(orgQos > 0); // We only need to construct new publishes for QoS. If you're doing it elsewhere, it's a bug. | ||
| 96 | 100 | ||
| 97 | if (packet) | 101 | if (packet) |
| 98 | { | 102 | { |
| 99 | - Publish p(*packet->getPublish()); | 103 | + Publish p(*packet->getPublishData()); |
| 100 | return p; | 104 | return p; |
| 101 | } | 105 | } |
| 102 | 106 |
publishcopyfactory.h
| @@ -5,6 +5,7 @@ | @@ -5,6 +5,7 @@ | ||
| 5 | 5 | ||
| 6 | #include "forward_declarations.h" | 6 | #include "forward_declarations.h" |
| 7 | #include "types.h" | 7 | #include "types.h" |
| 8 | +#include "unordered_map" | ||
| 8 | 9 | ||
| 9 | /** | 10 | /** |
| 10 | * @brief The PublishCopyFactory class is for managing copies of an incoming publish, including sometimes not making copies at all. | 11 | * @brief The PublishCopyFactory class is for managing copies of an incoming publish, including sometimes not making copies at all. |
| @@ -21,16 +22,14 @@ class PublishCopyFactory | @@ -21,16 +22,14 @@ class PublishCopyFactory | ||
| 21 | Publish *publish = nullptr; | 22 | Publish *publish = nullptr; |
| 22 | std::unique_ptr<MqttPacket> oneShotPacket; | 23 | std::unique_ptr<MqttPacket> oneShotPacket; |
| 23 | const char orgQos; | 24 | const char orgQos; |
| 24 | - std::shared_ptr<MqttPacket> downgradedQos0PacketCopy; | ||
| 25 | - | ||
| 26 | - // TODO: constructed mqtt3 packet and mqtt5 packet? | 25 | + std::unordered_map<uint8_t, std::unique_ptr<MqttPacket>> constructedPacketCache; |
| 27 | public: | 26 | public: |
| 28 | PublishCopyFactory(MqttPacket *packet); | 27 | PublishCopyFactory(MqttPacket *packet); |
| 29 | PublishCopyFactory(Publish *publish); | 28 | PublishCopyFactory(Publish *publish); |
| 30 | PublishCopyFactory(const PublishCopyFactory &other) = delete; | 29 | PublishCopyFactory(const PublishCopyFactory &other) = delete; |
| 31 | PublishCopyFactory(PublishCopyFactory &&other) = delete; | 30 | PublishCopyFactory(PublishCopyFactory &&other) = delete; |
| 32 | 31 | ||
| 33 | - MqttPacket *getOptimumPacket(char max_qos, ProtocolVersion protocolVersion); | 32 | + MqttPacket *getOptimumPacket(const char max_qos, const ProtocolVersion protocolVersion); |
| 34 | char getEffectiveQos(char max_qos) const; | 33 | char getEffectiveQos(char max_qos) const; |
| 35 | const std::string &getTopic() const; | 34 | const std::string &getTopic() const; |
| 36 | const std::vector<std::string> &getSubtopics(); | 35 | const std::vector<std::string> &getSubtopics(); |
qospacketqueue.cpp
| @@ -67,6 +67,7 @@ void QoSPublishQueue::queuePublish(PublishCopyFactory &copyFactory, uint16_t id, | @@ -67,6 +67,7 @@ void QoSPublishQueue::queuePublish(PublishCopyFactory &copyFactory, uint16_t id, | ||
| 67 | assert(id > 0); | 67 | assert(id > 0); |
| 68 | 68 | ||
| 69 | Publish pub = copyFactory.getNewPublish(); | 69 | Publish pub = copyFactory.getNewPublish(); |
| 70 | + pub.splitTopic = false; | ||
| 70 | queue.emplace_back(std::move(pub), id); | 71 | queue.emplace_back(std::move(pub), id); |
| 71 | qosQueueBytes += queue.back().getApproximateMemoryFootprint(); | 72 | qosQueueBytes += queue.back().getApproximateMemoryFootprint(); |
| 72 | } | 73 | } |
| @@ -75,6 +76,7 @@ void QoSPublishQueue::queuePublish(Publish &&pub, uint16_t id) | @@ -75,6 +76,7 @@ void QoSPublishQueue::queuePublish(Publish &&pub, uint16_t id) | ||
| 75 | { | 76 | { |
| 76 | assert(id > 0); | 77 | assert(id > 0); |
| 77 | 78 | ||
| 79 | + pub.splitTopic = false; | ||
| 78 | queue.emplace_back(std::move(pub), id); | 80 | queue.emplace_back(std::move(pub), id); |
| 79 | qosQueueBytes += queue.back().getApproximateMemoryFootprint(); | 81 | qosQueueBytes += queue.back().getApproximateMemoryFootprint(); |
| 80 | } | 82 | } |
types.cpp
| @@ -117,9 +117,14 @@ size_t Publish::getLengthWithoutFixedHeader() const | @@ -117,9 +117,14 @@ size_t Publish::getLengthWithoutFixedHeader() const | ||
| 117 | return result; | 117 | return result; |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | +/** | ||
| 121 | + * @brief Publish::setClientSpecificProperties generates the properties byte array for one client. You're supposed to call it before any publish. | ||
| 122 | + * | ||
| 123 | + */ | ||
| 120 | void Publish::setClientSpecificProperties() | 124 | void Publish::setClientSpecificProperties() |
| 121 | { | 125 | { |
| 122 | - propertyBuilder->clearClientSpecificBytes(); | 126 | + if (propertyBuilder) |
| 127 | + propertyBuilder->clearClientSpecificBytes(); | ||
| 123 | // TODO. Expires at? | 128 | // TODO. Expires at? |
| 124 | } | 129 | } |
| 125 | 130 |
types.h
| @@ -198,6 +198,7 @@ public: | @@ -198,6 +198,7 @@ public: | ||
| 198 | char qos = 0; | 198 | char qos = 0; |
| 199 | bool retain = false; // Note: existing subscribers don't get publishes of retained messages with retain=1. [MQTT-3.3.1-9] | 199 | bool retain = false; // Note: existing subscribers don't get publishes of retained messages with retain=1. [MQTT-3.3.1-9] |
| 200 | uint32_t will_delay = 0; // if will, this is the delay. Just storing here, to avoid having to make a WillMessage class | 200 | uint32_t will_delay = 0; // if will, this is the delay. Just storing here, to avoid having to make a WillMessage class |
| 201 | + bool splitTopic = true; | ||
| 201 | std::chrono::time_point<std::chrono::steady_clock> createdAt; | 202 | std::chrono::time_point<std::chrono::steady_clock> createdAt; |
| 202 | std::chrono::seconds expiresAfter; | 203 | std::chrono::seconds expiresAfter; |
| 203 | std::shared_ptr<Mqtt5PropertyBuilder> propertyBuilder; | 204 | std::shared_ptr<Mqtt5PropertyBuilder> propertyBuilder; |