]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[2107merge] Merge branch 'trac2107' into trac2107merge with fixing conflicts.
authorJINMEI Tatuya <jinmei@isc.org>
Wed, 29 Aug 2012 00:09:26 +0000 (17:09 -0700)
committerJINMEI Tatuya <jinmei@isc.org>
Wed, 29 Aug 2012 00:09:26 +0000 (17:09 -0700)
Conflict resolution is mostly straightforward adujstment for the latest
interface, but it's not super trivial and should be reviewed.

1  2 
src/lib/datasrc/memory/Makefile.am
src/lib/datasrc/memory/domaintree.h
src/lib/datasrc/memory/tests/Makefile.am
src/lib/datasrc/memory/tests/rdataset_unittest.cc
src/lib/datasrc/memory/tests/zone_data_unittest.cc
src/lib/datasrc/memory/tests/zone_table_unittest.cc
src/lib/datasrc/memory/zone_data.cc
src/lib/datasrc/memory/zone_table.cc
src/lib/datasrc/memory/zone_table.h
src/lib/dns/labelsequence.h
src/lib/dns/tests/labelsequence_unittest.cc

index 7cdc7369fe1f4bb6cffd8fc9216280959c6ddaf9,4b427937d740ebd9174f2a260a04ccf160720724..b8307c0a7e7903ac14877edd4fe1f5d7d7a59867
@@@ -10,10 -10,11 +10,9 @@@ CLEANFILES = *.gcno *.gcda datasrc_mess
  
  noinst_LTLIBRARIES = libdatasrc_memory.la
  
--libdatasrc_memory_la_SOURCES = \
 -      rdata_encoder.h rdata_encoder.cc \
 -      rdata_field.h rdata_field.cc \
 -      rdata_reader.h rdata_reader.cc \
--      rdataset.h rdataset.cc \
-       rdata_serialization.h rdata_serialization.cc \
-       domaintree.h
- libdatasrc_memory_la_SOURCES += zone_data.h
 -      domaintree.h \
 -      segment_object_holder.h \
 -      zone_data.h zone_data.cc
++libdatasrc_memory_la_SOURCES = domaintree.h
++libdatasrc_memory_la_SOURCES += rdataset.h rdataset.cc
++libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc
++libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc
 +libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
 +EXTRA_DIST  = rdata_serialization_priv.cc
Simple merge
index 128b8e7f9579a46a421b3f17e242ea1e31c94391,e210a4ad139e7cfbfe6c900408814ba7b6fd00d3..22fd4bf947c9973c7cdc91cb0d1c27d749b92529
@@@ -21,7 -21,9 +21,10 @@@ run_unittests_SOURCES = run_unittests.c
  run_unittests_SOURCES += rdata_serialization_unittest.cc
  run_unittests_SOURCES += rdataset_unittest.cc
  run_unittests_SOURCES += domaintree_unittest.cc
 +run_unittests_SOURCES += zone_table_unittest.cc
+ run_unittests_SOURCES += zone_data_unittest.cc
+ run_unittests_SOURCES += memory_segment_test.h
+ run_unittests_SOURCES += segment_object_holder_unittest.cc
  
  run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
  run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
index 0000000000000000000000000000000000000000,d2844ea84efdbde1c0112586950c9665a5596078..d15fe8beb2c6ca1a6a9beff9ff66b80dc64b7b05
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,255 +1,255 @@@
 -#include <datasrc/memory/rdata_encoder.h>
