Browse Source

[master] Merge branch 'trac2100'

JINMEI Tatuya 12 years ago
parent
commit
300c412c3f

+ 4 - 4
src/lib/datasrc/memory/Makefile.am

@@ -10,7 +10,7 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
 
 noinst_LTLIBRARIES = libdatasrc_memory.la
 
-libdatasrc_memory_la_SOURCES = \
-	rdata_encoder.h \
-	rdata_encoder.cc \
-	domaintree.h
+libdatasrc_memory_la_SOURCES = rdata_encoder.h rdata_encoder.cc
+libdatasrc_memory_la_SOURCES += domaintree.h
+libdatasrc_memory_la_SOURCES += zone_data.h
+libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc

+ 17 - 17
src/lib/datasrc/memory/domaintree.h

@@ -229,37 +229,33 @@ public:
     ///
     /// You should not delete the data, it is deleted when the tree is
     /// destroyed.
-    T* getData() { return (data_); }
+    T* getData() { return (data_.get()); }
 
     /// \brief Return the data stored in this node (const).
-    const T* getData() const { return (data_); }
+    const T* getData() const { return (data_.get()); }
 
     /// \brief return whether the node has related data.
     ///
     /// There can be empty nodes inside the DomainTree. They are usually the
     /// non-terminal domains, but it is possible (yet probably meaningless)
     /// empty nodes anywhere.
-    bool isEmpty() const { return (data_ == NULL); }
+    bool isEmpty() const { return (!data_); }
     //@}
 
     /// \name Setter functions.
     //@{
 
-    /// \brief Set the data stored in the node. If there is old data, it
-    /// is either returned or destroyed based on what is passed in \c
-    /// old_data.
+    /// \brief Set the data stored in the node.
+    ///
+    /// Any old data is destroyed.
+    ///
     /// \param mem_sgmt The \c MemorySegment that allocated memory for
     ///                 the node data.
     /// \param data The new data to set.
