mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
}
-ZoneTable::AddResult
+result::Result
ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
- const Name& zone_name)
+ const Name& zone_name,
+ SegmentObjectHolder<ZoneData, RRClass>& content)
{
- // 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.
- SegmentObjectHolder<ZoneData, RRClass> holder(
- mem_sgmt, ZoneData::create(mem_sgmt, zone_name), zone_class);
-
// Get the node where we put the zone
ZoneTableNode* node(NULL);
switch (zones_->insert(mem_sgmt, zone_name, &node)) {
// Can Not Happen
assert(node != NULL);
- // Is it empty? We either just created it or it might be nonterminal
- if (node->isEmpty()) {
- node->setData(holder.get());
- return (AddResult(result::SUCCESS, holder.release()));
- } else { // There's something there already
- return (AddResult(result::EXIST, node->getData()));
+ // We can release now, setData never throws
+ ZoneData* old = node->setData(content.release());
+ if (old != NULL) {
+ ZoneData::destroy(mem_sgmt, old, zone_class);
+ return (result::EXIST);
+ } else {
+ return (result::SUCCESS);
}
}
return (FindResult(my_result, node->getData()));
}
-ZoneTable::FindResult
-ZoneTable::setZoneData(const Name& name, ZoneData* data)
-{
- ZoneTableNode* node(NULL);
-
- ZoneTableTree::Result result(zones_->find(name, &node));
-
- if (result != ZoneTableTree::EXACTMATCH) {
- return (FindResult(result::NOTFOUND, NULL));
- } else {
- return (FindResult(result::SUCCESS, node->setData(data)));
- }
-}
-
} // end of namespace memory
} // end of namespace datasrc
} // end of namespace isc
// forward declaration: in this header it's mostly an opaque type.
class ZoneData;
+namespace detail {
+template<class T, class C> class SegmentObjectHolder;
+}
+
/// \brief A conceptual table of authoritative zones.
///
/// This class is actually a simple wrapper for a \c DomainTree whose data is
typedef DomainTreeNode<ZoneData> 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 {
/// 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.
- ///
- /// The zone table keeps the ownership of the created zone data; the
- /// caller must not try to destroy it directly. (We'll eventually
- /// add an interface to delete specific zone data from the table).
+ /// This method adds a given zone data to the internal table.
///
/// \throw std::bad_alloc Internal resource allocation fails.
///
/// \param mem_sgmt The \c MemorySegment to allocate zone data to be
- /// created. It must be the same segment that was used to create
- /// the zone table at the time of create().
+ /// created. It must be the same segment that was used to create
+ /// the zone table at the time of create().
/// \param zone_name The name of the zone to be added.
/// \param zone_class The RR class of the zone. It must be the RR class
- /// that is supposed to be associated to the zone table.
+ /// that is supposed to be associated to the zone table.
+ /// \param content This one should hold the zone content (the ZoneData).
+ /// When it is added successfully, it is released from the holder.
/// \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, dns::RRClass zone_class,
- const dns::Name& zone_name);
+ /// added to the zone table.
+ /// \return \c result::EXIST The zone table already contained
+ /// zone of the same origin. The old data is released and replaced
+ /// by the new one.
+ result::Result addZone(util::MemorySegment& mem_sgmt,
+ dns::RRClass zone_class,
+ const dns::Name& zone_name,
+ detail::SegmentObjectHolder<ZoneData,
+ isc::dns::RRClass>& content);
/// Find a zone that best matches the given name in the \c ZoneTable.
///
/// \return A \c FindResult object enclosing the search result (see above).
FindResult findZone(const isc::dns::Name& name) const;
- /// Override the ZoneData for a node (zone) in the zone tree.
- ///
- /// \throw none
- ///
- /// \param name A domain name for which the zone data is set.
- /// \param data The new zone data to set.
- /// \return A \c FindResult object containing the old data if the
- /// zone was found.
- FindResult setZoneData(const isc::dns::Name& name, ZoneData* data);
-
private:
boost::interprocess::offset_ptr<ZoneTableTree> zones_;
};
#include <datasrc/result.h>
#include <datasrc/memory/zone_data.h>
#include <datasrc/memory/zone_table.h>
+#include <datasrc/memory/segment_object_holder.h>
#include <gtest/gtest.h>
using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::detail;
namespace {
// Memory segment specified for tests. It normally behaves like a "local"
}
TEST_F(ZoneTableTest, addZone) {
+ SegmentObjectHolder<ZoneData, RRClass> holder1(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
// Normal successful case.
- const ZoneTable::AddResult result1 =
- zone_table->addZone(mem_sgmt_, zclass_, zname1);
- EXPECT_EQ(result::SUCCESS, result1.code);
+ EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
+ zname1, holder1));
+ // It got released by it
+ EXPECT_EQ(NULL, holder1.get());
// Duplicate add doesn't replace the existing data.
+ SegmentObjectHolder<ZoneData, RRClass> holder2(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
- zname1).code);
- EXPECT_EQ(result1.zone_data,
- zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
+ zname1, holder2));
+ // It releases this one even when we replace the old zone
+ EXPECT_EQ(NULL, holder2.get());
+
+ SegmentObjectHolder<ZoneData, RRClass> holder3(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, Name("EXAMPLE.COM")),
+ zclass_);
// names are compared in a case insensitive manner.
EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
- Name("EXAMPLE.COM")).code);
+ Name("EXAMPLE.COM"),
+ holder3));
// Add some more different ones. Should just succeed.
+ SegmentObjectHolder<ZoneData, RRClass> holder4(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_);
EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+ zone_table->addZone(mem_sgmt_, zclass_, zname2, holder4));
+ SegmentObjectHolder<ZoneData, RRClass> holder5(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_);
EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+ zone_table->addZone(mem_sgmt_, zclass_, zname3, holder5));
// 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_, zclass_, Name("example.org")),
+ SegmentObjectHolder<ZoneData, RRClass> holder6(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, Name("example.org")), zclass_);
+ mem_sgmt_.setThrowCount(1);
+ EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org"),
+ holder6),
std::bad_alloc);
}
TEST_F(ZoneTableTest, findZone) {
- const ZoneTable::AddResult add_result1 =
- zone_table->addZone(mem_sgmt_, zclass_, zname1);
- EXPECT_EQ(result::SUCCESS, add_result1.code);
+ SegmentObjectHolder<ZoneData, RRClass> holder1(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
+ ZoneData* zone_data = holder1.get();
+ EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1,
+ holder1));
+ SegmentObjectHolder<ZoneData, RRClass> holder2(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_);
EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+ zone_table->addZone(mem_sgmt_, zclass_, zname2, holder2));
+ SegmentObjectHolder<ZoneData, RRClass> holder3(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_);
EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+ zone_table->addZone(mem_sgmt_, zclass_, zname3, holder3));
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(zone_data, find_result1.zone_data);
EXPECT_EQ(result::NOTFOUND,
zone_table->findZone(Name("example.org")).code);
// and the code should be PARTIALMATCH.
EXPECT_EQ(result::PARTIALMATCH,
zone_table->findZone(Name("www.example.com")).code);
- EXPECT_EQ(add_result1.zone_data,
+ EXPECT_EQ(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.
+ SegmentObjectHolder<ZoneData, RRClass> holder4(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_);
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
- Name("com")).code);
- EXPECT_EQ(add_result1.zone_data,
+ Name("com"), holder4));
+ EXPECT_EQ(zone_data,
zone_table->findZone(Name("www.example.com")).zone_data);
}
}