+ // 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 "memory_segment_test.h"
+ #include <dns/rdataclass.h>
+ #include <exceptions/exceptions.h>
+ #include <dns/name.h>
+ #include <dns/labelsequence.h>
+ #include <dns/rrclass.h>
++#include <datasrc/memory/rdata_serialization.h>
+ #include <datasrc/memory/rdataset.h>
+ #include <datasrc/memory/zone_data.h>
+ #include <testutils/dnsmessage_test.h>
+ #include <gtest/gtest.h>
+ #include <new>                  // for bad_alloc
+ #include <string>
+ using namespace isc::dns;
+ using namespace isc::dns::rdata;
+ using namespace isc::datasrc::memory;
+ using namespace isc::datasrc::memory::test;
+ using namespace isc::testutils;
+ namespace {
+ // With this single fixture we'll test both NSEC3Data and ZoneData
+ class ZoneDataTest : public ::testing::Test {
+ protected:
+     ZoneDataTest() :
+         nsec3_data_(NULL), param_rdata_("1 0 12 aabbccdd"),
+         param_rdata_nosalt_("1 1 10 -"),
+         param_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a')),
+         nsec3_rdata_("1 0 12 aabbccdd TDK23RP6 SOA"),
+         nsec3_rdata_nosalt_("1 1 10 - TDK23RP6 SOA"),
+         nsec3_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a') +
+                                " TDK23RP6 SOA"),
+         zname_("example.com"),
+         zone_data_(ZoneData::create(mem_sgmt_, zname_)),
+         a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1")),
+         aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA 2001:db8::1")),
+         nsec3_rrset_(textToRRset("TDK23RP6.example.com. 3600 IN NSEC3 "
+                                  "1 0 12 aabbccdd TDK23RP6 SOA"))
+     {}
+     void TearDown() {
+         if (nsec3_data_ != NULL) {
+             NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
+         }
+         if (zone_data_ != NULL) {
+             ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+         }
+         // detect any memory leak in the test memory segment
+         EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+     }
+     MemorySegmentTest mem_sgmt_;
+     NSEC3Data* nsec3_data_;
+     const generic::NSEC3PARAM param_rdata_, param_rdata_nosalt_,
+         param_rdata_largesalt_;
+     const generic::NSEC3 nsec3_rdata_, nsec3_rdata_nosalt_,
+         nsec3_rdata_largesalt_;
+     const Name zname_;
+     ZoneData* zone_data_;
+     const ConstRRsetPtr a_rrset_, aaaa_rrset_, nsec3_rrset_;
+     RdataEncoder encoder_;
+ };
+ // Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
+ template <typename RdataType>
+ void
+ checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
+     NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
+     // Internal tree should be created and empty.
+     EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
+     EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
+     EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
+     EXPECT_EQ(expect_rdata.getIterations(), nsec3_data->iterations);
+     EXPECT_EQ(expect_rdata.getSalt().size(), nsec3_data->getSaltLen());
+     if (expect_rdata.getSalt().size() > 0) {
+         EXPECT_EQ(0, memcmp(&expect_rdata.getSalt()[0],
+                             nsec3_data->getSaltData(),
+                             expect_rdata.getSalt().size()));
+     }
+     NSEC3Data::destroy(mem_sgmt, nsec3_data, RRClass::IN());
+ }
+ void
+ checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
+                   const RdataSet* expected_set)
+ {
+     ZoneNode* node = NULL;
+     tree.find(name, &node);
+     ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+     EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
+ }
+ TEST_F(ZoneDataTest, createNSEC3Data) {
+     // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
+     // and of NSEC3), check if the resulting parameters match.
+     checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
+     checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
+     checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
+     // Same concepts of the tests, using NSEC3 RDATA.
+     checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
+     checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
+     checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
+ }
+ TEST_F(ZoneDataTest, addNSEC3) {
+     nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
+     ZoneNode* node = NULL;
+     nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
+     ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+     EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+     RdataSet* rdataset_nsec3 =
+         RdataSet::create(mem_sgmt_, encoder_, nsec3_rrset_, ConstRRsetPtr());
+     node->setData(rdataset_nsec3);
+     // Confirm we can find the added ones from the zone data.
+     checkFindRdataSet(nsec3_data_->getNSEC3Tree(), nsec3_rrset_->getName(),
+                       RRType::NSEC3(), rdataset_nsec3);
+     // TearDown() will confirm there's no leak on destroy
+ }
+ TEST_F(ZoneDataTest, getOriginNode) {
+     EXPECT_EQ(LabelSequence(zname_), zone_data_->getOriginNode()->getLabels());
+ }
+ TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
+     // Note: below, we use our knowledge of how memory allocation happens
+     // within the NSEC3Data, the zone data and the underlying domain tree
+     // implementation.  We'll emulate rare situations where allocate() fails
+     // with an exception, and confirm it doesn't cause any harsh disruption
+     // or leak.
+     // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
+     // will fail due to bad_alloc.  It shouldn't cause memory leak
+     // (that would be caught in TearDown()).
+     mem_sgmt_.setThrowCount(2);
+     EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
+     // allocate() will throw on the insertion of the origin node.
+     mem_sgmt_.setThrowCount(2);
+     EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+     // allocate() will throw on creating the zone data.
+     mem_sgmt_.setThrowCount(3);
+     EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+     // These incomplete create() attempts shouldn't cause memory leak
+     // (that would be caught in TearDown()).
+ }
+ TEST_F(ZoneDataTest, addRdataSets) {
+     // Insert a name to the zone, and add a couple the data (RdataSet) objects
+     // to the corresponding node.
+     ZoneNode* node = NULL;
+     zone_data_->insertName(mem_sgmt_, a_rrset_->getName(), &node);
+     ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+     EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+     RdataSet* rdataset_a =
+         RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr());
+     node->setData(rdataset_a);
+     RdataSet* rdataset_aaaa =
+         RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_, ConstRRsetPtr());
+     // make a linked list and replace the list head
+     rdataset_aaaa->next = rdataset_a;
+     node->setData(rdataset_aaaa);
+     // Confirm we can find the added ones from the zone data.
+     checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                       RRType::A(), rdataset_a);
+     checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                       RRType::AAAA(), rdataset_aaaa);
+     // There's no NS (or anything other than AAAA or A) RdataSet in the list
+     checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                       RRType::NS(), NULL);
+     // TearDown() will confirm there's no leak on destroy
+ }
+ TEST_F(ZoneDataTest, getSetNSEC3Data) {
+     // Initially there's no NSEC3 data
+     EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+     // isNSEC3Signed is true iff zone data has non NULL NSEC3 data
+     EXPECT_FALSE(zone_data_->isNSEC3Signed());
+     // Set a new one.  The set method should return NULL.  The get method
+     // should return the new one.
+     NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
+     NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
+     EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
+     EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
+     EXPECT_TRUE(zone_data_->isNSEC3Signed());
+     // Replace an existing one with a yet another one.
+     // We're responsible for destroying the old one.
+     NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
+     old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
+     EXPECT_EQ(nsec3_data, old_nsec3_data);
+     EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
+     EXPECT_TRUE(zone_data_->isNSEC3Signed());
+     NSEC3Data::destroy(mem_sgmt_, old_nsec3_data, RRClass::IN());
+     // Setting NULL clears any existing one.
+     old_nsec3_data = zone_data_->setNSEC3Data(NULL);
+     EXPECT_EQ(nsec3_data2, old_nsec3_data);
+     EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+     EXPECT_FALSE(zone_data_->isNSEC3Signed());
+     // Then set it again.  The zone data should destroy it on its own
+     // destruction.
+     zone_data_->setNSEC3Data(old_nsec3_data);
+ }
+ TEST_F(ZoneDataTest, isSigned) {
+     // By default it's considered unsigned
+     EXPECT_FALSE(zone_data_->isSigned());
+     // declare it's signed, the isSigned() says so too
+     zone_data_->setSigned(true);
+     EXPECT_TRUE(zone_data_->isSigned());
+     // change it to unsigned again
+     zone_data_->setSigned(false);
+     EXPECT_FALSE(zone_data_->isSigned());
+ }
+ }
index 4f0af5e7d1f2590805d228a81f6aa6153d1b030f,0000000000000000000000000000000000000000..359df4923fecb3bf95b9c51596cae659178a0431
mode 100644,000000..100644
--- /dev/null
@@@ -1,143 -1,0 +1,150 @@@
-     ZoneTableTest() : zname1(Name("example.com")),
 +// 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:
-                       zone_table(ZoneTable::create(mem_sgmt_))
++    ZoneTableTest() : zclass_(RRClass::IN()),
++                      zname1(Name("example.com")),
 +                      zname2(Name("example.net")),
 +                      zname3(Name("example")),
-             ZoneTable::destroy(mem_sgmt_, zone_table);
++                      zone_table(ZoneTable::create(mem_sgmt_, zclass_))
 +    {}
 +    ~ZoneTableTest() {
 +        if (zone_table != NULL) {
-         ZoneTable::destroy(mem_sgmt_, zone_table);
++            ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
 +        }
 +    }
 +    void TearDown() {
-     EXPECT_THROW(ZoneTable::create(mem_sgmt_), std::bad_alloc);
++        ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
 +        zone_table = NULL;
 +        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
 +    }
++    const RRClass zclass_;
 +    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);
-         zone_table->addZone(mem_sgmt_, zname1);
++    EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), 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 =
-     EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zname1).code);
++        zone_table->addZone(mem_sgmt_, zclass_, zname1);
 +    EXPECT_EQ(result::SUCCESS, result1.code);
 +
 +    // Duplicate add doesn't replace the existing data.
-               zone_table->addZone(mem_sgmt_, zname1).zone_data);
++    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
++                                                 zname1).code);
 +    EXPECT_EQ(result1.zone_data,
-     EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_,
++              zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
 +    // names are compared in a case insensitive manner.
-     EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
-     EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
++    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
 +                                                 Name("EXAMPLE.COM")).code);
 +    // Add some more different ones.  Should just succeed.