-    /// \param old_data If \c NULL is passed here, any old data is
-    ///                 destroyed. Otherwise, the old data is returned
-    ///                 in this location.
-    void setData(util::MemorySegment& mem_sgmt, T* data, T** old_data = NULL) {
-        if (old_data != NULL) {
-            *old_data = data;
-        } else {
+    void setData(util::MemorySegment& mem_sgmt, T* data) {
+        if (data_) {
             const DT deleter;
-            deleter(mem_sgmt, data_);
+            deleter(mem_sgmt, data_.get());
         }
         data_ = data;
     }
@@ -476,7 +472,7 @@ private:
     }
 
     /// \brief Data stored here.
-    T* data_;
+    boost::interprocess::offset_ptr<T> data_;
 
     /// \brief Internal or user-configurable flags of node's properties.
     ///
@@ -1390,7 +1386,7 @@ DomainTree<T, DT>::deleteHelper(util::MemorySegment& mem_sgmt,
             // free this one and go back to its parent.
             DomainTreeNode<T, DT>* node = root;
             root = root->getParent();
-            deleter(mem_sgmt, node->data_);
+            deleter(mem_sgmt, node->data_.get());
             DomainTreeNode<T, DT>::destroy(mem_sgmt, node);
             --node_count_;
         }
@@ -1652,8 +1648,12 @@ DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
     isc::dns::LabelSequence target_labels(target_name);
 
     int order = -1;
+    // For possible LabelSequence serialization we always store labels data
+    // in the separate local buffer.
+    uint8_t labels_buf[dns::LabelSequence::MAX_SERIALIZED_LENGTH];
     while (current != NULL) {
-        const dns::LabelSequence current_labels(current->getLabels());
+        const dns::LabelSequence current_labels(
+            dns::LabelSequence(current->getLabels(), labels_buf));
         const isc::dns::NameComparisonResult compare_result =
             target_labels.compare(current_labels);
         const isc::dns::NameComparisonResult::NameRelation relation =

+ 1 - 0
src/lib/datasrc/memory/tests/Makefile.am

@@ -20,6 +20,7 @@ TESTS += run_unittests
 run_unittests_SOURCES = run_unittests.cc
 run_unittests_SOURCES += rdata_encoder_unittest.cc
 run_unittests_SOURCES += domaintree_unittest.cc
+run_unittests_SOURCES += zone_table_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)

+ 142 - 0
src/lib/datasrc/memory/tests/zone_table_unittest.cc

@@ -0,0 +1,142 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/result.h>
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+
+#include <gtest/gtest.h>
+
+#include <new>                  // for bad_alloc
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+using namespace isc::datasrc::memory;
+
+namespace {
+// Memory segment specified for tests.  It normally behaves like a "local"
+// memory segment.  If "throw count" is set to non 0 via setThrowCount(),
+// it continues the normal behavior up to the specified number of calls to
+// allocate(), and throws an exception at the next call.
+class TestMemorySegment : public isc::util::MemorySegmentLocal {
+public:
+    TestMemorySegment() : throw_count_(0) {}
+    virtual void* allocate(size_t size) {
+        if (throw_count_ > 0) {
+            if (--throw_count_ == 0) {
+                throw std::bad_alloc();
+            }
+        }
+        return (isc::util::MemorySegmentLocal::allocate(size));
+    }
+    void setThrowCount(size_t count) { throw_count_ = count; }
+
+private:
+    size_t throw_count_;
+};
+
+class ZoneTableTest : public ::testing::Test {
+protected:
+    ZoneTableTest() : zname1(Name("example.com")),
+                      zname2(Name("example.net")),
+                      zname3(Name("example")),
+                      zone_table(ZoneTable::create(mem_sgmt_))
+    {}
+    ~ZoneTableTest() {
+        if (zone_table != NULL) {
+            ZoneTable::destroy(mem_sgmt_, zone_table);
+        }
+    }
+    void TearDown() {
+        ZoneTable::destroy(mem_sgmt_, zone_table);
+        zone_table = NULL;
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
+    }
+    const Name zname1, zname2, zname3;
+    TestMemorySegment mem_sgmt_;
+    ZoneTable* zone_table;
+};
+
+TEST_F(ZoneTableTest, create) {
+    // Test about creating a zone table.  Normal case covers through other
+    // tests.  We only check exception safety by letting the test memory
+    // segment throw.
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(ZoneTable::create(mem_sgmt_), std::bad_alloc);
+    // This shouldn't cause memory leak (that would be caught in TearDown()).
+}
+
+TEST_F(ZoneTableTest, addZone) {
+    // Normal successful case.
+    const ZoneTable::AddResult result1 =
+        zone_table->addZone(mem_sgmt_, zname1);
+    EXPECT_EQ(result::SUCCESS, result1.code);
+
+    // Duplicate add doesn't replace the existing data.
+    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zname1).code);
+    EXPECT_EQ(result1.zone_data,
+              zone_table->addZone(mem_sgmt_, zname1).zone_data);
+    // names are compared in a case insensitive manner.
+    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_,
+                                                 Name("EXAMPLE.COM")).code);
+    // Add some more different ones.  Should just succeed.
+    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
+    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
+
+    // Have the memory segment throw an exception in extending the internal
+    // tree.  It still shouldn't cause memory leak (which would be detected
+    // in TearDown()).
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(zone_table->addZone(mem_sgmt_, Name("example.org")),
+                 std::bad_alloc);
+}
+
+TEST_F(ZoneTableTest, findZone) {
+    const ZoneTable::AddResult add_result1 =
+        zone_table->addZone(mem_sgmt_, zname1);
+    EXPECT_EQ(result::SUCCESS, add_result1.code);
+    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
+    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
+
+    const ZoneTable::FindResult find_result1 =
+        zone_table->findZone(Name("example.com"));
+    EXPECT_EQ(result::SUCCESS, find_result1.code);
+    EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
+
+    EXPECT_EQ(result::NOTFOUND,
+              zone_table->findZone(Name("example.org")).code);
+    EXPECT_EQ(NULL, zone_table->findZone(Name("example.org")).zone_data);
+
+    // there's no exact match.  the result should be the longest match,
+    // and the code should be PARTIALMATCH.
+    EXPECT_EQ(result::PARTIALMATCH,
+              zone_table->findZone(Name("www.example.com")).code);
+    EXPECT_EQ(add_result1.zone_data,
+              zone_table->findZone(Name("www.example.com")).zone_data);
+
+    // make sure the partial match is indeed the longest match by adding
+    // a zone with a shorter origin and query again.
+    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_,
+                                                   Name("com")).code);
+    EXPECT_EQ(add_result1.zone_data,
+              zone_table->findZone(Name("www.example.com")).zone_data);
+}
+}

