0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
+
+DNSName::RawLabelsVisitor::RawLabelsVisitor(const DNSName::string_t& storage): d_storage(storage)
+{
+ size_t position = 0;
+ while (position < storage.size()) {
+ auto labelLength = static_cast<unsigned char>(storage.at(position));
+ if (labelLength == 0) {
+ break;
+ }
+ d_labelPositions.at(d_position) = position;
+ d_position++;
+ position += labelLength + 1;
+ }
+}
+
+DNSName::RawLabelsVisitor DNSName::getRawLabelsVisitor() const
+{
+ return DNSName::RawLabelsVisitor(getStorage());
+}
+
+std::string_view DNSName::RawLabelsVisitor::front() const
+{
+ if (d_position == 0) {
+ throw std::out_of_range("trying to access the front of an empty DNSName::RawLabelsVisitor");
+ }
+ uint8_t length = d_storage.at(0);
+ if (length == 0) {
+ return std::string_view();
+ }
+ return std::string_view(&d_storage.at(1), length);
+}
+
+std::string_view DNSName::RawLabelsVisitor::back() const
+{
+ if (d_position == 0) {
+ throw std::out_of_range("trying to access the back of an empty DNSName::RawLabelsVisitor");
+ }
+ size_t offset = d_labelPositions.at(d_position-1);
+ uint8_t length = d_storage.at(offset);
+ if (length == 0) {
+ return std::string_view();
+ }
+ return std::string_view(&d_storage.at(offset + 1), length);
+}
+
+bool DNSName::RawLabelsVisitor::pop_back()
+{
+ if (d_position > 0) {
+ d_position--;
+ return true;
+ }
+ return false;
+}
+
+bool DNSName::RawLabelsVisitor::empty() const
+{
+ return d_position == 0;
+}
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
+#include <array>
#include <cstring>
#include <string>
#include <vector>
bool has8bitBytes() const; /* returns true if at least one byte of the labels forming the name is not included in [A-Za-z0-9_*./@ \\:-] */
+ class RawLabelsVisitor
+ {
+ public:
+ /* Zero-copy, zero-allocation raw labels visitor.
+ The general idea is that we walk the labels in the constructor,
+ filling up our array of labels position and setting the initial
+ value of d_position at the number of labels.
+ We then can easily provide string_view into the first and last label.
+ pop_back() moves d_position one label closer to the start, so we
+ can also easily walk back the labels in reverse order.
+ There is no copy because we use a reference into the DNSName storage,
+ so it is absolutely forbidden to alter the DNSName for as long as we
+ exist, and no allocation because we use a static array (there cannot
+ be more than 128 labels in a DNSName).
+ */
+ RawLabelsVisitor(const string_t& storage);
+ std::string_view front() const;
+ std::string_view back() const;
+ bool pop_back();
+ bool empty() const;
+ private:
+ std::array<uint8_t, 128> d_labelPositions;
+ const string_t& d_storage;
+ size_t d_position{0};
+ };
+ RawLabelsVisitor getRawLabelsVisitor() const;
+
private:
string_t d_storage;
return nullptr;
}
- auto labels = name.getRawLabels();
- return lookup(labels);
+ auto visitor = name.getRawLabelsVisitor();
+ return lookup(visitor);
}
- T* lookup(std::vector<std::string>& labels) const
+ T* lookup(DNSName::RawLabelsVisitor& visitor) const
{
- if (labels.empty()) { // optimization
+ if (visitor.empty()) { // optimization
if (endNode) {
return &d_value;
}
return nullptr;
}
- SuffixMatchTree smn(*labels.rbegin());
+ SuffixMatchTree smn(std::string(visitor.back()));
auto child = children.find(smn);
if (child == children.end()) {
if(endNode) {
}
return nullptr;
}
- labels.pop_back();
- auto result = child->lookup(labels);
+ visitor.pop_back();
+ auto result = child->lookup(visitor);
if (result) {
return result;
}
};
+struct SuffixMatchNodeTest
+{
+ SuffixMatchNodeTest()
+ {
+ d_smn.add(d_exist);
+ }
+
+ string getName() const
+ {
+ return "SuffixMatchNode";
+ }
+
+ void operator()() const
+ {
+ if (!d_smn.check(d_exist)) {
+ throw std::runtime_error("Entry not found in SuffixMatchNodeTest");
+ }
+ if (d_smn.check(d_does_not_exist)) {
+ throw std::runtime_error("Non-existent entry found in SuffixMatchNodeTest");
+ }
+ }
+private:
+ const DNSName d_exist{"a.bb.ccc.ddd."};
+ const DNSName d_does_not_exist{"e.bb.ccc.ddd"};
+ SuffixMatchNode d_smn;
+};
struct IEqualsTest
{
doRun(DNSNameParseTest());
doRun(DNSNameRootTest());
+ doRun(SuffixMatchNodeTest());
+
doRun(NetmaskTreeTest());
doRun(UUIDGenTest());
BOOST_CHECK_THROW(name.getRawLabel(name.countLabels()), std::out_of_range);
}
+BOOST_AUTO_TEST_CASE(test_getrawlabels_visitor) {
+ DNSName name("a.bb.ccc.dddd.");
+ auto visitor = name.getRawLabelsVisitor();
+ BOOST_CHECK(!visitor.empty());
+ BOOST_CHECK_EQUAL(visitor.front(), *name.getRawLabels().begin());
+ BOOST_CHECK_EQUAL(visitor.back(), *name.getRawLabels().rbegin());
+
+ BOOST_CHECK_EQUAL(visitor.back(), "dddd");
+ BOOST_CHECK(visitor.pop_back());
+ BOOST_CHECK_EQUAL(visitor.back(), "ccc");
+ BOOST_CHECK(visitor.pop_back());
+ BOOST_CHECK_EQUAL(visitor.back(), "bb");
+ BOOST_CHECK(visitor.pop_back());
+ BOOST_CHECK_EQUAL(visitor.back(), "a");
+ BOOST_CHECK(visitor.pop_back());
+ BOOST_CHECK(visitor.empty());
+ BOOST_CHECK(!visitor.pop_back());
+ BOOST_CHECK_THROW(visitor.front(), std::out_of_range);
+ BOOST_CHECK_THROW(visitor.back(), std::out_of_range);
+}
+
BOOST_AUTO_TEST_CASE(test_getlastlabel) {
DNSName name("www.powerdns.com");
DNSName ans = name.getLastLabel();