-     EXPECT_THROW(zone_table->addZone(mem_sgmt_, Name("example.org")),
++    EXPECT_EQ(result::SUCCESS,
++              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
++    EXPECT_EQ(result::SUCCESS,
++              zone_table->addZone(mem_sgmt_, zclass_, 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);
-         zone_table->addZone(mem_sgmt_, zname1);
++    EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
 +                 std::bad_alloc);
 +}
 +
 +TEST_F(ZoneTableTest, findZone) {
 +    const ZoneTable::AddResult add_result1 =
-     EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
-     EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
++        zone_table->addZone(mem_sgmt_, zclass_, zname1);
 +    EXPECT_EQ(result::SUCCESS, add_result1.code);
-     EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_,
++    EXPECT_EQ(result::SUCCESS,
++              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
++    EXPECT_EQ(result::SUCCESS,
++              zone_table->addZone(mem_sgmt_, zclass_, 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(static_cast<ZoneData*>(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_, zclass_,
 +                                                   Name("com")).code);
 +    EXPECT_EQ(add_result1.zone_data,
 +              zone_table->findZone(Name("www.example.com")).zone_data);
 +}
 +}
index 0000000000000000000000000000000000000000,bb780086c2ab8a309a6710850533cd48459a7c25..033af150c34649355f493894e3133ecf39800736
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,170 +1,170 @@@
 -#include "rdata_encoder.h"
+ // 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 <dns/rrclass.h>
+ #include <dns/rdataclass.h>
+ #include "rdataset.h"
++#include "rdata_serialization.h"
+ #include "zone_data.h"
+ #include "segment_object_holder.h"
+ #include <boost/bind.hpp>
+ #include <boost/function.hpp>
+ #include <cassert>
+ #include <new>                  // for the placement new
+ #include <vector>
+ using namespace isc::dns;
+ using namespace isc::dns::rdata;
+ namespace isc {
+ namespace datasrc {
+ namespace memory {
+ namespace {
+ void
+ rdataSetDeleter(RRClass rrclass, util::MemorySegment* mem_sgmt,
+                 RdataSet* rdataset_head)
+ {
+     RdataSet* rdataset_next;
+     for (RdataSet* rdataset = rdataset_head;
+          rdataset != NULL;
+          rdataset = rdataset_next)
+     {
+         rdataset_next = rdataset->getNext();
+         RdataSet::destroy(*mem_sgmt, rrclass, rdataset);
+     }
+ }
+ void
+ nullDeleter(RdataSet* rdataset_head) {
+     assert(rdataset_head == NULL);
+ }
+ }
+ NSEC3Data*
+ NSEC3Data::create(util::MemorySegment& mem_sgmt,
+                   const generic::NSEC3PARAM& rdata)
+ {
+     return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+                               rdata.getIterations(), rdata.getSalt()));
+ }
+ NSEC3Data*
+ NSEC3Data::create(util::MemorySegment& mem_sgmt, const generic::NSEC3& rdata) {
+     return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+                               rdata.getIterations(), rdata.getSalt()));
+ }
+ NSEC3Data*
+ NSEC3Data::create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
+                   uint8_t flags, uint16_t iterations,
+                   const std::vector<uint8_t>& salt)
+ {
+     // NSEC3Data allocation can throw.  To avoid leaking the tree, we manage
+     // it in the holder.
+     // Note: we won't add any RdataSet, so we use the NO-OP deleter
+     // (with an assertion check for that).
+     typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+     detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+         mem_sgmt, ZoneTree::create(mem_sgmt, true),
+         boost::bind(nullDeleter, _1));
+     const size_t salt_len = salt.size();
+     void* p = mem_sgmt.allocate(sizeof(NSEC3Data) + 1 + salt_len);
+     NSEC3Data* const param_data =
+         new(p) NSEC3Data(holder.release(), hashalg, flags, iterations);
+     uint8_t* dp = param_data->getSaltBuf();
+     *dp++ = salt_len;
+     if (salt_len > 0) {
+         memcpy(dp, &salt.at(0), salt_len); // use at for safety
+     }
+     return (param_data);
+ }
+ void
+ NSEC3Data::destroy(util::MemorySegment& mem_sgmt, NSEC3Data* data,
+                    RRClass nsec3_class)
+ {
+     ZoneTree::destroy(mem_sgmt, data->nsec3_tree_.get(),
+                       boost::bind(rdataSetDeleter, nsec3_class, &mem_sgmt,
+                                   _1));
+     mem_sgmt.deallocate(data, sizeof(NSEC3Data) + 1 + data->getSaltLen());
+ }
+ void
+ NSEC3Data::insertName(util::MemorySegment& mem_sgmt, const Name& name,
+                       ZoneNode** node)
+ {
+     const ZoneTree::Result result = nsec3_tree_->insert(mem_sgmt, name, node);
+     // This should be ensured by the API:
+     assert((result == ZoneTree::SUCCESS ||
+             result == ZoneTree::ALREADYEXISTS) && node != NULL);
+ }
+ ZoneData*
+ ZoneData::create(util::MemorySegment& mem_sgmt, const Name& zone_origin) {
+     // ZoneTree::insert() and ZoneData allocation can throw.  See also
+     // NSEC3Data::create().
+     typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+     detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+         mem_sgmt, ZoneTree::create(mem_sgmt, true),
+         boost::bind(nullDeleter, _1));
+     ZoneTree* tree = holder.get();
+     ZoneNode* origin_node = NULL;
+     const ZoneTree::Result result =
+         tree->insert(mem_sgmt, zone_origin, &origin_node);
+     assert(result == ZoneTree::SUCCESS);
+     void* p = mem_sgmt.allocate(sizeof(ZoneData));
+     ZoneData* zone_data = new(p) ZoneData(holder.release(), origin_node);
+     return (zone_data);
+ }
+ void
+ ZoneData::destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data,
+                   RRClass zone_class)
+ {
+     ZoneTree::destroy(mem_sgmt, zone_data->zone_tree_.get(),
+                       boost::bind(rdataSetDeleter, zone_class, &mem_sgmt,
+                                   _1));
+     if (zone_data->nsec3_data_) {
+         NSEC3Data::destroy(mem_sgmt, zone_data->nsec3_data_.get(), zone_class);
+     }
+     mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
+ }
+ void
+ ZoneData::insertName(util::MemorySegment& mem_sgmt, const Name& name,
+                      ZoneNode** node)
+ {
+     const ZoneTree::Result result = zone_tree_->insert(mem_sgmt, name, node);
+     // This should be ensured by the API:
+     assert((result == ZoneTree::SUCCESS ||
+             result == ZoneTree::ALREADYEXISTS) && node != NULL);
+ }
+ } // namespace memory
+ } // namespace datasrc
+ } // datasrc isc
index 9c410d081a1804796d45369f0aef946bb36c6b56,0000000000000000000000000000000000000000..f9b2768e8d2ea228567b3103012967d035682aab
mode 100644,000000..100644
--- /dev/null
@@@ -1,144 -1,0 +1,137 @@@
- 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_;
- };
- }
 +// 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 <datasrc/memory/segment_object_holder.h>