+ 51 - 0
src/lib/datasrc/memory/zone_data.h

@@ -0,0 +1,51 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_ZONE_DATA_H
+#define DATASRC_MEMORY_ZONE_DATA_H 1
+
+#include <util/memory_segment.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+/// \brief Data for a single zone.
+///
+/// It's currently empty and is only provided for the implementation of
+/// ZoneTable.  The actual implementation of this class is the subject of
+/// Trac #2107.
+class ZoneData {
+private:
+    ZoneData() {}
+    ~ZoneData() {}
+public:
+    static ZoneData* create(util::MemorySegment& mem_sgmt) {
+        void* p = mem_sgmt.allocate(sizeof(ZoneData));
+        ZoneData* zone_data = new(p) ZoneData();
+        return (zone_data);
+    }
+    static void destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data) {
+        zone_data->~ZoneData();
+        mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
+    }
+};
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_ZONE_DATA_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 144 - 0
src/lib/datasrc/memory/zone_table.cc

@@ -0,0 +1,144 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/memory_segment.h>
+
+#include <dns/name.h>
+
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+#include <datasrc/memory/domaintree.h>
+
+#include <cassert>
+
+using namespace std;
+using namespace isc::dns;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+namespace {
+// A simple holder to create and use some objects in this implementation
+// in an exception safe manner.   It works like std::auto_ptr but much
+// more simplified.
+template <typename T>
+class Holder {
+public:
+    Holder(util::MemorySegment& mem_sgmt, T* obj) :
+        mem_sgmt_(mem_sgmt), obj_(obj)
+    {}
+    ~Holder() {
+        if (obj_ != NULL) {
+            T::destroy(mem_sgmt_, obj_);
+        }
+    }
+    T* get() { return (obj_); }
+    T* release() {
+        T* ret = obj_;
+        obj_ = NULL;
+        return (ret);
+    }
+private:
+    util::MemorySegment& mem_sgmt_;
+    T* obj_;
+};
+}
+
+void
+ZoneTable::ZoneDataDeleter::operator()(util::MemorySegment& mem_sgmt,
+                                       ZoneData* zone_data) const
+{
+    ZoneData::destroy(mem_sgmt, zone_data);
+}
+
+ZoneTable*
+ZoneTable::create(util::MemorySegment& mem_sgmt) {
+    Holder<ZoneTableTree> holder(mem_sgmt, ZoneTableTree::create(mem_sgmt));
+    void* p = mem_sgmt.allocate(sizeof(ZoneTable));
+    ZoneTable* zone_table = new(p) ZoneTable(holder.get());
+    holder.release();
+    return (zone_table);
+}
+
+void
+ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable) {
+    ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get());
+    mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
+}
+
+ZoneTable::AddResult
+ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
+    // Create a new ZoneData instance first.  If the specified name already
+    // exists in the table, the new data will soon be destroyed, but we want
+    // to make sure if this allocation fails the tree won't be changed to
+    // provide as strong guarantee as possible.  In practice, we generally
+    // expect the caller tries to add a zone only when it's a new one, so
+    // this should be a minor concern.
+    Holder<ZoneData> holder(mem_sgmt, ZoneData::create(mem_sgmt));
+
+    // Get the node where we put the zone
+    ZoneTableNode* node(NULL);
+    switch (zones_->insert(mem_sgmt, zone_name, &node)) {
+    case ZoneTableTree::SUCCESS:
+    case ZoneTableTree::ALREADYEXISTS:
+        // These are OK
+        break;
+    default:
+        // Can Not Happen
+        assert(false);
+    }
+    // Can Not Happen
+    assert(node != NULL);
+
+    // Is it empty? We either just created it or it might be nonterminal
+    if (node->isEmpty()) {
+        node->setData(mem_sgmt, holder.get());
+        return (AddResult(result::SUCCESS, holder.release()));
+    } else { // There's something there already
+        return (AddResult(result::EXIST, node->getData()));
+    }
+}
+
+ZoneTable::FindResult
+ZoneTable::findZone(const Name& name) const {
+    ZoneTableNode* node(NULL);
+    result::Result my_result;
+
+    // Translate the return codes
+    switch (zones_->find(name, &node)) {
+    case ZoneTableTree::EXACTMATCH:
+        my_result = result::SUCCESS;
+        break;
+    case ZoneTableTree::PARTIALMATCH:
+        my_result = result::PARTIALMATCH;
+        break;
+    case ZoneTableTree::NOTFOUND:
+        // We have no data there, so translate the pointer to NULL as well
+        return (FindResult(result::NOTFOUND, NULL));
+    default:
+        // Can Not Happen
+        assert(0);
+        // Because of warning
+        return (FindResult(result::NOTFOUND, NULL));
+    }
+
+    // Can Not Happen (remember, NOTFOUND is handled)
+    assert(node != NULL);
+
+    return (FindResult(my_result, node->getData()));
+}
+
+} // end of namespace memory
+} // end of namespace datasrc
+} // end of namespace isc

