Commit d3072e9cbc756dc86f35c067a3e8075c0538ef5f
1 parent
367579cc
Refactor SIMD/SSE
Showing
10 changed files
with
145 additions
and
65 deletions
FlashMQTests/tst_maintests.cpp
| @@ -78,11 +78,13 @@ private slots: | @@ -78,11 +78,13 @@ private slots: | ||
| 78 | 78 | ||
| 79 | void test_sse_split(); | 79 | void test_sse_split(); |
| 80 | 80 | ||
| 81 | - void test_validUtf8(); | 81 | + void test_validUtf8Generic(); |
| 82 | void test_validUtf8Sse(); | 82 | void test_validUtf8Sse(); |
| 83 | 83 | ||
| 84 | void testPacketInt16Parse(); | 84 | void testPacketInt16Parse(); |
| 85 | 85 | ||
| 86 | + void testTopicsMatch(); | ||
| 87 | + | ||
| 86 | }; | 88 | }; |
| 87 | 89 | ||
| 88 | MainTests::MainTests() | 90 | MainTests::MainTests() |
| @@ -600,7 +602,8 @@ void MainTests::test_acl_patterns_clientid() | @@ -600,7 +602,8 @@ void MainTests::test_acl_patterns_clientid() | ||
| 600 | 602 | ||
| 601 | void MainTests::test_sse_split() | 603 | void MainTests::test_sse_split() |
| 602 | { | 604 | { |
| 603 | - Utils data; | 605 | + SimdUtils data; |
| 606 | + std::vector<std::string> output; | ||
| 604 | 607 | ||
| 605 | std::list<std::string> topics; | 608 | std::list<std::string> topics; |
| 606 | topics.push_back("one/two/threeabcasdfasdf/koe"); | 609 | topics.push_back("one/two/threeabcasdfasdf/koe"); |
| @@ -619,50 +622,51 @@ void MainTests::test_sse_split() | @@ -619,50 +622,51 @@ void MainTests::test_sse_split() | ||
| 619 | 622 | ||
| 620 | for (const std::string &t : topics) | 623 | for (const std::string &t : topics) |
| 621 | { | 624 | { |
| 622 | - QCOMPARE(*data.splitTopic(t), splitToVector(t, '/')); | 625 | + data.splitTopic(t, output); |
| 626 | + QCOMPARE(output, splitToVector(t, '/')); | ||
| 623 | } | 627 | } |
| 624 | } | 628 | } |
| 625 | 629 | ||
| 626 | -void MainTests::test_validUtf8() | 630 | +void MainTests::test_validUtf8Generic() |
| 627 | { | 631 | { |
| 628 | char m[16]; | 632 | char m[16]; |
| 629 | 633 | ||
| 630 | - QVERIFY(isValidUtf8("")); | ||
| 631 | - QVERIFY(isValidUtf8("ƀ")); | ||
| 632 | - QVERIFY(isValidUtf8("Hello")); | 634 | + QVERIFY(isValidUtf8Generic("")); |
| 635 | + QVERIFY(isValidUtf8Generic("ƀ")); | ||
| 636 | + QVERIFY(isValidUtf8Generic("Hello")); | ||
| 633 | 637 | ||
| 634 | std::memset(m, 0, 16); | 638 | std::memset(m, 0, 16); |
| 635 | - QVERIFY(!isValidUtf8(std::string(m, 16))); | 639 | + QVERIFY(!isValidUtf8Generic(std::string(m, 16))); |
| 636 | 640 | ||
| 637 | - QVERIFY(isValidUtf8("Straƀe")); // two byte chars | ||
| 638 | - QVERIFY(isValidUtf8("StraƀeHelloHelloHelloHelloHelloHello")); // two byte chars | ||
| 639 | - QVERIFY(isValidUtf8("HelloHelloHelloHelloHelloHelloHelloHelloStraƀeHelloHelloHelloHelloHelloHello")); // two byte chars | 641 | + QVERIFY(isValidUtf8Generic("Straƀe")); // two byte chars |
| 642 | + QVERIFY(isValidUtf8Generic("StraƀeHelloHelloHelloHelloHelloHello")); // two byte chars | ||
| 643 | + QVERIFY(isValidUtf8Generic("HelloHelloHelloHelloHelloHelloHelloHelloStraƀeHelloHelloHelloHelloHelloHello")); // two byte chars | ||
| 640 | 644 | ||
| 641 | std::memset(m, 0, 16); | 645 | std::memset(m, 0, 16); |
| 642 | m[0] = 'a'; | 646 | m[0] = 'a'; |
| 643 | m[1] = 13; // is \r | 647 | m[1] = 13; // is \r |
| 644 | - QVERIFY(!isValidUtf8(std::string(m, 16))); | 648 | + QVERIFY(!isValidUtf8Generic(std::string(m, 16))); |
| 645 | 649 | ||
| 646 | const std::string unicode_ballet_shoes("🩰"); | 650 | const std::string unicode_ballet_shoes("🩰"); |
| 647 | QVERIFY(unicode_ballet_shoes.length() == 4); | 651 | QVERIFY(unicode_ballet_shoes.length() == 4); |
| 648 | - QVERIFY(isValidUtf8(unicode_ballet_shoes)); | 652 | + QVERIFY(isValidUtf8Generic(unicode_ballet_shoes)); |
| 649 | 653 | ||
| 650 | const std::string unicode_ballot_box("☐"); | 654 | const std::string unicode_ballot_box("☐"); |
| 651 | QVERIFY(unicode_ballot_box.length() == 3); | 655 | QVERIFY(unicode_ballot_box.length() == 3); |
| 652 | - QVERIFY(isValidUtf8(unicode_ballot_box)); | 656 | + QVERIFY(isValidUtf8Generic(unicode_ballot_box)); |
| 653 | 657 | ||
| 654 | std::memset(m, 0, 16); | 658 | std::memset(m, 0, 16); |
| 655 | m[0] = 0b11000001; // Start 2 byte char | 659 | m[0] = 0b11000001; // Start 2 byte char |
| 656 | m[1] = 0b00000001; // Next byte doesn't start with 1, which is wrong | 660 | m[1] = 0b00000001; // Next byte doesn't start with 1, which is wrong |
| 657 | std::string a(m, 2); | 661 | std::string a(m, 2); |
| 658 | - QVERIFY(!isValidUtf8(a)); | 662 | + QVERIFY(!isValidUtf8Generic(a)); |
| 659 | 663 | ||
| 660 | std::memset(m, 0, 16); | 664 | std::memset(m, 0, 16); |
| 661 | m[0] = 0b11100001; // Start 3 byte char | 665 | m[0] = 0b11100001; // Start 3 byte char |
| 662 | m[1] = 0b10100001; | 666 | m[1] = 0b10100001; |
| 663 | m[2] = 0b00000001; // Next byte doesn't start with 1, which is wrong | 667 | m[2] = 0b00000001; // Next byte doesn't start with 1, which is wrong |
| 664 | std::string b(m, 3); | 668 | std::string b(m, 3); |
| 665 | - QVERIFY(!isValidUtf8(b)); | 669 | + QVERIFY(!isValidUtf8Generic(b)); |
| 666 | 670 | ||
| 667 | std::memset(m, 0, 16); | 671 | std::memset(m, 0, 16); |
| 668 | m[0] = 0b11110001; // Start 4 byte char | 672 | m[0] = 0b11110001; // Start 4 byte char |
| @@ -670,7 +674,7 @@ void MainTests::test_validUtf8() | @@ -670,7 +674,7 @@ void MainTests::test_validUtf8() | ||
| 670 | m[2] = 0b10100001; | 674 | m[2] = 0b10100001; |
| 671 | m[3] = 0b00000001; // Next byte doesn't start with 1, which is wrong | 675 | m[3] = 0b00000001; // Next byte doesn't start with 1, which is wrong |
| 672 | std::string c(m, 4); | 676 | std::string c(m, 4); |
| 673 | - QVERIFY(!isValidUtf8(c)); | 677 | + QVERIFY(!isValidUtf8Generic(c)); |
| 674 | 678 | ||
| 675 | std::memset(m, 0, 16); | 679 | std::memset(m, 0, 16); |
| 676 | m[0] = 0b11110001; // Start 4 byte char | 680 | m[0] = 0b11110001; // Start 4 byte char |
| @@ -678,18 +682,18 @@ void MainTests::test_validUtf8() | @@ -678,18 +682,18 @@ void MainTests::test_validUtf8() | ||
| 678 | m[2] = 0b00100001; // Doesn't start with 1: invalid. | 682 | m[2] = 0b00100001; // Doesn't start with 1: invalid. |
| 679 | m[3] = 0b10000001; | 683 | m[3] = 0b10000001; |
| 680 | std::string d(m, 4); | 684 | std::string d(m, 4); |
| 681 | - QVERIFY(!isValidUtf8(d)); | 685 | + QVERIFY(!isValidUtf8Generic(d)); |
| 682 | 686 | ||
| 683 | // Upper ASCII, invalid | 687 | // Upper ASCII, invalid |
| 684 | std::memset(m, 0, 16); | 688 | std::memset(m, 0, 16); |
| 685 | m[0] = 127; | 689 | m[0] = 127; |
| 686 | std::string e(m, 1); | 690 | std::string e(m, 1); |
| 687 | - QVERIFY(!isValidUtf8(e)); | 691 | + QVERIFY(!isValidUtf8Generic(e)); |
| 688 | } | 692 | } |
| 689 | 693 | ||
| 690 | void MainTests::test_validUtf8Sse() | 694 | void MainTests::test_validUtf8Sse() |
| 691 | { | 695 | { |
| 692 | - Utils data; | 696 | + SimdUtils data; |
| 693 | 697 | ||
| 694 | char m[16]; | 698 | char m[16]; |
| 695 | 699 | ||
| @@ -776,6 +780,33 @@ void MainTests::testPacketInt16Parse() | @@ -776,6 +780,33 @@ void MainTests::testPacketInt16Parse() | ||
| 776 | } | 780 | } |
| 777 | } | 781 | } |
| 778 | 782 | ||
| 783 | +void MainTests::testTopicsMatch() | ||
| 784 | +{ | ||
| 785 | + QVERIFY(topicsMatch("#", "")); | ||
| 786 | + QVERIFY(topicsMatch("#", "asdf/b/sdf")); | ||
| 787 | + QVERIFY(topicsMatch("#", "+/b/sdf")); | ||
| 788 | + QVERIFY(topicsMatch("#", "/one/two/asdf")); | ||
| 789 | + QVERIFY(topicsMatch("#", "/one/two/asdf/")); | ||
| 790 | + QVERIFY(topicsMatch("+/+/+/+/+", "/one/two/asdf/")); | ||
| 791 | + QVERIFY(topicsMatch("+/+/#", "/one/two/asdf/")); | ||
| 792 | + QVERIFY(topicsMatch("+/+/#", "/1234567890abcdef/two/asdf/")); | ||
| 793 | + QVERIFY(topicsMatch("+/+/#", "/1234567890abcdefg/two/asdf/")); | ||
| 794 | + QVERIFY(topicsMatch("+/+/#", "/1234567890abcde/two/asdf/")); | ||
| 795 | + QVERIFY(topicsMatch("+/+/#", "1234567890abcde//two/asdf/")); | ||
| 796 | + | ||
| 797 | + QVERIFY(!topicsMatch("+/santa", "/one/two/asdf/")); | ||
| 798 | + QVERIFY(!topicsMatch("+/+/+/+/", "/one/two/asdf/a")); | ||
| 799 | + QVERIFY(!topicsMatch("+/one/+/+/", "/one/two/asdf/a")); | ||
| 800 | + | ||
| 801 | + QVERIFY(topicsMatch("$SYS/cow", "$SYS/cow")); | ||
| 802 | + QVERIFY(topicsMatch("$SYS/cow/+", "$SYS/cow/bla")); | ||
| 803 | + QVERIFY(topicsMatch("$SYS/#", "$SYS/broker/clients/connected")); | ||
| 804 | + | ||
| 805 | + QVERIFY(!topicsMatch("$SYS/cow/+", "$SYS/cow/bla/foobar")); | ||
| 806 | + QVERIFY(!topicsMatch("#", "$SYS/cow")); | ||
| 807 | + | ||
| 808 | +} | ||
| 809 | + | ||
| 779 | QTEST_GUILESS_MAIN(MainTests) | 810 | QTEST_GUILESS_MAIN(MainTests) |
| 780 | 811 | ||
| 781 | #include "tst_maintests.moc" | 812 | #include "tst_maintests.moc" |
main.cpp
| @@ -84,14 +84,11 @@ int main(int argc, char *argv[]) | @@ -84,14 +84,11 @@ int main(int argc, char *argv[]) | ||
| 84 | check<std::runtime_error>(register_signal_handers()); | 84 | check<std::runtime_error>(register_signal_handers()); |
| 85 | 85 | ||
| 86 | std::string sse = "without SSE support"; | 86 | std::string sse = "without SSE support"; |
| 87 | -#ifdef __SSE2__ | ||
| 88 | - sse = "with SSE2 support"; | ||
| 89 | -#endif | ||
| 90 | #ifdef __SSE4_2__ | 87 | #ifdef __SSE4_2__ |
| 91 | sse = "with SSE4.2 support"; | 88 | sse = "with SSE4.2 support"; |
| 92 | #endif | 89 | #endif |
| 93 | #ifdef NDEBUG | 90 | #ifdef NDEBUG |
| 94 | - logger->logf(LOG_NOTICE, "Starting FlashMQ version %s, release build.", VERSION, sse.c_str()); | 91 | + logger->logf(LOG_NOTICE, "Starting FlashMQ version %s, release build %s.", VERSION, sse.c_str()); |
| 95 | #else | 92 | #else |
| 96 | logger->logf(LOG_NOTICE, "Starting FlashMQ version %s, debug build %s.", VERSION, sse.c_str()); | 93 | logger->logf(LOG_NOTICE, "Starting FlashMQ version %s, debug build %s.", VERSION, sse.c_str()); |
| 97 | #endif | 94 | #endif |
mainapp.cpp
| @@ -340,10 +340,11 @@ void MainApp::publishStatsOnDollarTopic() | @@ -340,10 +340,11 @@ void MainApp::publishStatsOnDollarTopic() | ||
| 340 | 340 | ||
| 341 | void MainApp::publishStat(const std::string &topic, uint64_t n) | 341 | void MainApp::publishStat(const std::string &topic, uint64_t n) |
| 342 | { | 342 | { |
| 343 | - std::vector<std::string> *subtopics = utils.splitTopic(topic); | 343 | + std::vector<std::string> subtopics; |
| 344 | + splitTopic(topic, subtopics); | ||
| 344 | const std::string payload = std::to_string(n); | 345 | const std::string payload = std::to_string(n); |
| 345 | Publish p(topic, payload, 0); | 346 | Publish p(topic, payload, 0); |
| 346 | - subscriptionStore->queuePacketAtSubscribers(*subtopics, p, true); | 347 | + subscriptionStore->queuePacketAtSubscribers(subtopics, p, true); |
| 347 | subscriptionStore->setRetainedMessage(topic, payload, 0); | 348 | subscriptionStore->setRetainedMessage(topic, payload, 0); |
| 348 | } | 349 | } |
| 349 | 350 |
mainapp.h
| @@ -41,7 +41,6 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | @@ -41,7 +41,6 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | ||
| 41 | #include "timer.h" | 41 | #include "timer.h" |
| 42 | #include "scopedsocket.h" | 42 | #include "scopedsocket.h" |
| 43 | #include "oneinstancelock.h" | 43 | #include "oneinstancelock.h" |
| 44 | -#include "threadlocalutils.h" | ||
| 45 | 44 | ||
| 46 | #define VERSION "0.7.0" | 45 | #define VERSION "0.7.0" |
| 47 | 46 | ||
| @@ -65,7 +64,6 @@ class MainApp | @@ -65,7 +64,6 @@ class MainApp | ||
| 65 | std::mutex quitMutex; | 64 | std::mutex quitMutex; |
| 66 | std::string fuzzFilePath; | 65 | std::string fuzzFilePath; |
| 67 | OneInstanceLock oneInstanceLock; | 66 | OneInstanceLock oneInstanceLock; |
| 68 | - Utils utils; | ||
| 69 | 67 | ||
| 70 | Logger *logger = Logger::getInstance(); | 68 | Logger *logger = Logger::getInstance(); |
| 71 | 69 |
mqttpacket.cpp
| @@ -23,9 +23,9 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | @@ -23,9 +23,9 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | ||
| 23 | 23 | ||
| 24 | #include "utils.h" | 24 | #include "utils.h" |
| 25 | 25 | ||
| 26 | -#include "threadlocalutils.h" | ||
| 27 | - | ||
| 28 | -thread_local Utils utils; | 26 | +// We can void constant reallocation of space for parsed subtopics by using this. But, beware to only use it during handling of the current |
| 27 | +// packet. Don't access it for a stored packet, because then it will have changed. | ||
| 28 | +thread_local std::vector<std::string> gSubtopics; | ||
| 29 | 29 | ||
| 30 | RemainingLength::RemainingLength() | 30 | RemainingLength::RemainingLength() |
| 31 | { | 31 | { |
| @@ -106,7 +106,8 @@ MqttPacket::MqttPacket(const Publish &publish) : | @@ -106,7 +106,8 @@ MqttPacket::MqttPacket(const Publish &publish) : | ||
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | this->topic = publish.topic; | 108 | this->topic = publish.topic; |
| 109 | - this->subtopics = utils.splitTopic(this->topic); | 109 | + this->subtopics = &gSubtopics; |
| 110 | + splitTopic(this->topic, gSubtopics); | ||
| 110 | 111 | ||
| 111 | packetType = PacketType::PUBLISH; | 112 | packetType = PacketType::PUBLISH; |
| 112 | this->qos = publish.qos; | 113 | this->qos = publish.qos; |
| @@ -299,7 +300,7 @@ void MqttPacket::handleConnect() | @@ -299,7 +300,7 @@ void MqttPacket::handleConnect() | ||
| 299 | } | 300 | } |
| 300 | 301 | ||
| 301 | // The specs don't really say what to do when client id not UTF8, so including here. | 302 | // The specs don't really say what to do when client id not UTF8, so including here. |
| 302 | - if (!utils.isValidUtf8(client_id) || !utils.isValidUtf8(username) || !utils.isValidUtf8(password) || !utils.isValidUtf8(will_topic)) | 303 | + if (!isValidUtf8(client_id) || !isValidUtf8(username) || !isValidUtf8(password) || !isValidUtf8(will_topic)) |
| 303 | { | 304 | { |
| 304 | ConnAck connAck(ConnAckReturnCodes::MalformedUsernameOrPassword); | 305 | ConnAck connAck(ConnAckReturnCodes::MalformedUsernameOrPassword); |
| 305 | MqttPacket response(connAck); | 306 | MqttPacket response(connAck); |
| @@ -419,7 +420,7 @@ void MqttPacket::handleSubscribe() | @@ -419,7 +420,7 @@ void MqttPacket::handleSubscribe() | ||
| 419 | uint16_t topicLength = readTwoBytesToUInt16(); | 420 | uint16_t topicLength = readTwoBytesToUInt16(); |
| 420 | std::string topic(readBytes(topicLength), topicLength); | 421 | std::string topic(readBytes(topicLength), topicLength); |
| 421 | 422 | ||
| 422 | - if (topic.empty() || !utils.isValidUtf8(topic)) | 423 | + if (topic.empty() || !isValidUtf8(topic)) |
| 423 | throw ProtocolError("Subscribe topic not valid UTF-8."); | 424 | throw ProtocolError("Subscribe topic not valid UTF-8."); |
| 424 | 425 | ||
| 425 | if (!isValidSubscribePath(topic)) | 426 | if (!isValidSubscribePath(topic)) |
| @@ -438,6 +439,7 @@ void MqttPacket::handleSubscribe() | @@ -438,6 +439,7 @@ void MqttPacket::handleSubscribe() | ||
| 438 | SubAck subAck(packet_id, subs_reponse_codes); | 439 | SubAck subAck(packet_id, subs_reponse_codes); |
| 439 | MqttPacket response(subAck); | 440 | MqttPacket response(subAck); |
| 440 | sender->writeMqttPacket(response); | 441 | sender->writeMqttPacket(response); |
| 442 | + this->subtopics = nullptr; | ||
| 441 | } | 443 | } |
| 442 | 444 | ||
| 443 | void MqttPacket::handleUnsubscribe() | 445 | void MqttPacket::handleUnsubscribe() |
| @@ -454,7 +456,7 @@ void MqttPacket::handleUnsubscribe() | @@ -454,7 +456,7 @@ void MqttPacket::handleUnsubscribe() | ||
| 454 | uint16_t topicLength = readTwoBytesToUInt16(); | 456 | uint16_t topicLength = readTwoBytesToUInt16(); |
| 455 | std::string topic(readBytes(topicLength), topicLength); | 457 | std::string topic(readBytes(topicLength), topicLength); |
| 456 | 458 | ||
| 457 | - if (topic.empty() || !utils.isValidUtf8(topic)) | 459 | + if (topic.empty() || !isValidUtf8(topic)) |
| 458 | throw ProtocolError("Subscribe topic not valid UTF-8."); | 460 | throw ProtocolError("Subscribe topic not valid UTF-8."); |
| 459 | 461 | ||
| 460 | sender->getThreadData()->getSubscriptionStore()->removeSubscription(sender, topic); | 462 | sender->getThreadData()->getSubscriptionStore()->removeSubscription(sender, topic); |
| @@ -485,9 +487,10 @@ void MqttPacket::handlePublish() | @@ -485,9 +487,10 @@ void MqttPacket::handlePublish() | ||
| 485 | throw ProtocolError("Duplicate flag is set for QoS 0 packet. This is illegal."); | 487 | throw ProtocolError("Duplicate flag is set for QoS 0 packet. This is illegal."); |
| 486 | 488 | ||
| 487 | topic = std::string(readBytes(variable_header_length), variable_header_length); | 489 | topic = std::string(readBytes(variable_header_length), variable_header_length); |
| 488 | - subtopics = utils.splitTopic(topic); | 490 | + subtopics = &gSubtopics; |
| 491 | + splitTopic(topic, gSubtopics); | ||
| 489 | 492 | ||
| 490 | - if (!utils.isValidUtf8(topic, true)) | 493 | + if (!isValidUtf8(topic, true)) |
| 491 | { | 494 | { |
| 492 | logger->logf(LOG_WARNING, "Client '%s' published a message with invalid UTF8 or +/# in it. Dropping.", sender->repr().c_str()); | 495 | logger->logf(LOG_WARNING, "Client '%s' published a message with invalid UTF8 or +/# in it. Dropping.", sender->repr().c_str()); |
| 493 | return; | 496 | return; |
| @@ -546,6 +549,7 @@ void MqttPacket::handlePublish() | @@ -546,6 +549,7 @@ void MqttPacket::handlePublish() | ||
| 546 | // For the existing clients, we can just write the same packet back out, with our small alterations. | 549 | // For the existing clients, we can just write the same packet back out, with our small alterations. |
| 547 | sender->getThreadData()->getSubscriptionStore()->queuePacketAtSubscribers(*subtopics, *this); | 550 | sender->getThreadData()->getSubscriptionStore()->queuePacketAtSubscribers(*subtopics, *this); |
| 548 | } | 551 | } |
| 552 | + this->subtopics = nullptr; | ||
| 549 | } | 553 | } |
| 550 | 554 | ||
| 551 | void MqttPacket::handlePubAck() | 555 | void MqttPacket::handlePubAck() |
| @@ -678,6 +682,10 @@ const std::string &MqttPacket::getTopic() const | @@ -678,6 +682,10 @@ const std::string &MqttPacket::getTopic() const | ||
| 678 | return this->topic; | 682 | return this->topic; |
| 679 | } | 683 | } |
| 680 | 684 | ||
| 685 | +/** | ||
| 686 | + * @brief MqttPacket::getSubtopics returns a pointer to the parsed subtopics. Use with care! | ||
| 687 | + * @return a pointer to a vector of subtopics that will be overwritten the next packet! | ||
| 688 | + */ | ||
| 681 | const std::vector<std::string> *MqttPacket::getSubtopics() const | 689 | const std::vector<std::string> *MqttPacket::getSubtopics() const |
| 682 | { | 690 | { |
| 683 | return this->subtopics; | 691 | return this->subtopics; |
mqttpacket.h
| @@ -48,7 +48,7 @@ class MqttPacket | @@ -48,7 +48,7 @@ class MqttPacket | ||
| 48 | #endif | 48 | #endif |
| 49 | 49 | ||
| 50 | std::string topic; | 50 | std::string topic; |
| 51 | - std::vector<std::string> *subtopics; // comes from local thread storage. See std::vector<std::string> *ThreadData::splitTopic(std::string &topic) | 51 | + std::vector<std::string> *subtopics = nullptr; |
| 52 | std::vector<char> bites; | 52 | std::vector<char> bites; |
| 53 | size_t fixed_header_length = 0; // if 0, this packet does not contain the bytes of the fixed header. | 53 | size_t fixed_header_length = 0; // if 0, this packet does not contain the bytes of the fixed header. |
| 54 | RemainingLength remainingLength; | 54 | RemainingLength remainingLength; |
threadlocalutils.cpp
| 1 | +#ifdef __SSE4_2__ | ||
| 2 | + | ||
| 1 | #include "threadlocalutils.h" | 3 | #include "threadlocalutils.h" |
| 2 | 4 | ||
| 3 | #include <cstring> | 5 | #include <cstring> |
| 4 | #include <cassert> | 6 | #include <cassert> |
| 5 | 7 | ||
| 6 | -Utils::Utils() : | 8 | +SimdUtils::SimdUtils() : |
| 7 | subtopicParseMem(TOPIC_MEMORY_LENGTH), | 9 | subtopicParseMem(TOPIC_MEMORY_LENGTH), |
| 8 | topicCopy(TOPIC_MEMORY_LENGTH) | 10 | topicCopy(TOPIC_MEMORY_LENGTH) |
| 9 | { | 11 | { |
| @@ -11,15 +13,14 @@ Utils::Utils() : | @@ -11,15 +13,14 @@ Utils::Utils() : | ||
| 11 | } | 13 | } |
| 12 | 14 | ||
| 13 | /** | 15 | /** |
| 14 | - * @brief ThreadData::splitTopic uses SSE4.2 to detect the '/' chars, 16 chars at a time, and returns a pointer to thread-local memory. | ||
| 15 | - * @param topic string is altered: some extra space is reserved. | ||
| 16 | - * @return Pointer to thread-owned vector of subtopics. | ||
| 17 | - * | ||
| 18 | - * Because it returns a pointer to the thread-local vector, only the current thread should touch it. | 16 | + * @brief SimdUtils::splitTopic uses SSE4.2 to detect the '/' chars, 16 chars at a time, and returns a pointer to thread-local memory. |
| 17 | + * @param topic | ||
| 18 | + * @param output is cleared and emplaced in. You can give it members from the Utils class, to avoid re-allocation. | ||
| 19 | + * @return | ||
| 19 | */ | 20 | */ |
| 20 | -std::vector<std::string> *Utils::splitTopic(const std::string &topic) | 21 | +std::vector<std::string> *SimdUtils::splitTopic(const std::string &topic, std::vector<std::string> &output) |
| 21 | { | 22 | { |
| 22 | - subtopics.clear(); | 23 | + output.clear(); |
| 23 | 24 | ||
| 24 | const int s = topic.size(); | 25 | const int s = topic.size(); |
| 25 | std::memcpy(topicCopy.data(), topic.c_str(), s+1); | 26 | std::memcpy(topicCopy.data(), topic.c_str(), s+1); |
| @@ -41,16 +42,16 @@ std::vector<std::string> *Utils::splitTopic(const std::string &topic) | @@ -41,16 +42,16 @@ std::vector<std::string> *Utils::splitTopic(const std::string &topic) | ||
| 41 | 42 | ||
| 42 | if (index < 16 || n >= s) | 43 | if (index < 16 || n >= s) |
| 43 | { | 44 | { |
| 44 | - subtopics.emplace_back(subtopicParseMem.data(), carryi); | 45 | + output.emplace_back(subtopicParseMem.data(), carryi); |
| 45 | carryi = 0; | 46 | carryi = 0; |
| 46 | n++; | 47 | n++; |
| 47 | } | 48 | } |
| 48 | } | 49 | } |
| 49 | 50 | ||
| 50 | - return &subtopics; | 51 | + return &output; |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | -bool Utils::isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars) | 54 | +bool SimdUtils::isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars) |
| 54 | { | 55 | { |
| 55 | const int len = s.size(); | 56 | const int len = s.size(); |
| 56 | 57 | ||
| @@ -136,3 +137,5 @@ bool Utils::isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars) | @@ -136,3 +137,5 @@ bool Utils::isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars) | ||
| 136 | 137 | ||
| 137 | return true; | 138 | return true; |
| 138 | } | 139 | } |
| 140 | + | ||
| 141 | +#endif |
threadlocalutils.h
| 1 | #ifndef THREADLOCALUTILS_H | 1 | #ifndef THREADLOCALUTILS_H |
| 2 | #define THREADLOCALUTILS_H | 2 | #define THREADLOCALUTILS_H |
| 3 | 3 | ||
| 4 | +#ifdef __SSE4_2__ | ||
| 5 | + | ||
| 4 | #include <vector> | 6 | #include <vector> |
| 5 | #include <string> | 7 | #include <string> |
| 6 | #include <immintrin.h> | 8 | #include <immintrin.h> |
| 7 | 9 | ||
| 8 | #define TOPIC_MEMORY_LENGTH 65560 | 10 | #define TOPIC_MEMORY_LENGTH 65560 |
| 9 | 11 | ||
| 10 | -/** | ||
| 11 | - * @brief The Utils class have utility functions that make use of pre-allocated memory. Use with thread_local or create per thread manually. | ||
| 12 | - */ | ||
| 13 | -class Utils | 12 | + |
| 13 | + | ||
| 14 | +class SimdUtils | ||
| 14 | { | 15 | { |
| 15 | - std::vector<std::string> subtopics; | ||
| 16 | std::vector<char> subtopicParseMem; | 16 | std::vector<char> subtopicParseMem; |
| 17 | std::vector<char> topicCopy; | 17 | std::vector<char> topicCopy; |
| 18 | __m128i slashes = _mm_set1_epi8('/'); | 18 | __m128i slashes = _mm_set1_epi8('/'); |
| @@ -23,11 +23,13 @@ class Utils | @@ -23,11 +23,13 @@ class Utils | ||
| 23 | __m128i plus = _mm_set1_epi8('+'); | 23 | __m128i plus = _mm_set1_epi8('+'); |
| 24 | 24 | ||
| 25 | public: | 25 | public: |
| 26 | - Utils(); | 26 | + SimdUtils(); |
| 27 | 27 | ||
| 28 | - std::vector<std::string> *splitTopic(const std::string &topic); | 28 | + std::vector<std::string> *splitTopic(const std::string &topic, std::vector<std::string> &output); |
| 29 | bool isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars = false); | 29 | bool isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars = false); |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | +#endif | ||
| 33 | + | ||
| 32 | 34 | ||
| 33 | #endif // THREADLOCALUTILS_H | 35 | #endif // THREADLOCALUTILS_H |
utils.cpp
| @@ -34,6 +34,12 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | @@ -34,6 +34,12 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | ||
| 34 | #include "logger.h" | 34 | #include "logger.h" |
| 35 | #include "evpencodectxmanager.h" | 35 | #include "evpencodectxmanager.h" |
| 36 | 36 | ||
| 37 | + | ||
| 38 | +#ifdef __SSE4_2__ | ||
| 39 | +#include "threadlocalutils.h" | ||
| 40 | +thread_local SimdUtils simdUtils; | ||
| 41 | +#endif | ||
| 42 | + | ||
| 37 | std::list<std::string> split(const std::string &input, const char sep, size_t max, bool keep_empty_parts) | 43 | std::list<std::string> split(const std::string &input, const char sep, size_t max, bool keep_empty_parts) |
| 38 | { | 44 | { |
| 39 | std::list<std::string> list; | 45 | std::list<std::string> list; |
| @@ -60,8 +66,16 @@ bool topicsMatch(const std::string &subscribeTopic, const std::string &publishTo | @@ -60,8 +66,16 @@ bool topicsMatch(const std::string &subscribeTopic, const std::string &publishTo | ||
| 60 | if (!subscribeTopic.empty() && !publishTopic.empty() && publishTopic[0] == '$' && subscribeTopic[0] != '$') | 66 | if (!subscribeTopic.empty() && !publishTopic.empty() && publishTopic[0] == '$' && subscribeTopic[0] != '$') |
| 61 | return false; | 67 | return false; |
| 62 | 68 | ||
| 63 | - const std::vector<std::string> subscribeParts = splitToVector(subscribeTopic, '/'); | ||
| 64 | - const std::vector<std::string> publishParts = splitToVector(publishTopic, '/'); | 69 | + std::vector<std::string> subscribeParts; |
| 70 | + std::vector<std::string> publishParts; | ||
| 71 | + | ||
| 72 | +#ifdef __SSE4_2__ | ||
| 73 | + simdUtils.splitTopic(subscribeTopic, subscribeParts); | ||
| 74 | + simdUtils.splitTopic(publishTopic, publishParts); | ||
| 75 | +#else | ||
| 76 | + splitToVector(subscribeTopic, subscribeParts, '/'); | ||
| 77 | + splitToVector(publishTopic, publishParts, '/'); | ||
| 78 | +#endif | ||
| 65 | 79 | ||
| 66 | auto subscribe_itr = subscribeParts.begin(); | 80 | auto subscribe_itr = subscribeParts.begin(); |
| 67 | auto publish_itr = publishParts.begin(); | 81 | auto publish_itr = publishParts.begin(); |
| @@ -84,7 +98,7 @@ bool topicsMatch(const std::string &subscribeTopic, const std::string &publishTo | @@ -84,7 +98,7 @@ bool topicsMatch(const std::string &subscribeTopic, const std::string &publishTo | ||
| 84 | return result; | 98 | return result; |
| 85 | } | 99 | } |
| 86 | 100 | ||
| 87 | -bool isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars) | 101 | +bool isValidUtf8Generic(const std::string &s, bool alsoCheckInvalidPublishChars) |
| 88 | { | 102 | { |
| 89 | int multibyte_remain = 0; | 103 | int multibyte_remain = 0; |
| 90 | int cur_code_point = 0; | 104 | int cur_code_point = 0; |
| @@ -146,6 +160,15 @@ bool isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars) | @@ -146,6 +160,15 @@ bool isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars) | ||
| 146 | return multibyte_remain == 0; | 160 | return multibyte_remain == 0; |
| 147 | } | 161 | } |
| 148 | 162 | ||
| 163 | +bool isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars) | ||
| 164 | +{ | ||
| 165 | +#ifdef __SSE4_2__ | ||
| 166 | + return simdUtils.isValidUtf8(s, alsoCheckInvalidPublishChars); | ||
| 167 | +#else | ||
| 168 | + return isValidUtf8Generic(s, alsoCheckInvalidPublishChars); | ||
| 169 | +#endif | ||
| 170 | +} | ||
| 171 | + | ||
| 149 | bool strContains(const std::string &s, const std::string &needle) | 172 | bool strContains(const std::string &s, const std::string &needle) |
| 150 | { | 173 | { |
| 151 | return s.find(needle) != std::string::npos; | 174 | return s.find(needle) != std::string::npos; |
| @@ -209,25 +232,39 @@ bool containsDangerousCharacters(const std::string &s) | @@ -209,25 +232,39 @@ bool containsDangerousCharacters(const std::string &s) | ||
| 209 | return false; | 232 | return false; |
| 210 | } | 233 | } |
| 211 | 234 | ||
| 212 | -const std::vector<std::string> splitToVector(const std::string &input, const char sep, size_t max, bool keep_empty_parts) | 235 | +void splitTopic(const std::string &topic, std::vector<std::string> &output) |
| 236 | +{ | ||
| 237 | +#ifdef __SSE4_2__ | ||
| 238 | + simdUtils.splitTopic(topic, output); | ||
| 239 | +#else | ||
| 240 | + splitToVector(topic, output, '/'); | ||
| 241 | +#endif | ||
| 242 | +} | ||
| 243 | + | ||
| 244 | +void splitToVector(const std::string &input, std::vector<std::string> &output, const char sep, size_t max, bool keep_empty_parts) | ||
| 213 | { | 245 | { |
| 214 | const auto subtopic_count = std::count(input.begin(), input.end(), '/') + 1; | 246 | const auto subtopic_count = std::count(input.begin(), input.end(), '/') + 1; |
| 215 | 247 | ||
| 216 | - std::vector<std::string> result; | ||
| 217 | - result.reserve(subtopic_count); | 248 | + output.reserve(subtopic_count); |
| 218 | size_t start = 0; | 249 | size_t start = 0; |
| 219 | size_t end; | 250 | size_t end; |
| 220 | 251 | ||
| 221 | const auto npos = std::string::npos; | 252 | const auto npos = std::string::npos; |
| 222 | 253 | ||
| 223 | - while (result.size() < max && (end = input.find(sep, start)) != npos) | 254 | + while (output.size() < max && (end = input.find(sep, start)) != npos) |
| 224 | { | 255 | { |
| 225 | if (start != end || keep_empty_parts) | 256 | if (start != end || keep_empty_parts) |
| 226 | - result.push_back(input.substr(start, end - start)); | 257 | + output.push_back(input.substr(start, end - start)); |
| 227 | start = end + 1; // increase by length of seperator. | 258 | start = end + 1; // increase by length of seperator. |
| 228 | } | 259 | } |
| 229 | if (start != input.size() || keep_empty_parts) | 260 | if (start != input.size() || keep_empty_parts) |
| 230 | - result.push_back(input.substr(start, npos)); | 261 | + output.push_back(input.substr(start, npos)); |
| 262 | +} | ||
| 263 | + | ||
| 264 | +const std::vector<std::string> splitToVector(const std::string &input, const char sep, size_t max, bool keep_empty_parts) | ||
| 265 | +{ | ||
| 266 | + std::vector<std::string> result; | ||
| 267 | + splitToVector(input, result, sep, max, keep_empty_parts); | ||
| 231 | return result; | 268 | return result; |
| 232 | } | 269 | } |
| 233 | 270 |
utils.h
| @@ -46,9 +46,12 @@ template<typename T> int check(int rc) | @@ -46,9 +46,12 @@ template<typename T> int check(int rc) | ||
| 46 | 46 | ||
| 47 | std::list<std::string> split(const std::string &input, const char sep, size_t max = std::numeric_limits<int>::max(), bool keep_empty_parts = true); | 47 | std::list<std::string> split(const std::string &input, const char sep, size_t max = std::numeric_limits<int>::max(), bool keep_empty_parts = true); |
| 48 | const std::vector<std::string> splitToVector(const std::string &input, const char sep, size_t max = std::numeric_limits<int>::max(), bool keep_empty_parts = true); | 48 | const std::vector<std::string> splitToVector(const std::string &input, const char sep, size_t max = std::numeric_limits<int>::max(), bool keep_empty_parts = true); |
| 49 | +void splitToVector(const std::string &input, std::vector<std::string> &output, const char sep, size_t max = std::numeric_limits<int>::max(), bool keep_empty_parts = true); | ||
| 50 | +void splitTopic(const std::string &topic, std::vector<std::string> &output); | ||
| 49 | 51 | ||
| 50 | bool topicsMatch(const std::string &subscribeTopic, const std::string &publishTopic); | 52 | bool topicsMatch(const std::string &subscribeTopic, const std::string &publishTopic); |
| 51 | 53 | ||
| 54 | +bool isValidUtf8Generic(const std::string &s, bool alsoCheckInvalidPublishChars = false); | ||
| 52 | bool isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars = false); | 55 | bool isValidUtf8(const std::string &s, bool alsoCheckInvalidPublishChars = false); |
| 53 | 56 | ||
| 54 | bool strContains(const std::string &s, const std::string &needle); | 57 | bool strContains(const std::string &s, const std::string &needle); |