++
++#include <boost/function.hpp>
++#include <boost/bind.hpp>
 +
 +#include <cassert>
 +
 +using namespace std;
 +using namespace isc::dns;
 +
 +namespace isc {
 +namespace datasrc {
 +namespace memory {
- ZoneTable::ZoneDataDeleter::operator()(util::MemorySegment& mem_sgmt,
-                                        ZoneData* zone_data) const
++using detail::SegmentObjectHolder;
 +
++namespace {
 +void
-     ZoneData::destroy(mem_sgmt, zone_data);
++deleteZoneData(util::MemorySegment* mem_sgmt, ZoneData* zone_data,
++               RRClass rrclass)
 +{
- ZoneTable::create(util::MemorySegment& mem_sgmt) {
-     Holder<ZoneTableTree> holder(mem_sgmt, ZoneTableTree::create(mem_sgmt));
++    if (zone_data != NULL) {
++        ZoneData::destroy(*mem_sgmt, zone_data, rrclass);
++    }
++}
++typedef boost::function<void(ZoneData*)> ZoneDataDeleterType;
 +}
 +
 +ZoneTable*
- ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable) {
-     ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get());
++ZoneTable::create(util::MemorySegment& mem_sgmt, RRClass zone_class) {
++    SegmentObjectHolder<ZoneTableTree, ZoneDataDeleterType> holder(
++        mem_sgmt, ZoneTableTree::create(mem_sgmt),
++        boost::bind(deleteZoneData, &mem_sgmt, _1, zone_class));
 +    void* p = mem_sgmt.allocate(sizeof(ZoneTable));
 +    ZoneTable* zone_table = new(p) ZoneTable(holder.get());
 +    holder.release();
 +    return (zone_table);
 +}
 +
 +void
- ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
++ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
++                   RRClass zone_class)
++{
++    ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get(),
++                           boost::bind(deleteZoneData, &mem_sgmt, _1,
++                                       zone_class));
 +    mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
 +}
 +
 +ZoneTable::AddResult
-     Holder<ZoneData> holder(mem_sgmt, ZoneData::create(mem_sgmt));
++ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
++                   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.
-         node->setData(mem_sgmt, holder.get());
++    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)) {
 +    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(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
index b5da957eb45b2ed9ac27cbf4261bb575a07c3d5f,0000000000000000000000000000000000000000..2faf606dff90b34ab34ee9ef5ff845e8c734d94e
mode 100644,000000..100644
--- /dev/null
@@@ -1,178 -1,0 +1,198 @@@
-     typedef DomainTree<ZoneData, ZoneDataDeleter> ZoneTableTree;
-     typedef DomainTreeNode<ZoneData, ZoneDataDeleter> ZoneTableNode;
 +// 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 <dns/rrclass.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
-     static ZoneTable* create(util::MemorySegment& mem_sgmt);
++    typedef DomainTree<ZoneData> ZoneTableTree;
++    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 {
 +        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.
-     /// \c ztable.
++    /// \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.
++    static ZoneTable* create(util::MemorySegment& mem_sgmt,
++                             dns::RRClass zone_class);
 +
 +    /// \brief Destruct and deallocate \c ZoneTable
 +    ///
++    /// This method releases all internal resources including all zone data
++    /// created via \c addZone() calls.
++    ///
 +    /// \throw none
 +    ///
 +    /// \param mem_sgmt The \c MemorySegment that allocated memory for
-     static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable);
++    /// \c ztable and used for prior calls to \c addZone().
++    /// \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.
 +    /// \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).
-     AddResult addZone(util::MemorySegment& mem_sgmt,
++    static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
++                        dns::RRClass zone_class);
 +
 +    /// 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).
++    ///
 +    /// \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().
 +    /// \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.
 +    /// \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);
 +
 +    /// 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:
Simple merge