+ 178 - 0
src/lib/datasrc/memory/zone_table.h

@@ -0,0 +1,178 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __DATASRC_MEMORY_ZONE_TABLE_H
+#define __DATASRC_MEMORY_ZONE_TABLE_H 1
+
+#include <util/memory_segment.h>
+
+#include <datasrc/result.h>
+#include <datasrc/memory/domaintree.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/interprocess/offset_ptr.hpp>
+
+namespace isc {
+namespace dns {
+class Name;
+class RRClass;
+}
+
+namespace datasrc {
+namespace memory {
+// forward declaration: in this header it's mostly an opaque type.
+class ZoneData;
+
+/// \brief A conceptual table of authoritative zones.
+///
+/// This class is actually a simple wrapper for a \c DomainTree whose data is
+/// of \c ZoneData, and provides allocator, deallocator, and some basic
+/// manipulation methods.
+///
+/// A single \c ZoneData object is intended to be used for a single specific
+/// RR class, and provides a mapping from a name to a \c ZoneData (using the
+/// best matching search semantics).  The \c ZoneData class itself does not
+/// maintain the information of the RR class; the user of this class is
+/// responsible for associating a specific RR class to a corresponding
+/// \c ZoneData object.
+///
+/// This class is designed so an instance can be stored in a shared memory
+/// region.  So it only contains straightforward data (e.g., it doesn't hold
+/// a pointer to an object of some base class that contains virtual methods),
+/// and some pointers (either as a direct or indirect member variable) are
+/// represented as offset pointers.  For the same reason this class should
+/// never has virtual methods (and as a result, should never be inherited
+/// in practice).  When this class is extended these properties must be
+/// retained.
+///
+/// This class is intended to be used as a backend for the \c MemoryDataSrc
+/// class, and is not intended to be used for other general purposes.
+class ZoneTable : boost::noncopyable {
+private:
+    // The deleter for the zone data stored in the table.
+    struct ZoneDataDeleter {
+        ZoneDataDeleter() {}
+        void operator()(util::MemorySegment& mem_sgmt,
+                        ZoneData* zone_data) const;
+    };
+
+    // Type aliases to make it shorter
+    typedef DomainTree<ZoneData, ZoneDataDeleter> ZoneTableTree;
+    typedef DomainTreeNode<ZoneData, ZoneDataDeleter> ZoneTableNode;
+
+public:
+    /// \brief Result data of addZone() method.
+    struct AddResult {
+        AddResult(result::Result param_code, ZoneData* param_zone_data) :
+            code(param_code), zone_data(param_zone_data)
+        {}
+        const result::Result code;
+        ZoneData* const zone_data;
+    };
+
+    /// \brief Result data of findZone() method.
+    struct FindResult {
+        FindResult(result::Result param_code,
+                   const ZoneData* param_zone_data) :
+            code(param_code), zone_data(param_zone_data)
+        {}
+        const result::Result code;
+        const ZoneData* const zone_data;
+    };
+
+private:
+    /// Constructor.
+    ///
+    /// An object of this class is always expected to be created by the
+    /// allocator (\c create()), so the constructor is hidden as private.
+    ///
+    /// This constructor internally involves resource allocation, and if
+    /// it fails, a corresponding standard exception will be thrown.
+    /// It never throws an exception otherwise.
+    ZoneTable(ZoneTableTree* zones) : zones_(zones)
+    {}
+
+public:
+    /// \brief Allocate and construct \c ZoneTable
+    ///
+    /// This static method allocates memory for a new \c ZoneTable object
+    /// from the given memory segment, constructs the object, and returns
+    /// a pointer to it.
+    ///
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c ZoneTable is allocated.
+    static ZoneTable* create(util::MemorySegment& mem_sgmt);
+
+    /// \brief Destruct and deallocate \c ZoneTable
+    ///
+    /// \throw none
+    ///
+    /// \param mem_sgmt The \c MemorySegment that allocated memory for
+    /// \c ztable.
+    /// \param ztable A non NULL pointer to a valid \c ZoneTable object
+    /// that was originally created by the \c create() method (the behavior
+    /// is undefined if this condition isn't met).
+    static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable);
+
+    /// Add a new zone to the \c ZoneTable.
+    ///
+    /// This method creates a new \c ZoneData for the given zone name and
+    /// holds it in the internal table.  The newly created zone data will be
+    /// returned via the \c zone_data member of the return value.  If the given
+    /// zone name already exists in the table, a new data object won't be
+    /// created; instead, the existing corresponding data will be returned.
+    ///
+    /// \throw std::bad_alloc Internal resource allocation fails.
+    ///
+    /// \param zone_name The name of the zone to be added.
+    /// \return \c result::SUCCESS If the zone is successfully
+    /// added to the zone table.
+    /// \return \c result::EXIST The zone table already contains
+    /// zone of the same origin.
+    AddResult addZone(util::MemorySegment& mem_sgmt,
+                      const dns::Name& zone_name);
+
+    /// Find a zone that best matches the given name in the \c ZoneTable.
+    ///
+    /// It searches the internal storage for a zone that gives the
+    /// longest match against \c name, and returns the result in the
+    /// form of a \c FindResult object as follows:
+    /// - \c code: The result code of the operation.
+    ///   - \c result::SUCCESS: A zone that gives an exact match
+    ///    is found
+    ///   - \c result::PARTIALMATCH: A zone whose origin is a
+    ///    super domain of \c name is found (but there is no exact match)
+    ///   - \c result::NOTFOUND: For all other cases.
+    /// - \c zone_data: corresponding zone data of the found zone; NULL if
+    ///   no matching zone is found.
+    ///
+    /// \throw none
+    ///
+    /// \param name A domain name for which the search is performed.
+    /// \return A \c FindResult object enclosing the search result (see above).
+    FindResult findZone(const isc::dns::Name& name) const;
+
+private:
+    boost::interprocess::offset_ptr<ZoneTableTree> zones_;
+};
+}
+}
+}
+#endif  // __DATASRC_MEMORY_ZONE_TABLE_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 5 - 1
src/lib/datasrc/rbtree.h

@@ -1622,8 +1622,12 @@ RBTree<T>::insert(util::MemorySegment& mem_sgmt,
     isc::dns::LabelSequence target_labels(target_name);
 
     int order = -1;
+    // For possible LabelSequence serialization we always store labels data
+    // in the separate local buffer.
+    uint8_t labels_buf[dns::LabelSequence::MAX_SERIALIZED_LENGTH];
     while (current != NULL) {
-        const dns::LabelSequence current_labels(current->getLabels());
+        const dns::LabelSequence current_labels(
+            dns::LabelSequence(current->getLabels(), labels_buf));
         const isc::dns::NameComparisonResult compare_result =
             target_labels.compare(current_labels);
         const isc::dns::NameComparisonResult::NameRelation relation =

+ 21 - 1
src/lib/dns/labelsequence.cc

@@ -91,6 +91,19 @@ LabelSequence::getSerializedLength() const {
     return (1 + getLabelCount() + getDataLength());
 }
 
+namespace {
+// Check if buf is not in the range of [bp, ep), which means
+// - end of buffer is before bp, or
+// - beginning of buffer is on or after ep
+bool
+isOutOfRange(const uint8_t* bp, const uint8_t* ep,
+             const uint8_t* buf, size_t buf_len)
+{
+    return (bp >= buf + buf_len || // end of buffer is before bp
+            ep <= buf);            // beginning of buffer is on or after ep
+}
+}
+
 void
 LabelSequence::serialize(void* buf, size_t buf_len) const {
     const size_t expected_size = getSerializedLength();
@@ -101,12 +114,19 @@ LabelSequence::serialize(void* buf, size_t buf_len) const {
     const size_t offsets_len = getLabelCount();
     assert(offsets_len < 256);  // should be in the 8-bit range
 
+    // Overridden check.  Buffer shouldn't overwrap the offset of name data
+    // regions.
     uint8_t* bp = reinterpret_cast<uint8_t*>(buf);
+    const size_t ndata_len = getDataLength();
+    if (!isOutOfRange(offsets_, offsets_ + offsets_len, bp, buf_len) ||
+        !isOutOfRange(data_, data_ + ndata_len, bp, buf_len)) {
+        isc_throw(BadValue, "serialize would break the source sequence");
+    }
+
     *bp++ = offsets_len;
     for (size_t i = 0; i < offsets_len; ++i) {
         *bp++ = offsets_[first_label_ + i] - offsets_[first_label_];
     }
-    const size_t ndata_len = getDataLength();
     std::memcpy(bp, &data_[offsets_[first_label_]], ndata_len);
     bp += ndata_len;
 

+ 31 - 2
src/lib/dns/labelsequence.h

@@ -179,6 +179,34 @@ public:
     /// the value returned by getSerializedLength() (it can be larger than
     /// that).
     ///
+    /// Be careful about where the buffer is located; due to the nature
+    /// of the buffer, it's quite possible that the memory region is being used
+    /// to construct another active \c LabelSequence.  In such a case
+    /// the serialization would silently break that sequence object, and
+    /// it will be very difficult to identify the cause.  This method
+    /// has minimal level checks to avoid such disruption: If the serialization
+    /// would break "this" \c LabelSequence object, it doesn't write anything
+    /// to the given buffer and throw a \c isc::BadValue exception.
+    ///
+    /// In general, it should be safe to call this method on a
+    /// \c LabelSequence object constructed from a \c Name object or
+    /// a copy of such \c LabelSequence.  When you construct \c LabelSequence
+    /// from pre-serialized data, calling this method on it can be unsafe.
+    /// One safe (but a bit less efficient) way in such a case is to make
+    /// the source \c LabelSequence temporary and immediately create a
+    /// local copy using an explicit buffer, and call this method on the
+    /// latter:
+    /// \code
+    ///    // don't do this, it's not safe (and would result in exception):
+    ///    // LabelSequence(buf).serialize(buf, buf_len);
+    ///
+    ///    // The following are the safe way:
+    ///    uint8_t ext_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+    ///    LabelSequence seq(LabelSequence(buf), ext_buf);
+    ///    ... (strip the labels, etc)
+    ///    seq.serialize(buf, buf_len); // it's safe to override buf here
+    /// \endcode
+    ///
     /// The serialized image would be as follows:
     /// - olen: number of offsets (1 byte)
     /// - binary sequence of offsets (olen bytes, verbatim copy of offsets_
@@ -186,13 +214,14 @@ public:
     /// - binary sequence of name data (length determined by itself, verbatim
     ///   copy of data_ of the corresponding size)
     ///
-    /// Applications must use the resulting image opaque value and must not
+    /// Applications must use the resulting image as opaque value and must not
     /// use it for other purposes than input to the corresponding constructor
     /// to restore it.  Application behavior that assumes the specific
     /// organization of the image is not guaranteed.
     ///
     /// \throw isc::BadValue buf_len is too short (this method never throws
-    /// otherwise)
+    /// otherwise) or the serialization would override internal data of
+    /// of the source LabelSequence.
     ///
     /// \param buf Pointer to the placeholder to dump the serialized image
     /// \param buf_len The size of available region in \c buf

+ 29 - 2
src/lib/dns/tests/labelsequence_unittest.cc

@@ -712,8 +712,9 @@ TEST_F(LabelSequenceTest, LeftShiftOperator) {
 }
 
 TEST_F(LabelSequenceTest, serialize) {
-    // placeholder for serialized data
-    uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+    // placeholder for serialized data.  We use a sufficiently large space
+    // for testing the overwrapping cases below.
+    uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH * 3];
 
     // vector to store expected and actual data
     vector<LabelSequence> actual_labelseqs;
@@ -777,6 +778,32 @@ TEST_F(LabelSequenceTest, serialize) {
 
         EXPECT_EQ(NameComparisonResult::EQUAL,
                   LabelSequence(labels_buf).compare(*itl).getRelation());
+
+        // Shift the data to the middle of the buffer for overwrap check
+        uint8_t* const bp = labels_buf;
+        std::memcpy(bp + serialized_len, bp, serialized_len);
+        // Memory layout is now as follows:
+        //   <- ser_len ->          <- ser_len ------>
+        // bp             bp+ser_len                  bp+(ser_len*2)
+        //                           olen,odata,ndata
+
+        // end of buffer would be the first byte of offsets: invalid.
+        EXPECT_THROW(LabelSequence(bp + serialized_len).
+                     serialize(bp + 2, serialized_len),
+                     isc::BadValue);
+        // begin of buffer would be the last byte of ndata: invalid.
+        EXPECT_THROW(LabelSequence(bp + serialized_len).
+                     serialize(bp + (2 * serialized_len) - 1, serialized_len),
+                     isc::BadValue);
+        // A boundary safe case: buffer is placed after the sequence data.
+        // should cause no disruption.
+        LabelSequence(bp + serialized_len).
+                     serialize(bp + 2 * serialized_len, serialized_len);
+        // A boundary safe case: buffer is placed before the sequence data
+        // should cause no disruption. (but the original serialized data will
+        // be overridden, so it can't be used any more)
+        LabelSequence(bp + serialized_len).
+                     serialize(bp + 1, serialized_len);
     }
 
     EXPECT_THROW(ls1.serialize(labels_buf, ls1.getSerializedLength() - 1),