text:
// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <d2/d2_update_message.h>
#include <d2/d2_zone.h>
#include <d2srv/d2_config.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rrttl.h>

#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>

using namespace std;
using namespace isc;
using namespace isc::d2;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::util;

namespace {
 /// @brief Test fixture class for testing D2UpdateMessage object
class D2UpdateMessageTest : public ::testing::Test {
public:
    /// @brief Constructor
    //
    // Does nothing.
    D2UpdateMessageTest() { }

    /// @brief Destructor
    //
    // Does nothing.
    ~D2UpdateMessageTest() { };

    /// @brief Returns string representation of the name encoded in wire format.
    //
    // This function reads the number of bytes specified in the second
    // argument from the buffer. It doesn't check if buffer has sufficient
    // length for reading given number of bytes. Caller should verify it
    // prior to calling this function.
    //
    // @param buf input buffer, its internal pointer will be moved to
    //        the position after a name being read from it.
    // @param name_length length of the name stored in the buffer
    // @param no_zero_byte if true it indicates that the given buffer does not
    //        comprise the zero byte, which signals end of the name. This is
    //        the case, when dealing with compressed messages which don't have
    //        this byte.
    //
    // @return string representation of the name.
    std::string readNameFromWire(InputBuffer& buf, size_t name_length,
                                 const bool no_zero_byte = false) {
        std::vector<uint8_t> name_data;
        // Create another InputBuffer which holds only the name in the wire
        // format.
        buf.readVector(name_data, name_length);
        if (no_zero_byte) {
            ++name_length;
            name_data.push_back(0);
        }
        InputBuffer name_buf(&name_data[0], name_length);
        // Parse the name and return its textual representation.
        Name name(name_buf);
        return (name.toText());
    }
};

// This test verifies that DNS Update message ID can be set using
// setId function.
TEST_F(D2UpdateMessageTest, setId) {
    // Message ID is initialized to 0.
    D2UpdateMessage msg;
    EXPECT_EQ(0, msg.getId());
    // Override the default value and verify that it has been set.
    msg.setId(0x1234);
    EXPECT_EQ(0x1234, msg.getId());
}

// This test verifies that the DNS Update message RCODE can be set
// using setRcode function.
TEST_F(D2UpdateMessageTest, setRcode) {
    D2UpdateMessage msg;
    // Rcode must be explicitly set before it is accessed.
    msg.setRcode(Rcode::NOERROR());
    EXPECT_EQ(Rcode::NOERROR().getCode(), msg.getRcode().getCode());
    // Let's override current value to make sure that getter does
    // not return fixed value.
    msg.setRcode(Rcode::NOTIMP());
    EXPECT_EQ(Rcode::NOTIMP().getCode(), msg.getRcode().getCode());
}

// This test verifies that the Zone section in the DNS Update message
// can be set.
TEST_F(D2UpdateMessageTest, setZone) {
    D2UpdateMessage msg;
    // The zone pointer is initialized to NULL.
    D2ZonePtr zone = msg.getZone();
    EXPECT_FALSE(zone);
    // Let's create a new Zone and check that it is returned
    // via getter.
    msg.setZone(Name("example.com"), RRClass::ANY());
    zone = msg.getZone();
    EXPECT_TRUE(zone);
    EXPECT_EQ("example.com.", zone->getName().toText());
    EXPECT_EQ(RRClass::ANY().getCode(), zone->getClass().getCode());

    // Now, let's check that the existing Zone object can be
    // overridden with a new one.
    msg.setZone(Name("foo.example.com"), RRClass::NONE());
    zone = msg.getZone();
    EXPECT_TRUE(zone);
    EXPECT_EQ("foo.example.com.", zone->getName().toText());
    EXPECT_EQ(RRClass::NONE().getCode(), zone->getClass().getCode());
}

// This test verifies that the DNS message is properly decoded from the
// wire format.
TEST_F(D2UpdateMessageTest, fromWire) {
    // The following table holds the DNS response in on-wire format.
    // This message comprises the following sections:
    // - HEADER
    // - PREREQUISITE section with one RR
    // - UPDATE section with 1 RR.
    // Such a response may be generated by the DNS server as a result
    // of copying the contents of the REQUEST message sent by DDNS client.
    const uint8_t bin_msg[] = {
        // HEADER section starts here (see RFC 2136, section 2).
        0x05, 0xAF, // ID=0x05AF
        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
        0x0, 0x1,   // ZOCOUNT=1
        0x0, 0x2,   // PRCOUNT=2
        0x0, 0x1,   // UPCOUNT=1
        0x0, 0x0,   // ADCOUNT=0

        // Zone section starts here. The The first field comprises
        // the Zone name encoded as a set of labels, each preceded
        // by a length of the following label. The whole Zone name is
        // terminated with a NULL char.
        // For Zone section format see (RFC 2136, section 2.3).
        0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
        0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
        0x0,      // NULL character terminates the Zone name.
        0x0, 0x6, // ZTYPE='SOA'
        0x0, 0x1, // ZCLASS='IN'

        // Prerequisite section starts here. This section comprises two
        // prerequisites:
        // - 'Name is not in use'
        // - 'Name is in use'
        // See RFC 2136, section 2.4 for the format of Prerequisite section.
        // Each prerequisite RR starts with its name. It is expressed in the
        // compressed format as described in RFC 1035, section 4.1.4. The first
        // label is expressed as in case of non-compressed name. It is preceded
        // by the length value. The following two bytes are the pointer to the
        // offset in the message where 'example.com' was used. That is, in the
        // Zone name at offset 12. Pointer starts with two bits set - they
        // mark start of the pointer.

        // First prerequisite. NONE class indicates that the update requires
        // that the name 'foo.example.com' is not use/
        0x03, 0x66, 0x6F, 0x6F, // foo.
        0xC0, 0x0C,             // pointer to example.com.
        0x0, 0x1C,              // TYPE=AAAA
        0x0, 0xFE,              // CLASS=NONE
        0x0, 0x0, 0x0, 0x0,     // TTL=0
        0x0, 0x0,               // RDLENGTH=0

        // Second prerequisite. ANY class indicates tha the update requires
        // that the name 'bar.example.com' exists.
        0x03, 0x62, 0x61, 0x72, // bar.
        0xC0, 0x0C,             // pointer to example.com.
        0x0, 0x1C,              // TYPE=AAAA
        0x0, 0xFF,              // CLASS=ANY
        0x0, 0x0, 0x0, 0x0,     // TTL=0
        0x0, 0x0,               // RDLENGTH=0

        // Update section starts here. The format of this section conforms to
        // RFC 2136, section 2.5. The name of the RR is again expressed in
        // compressed format. The two pointer bytes point to the offset in the
        // message where 'foo.example.com' was used already - 29.
        0xC0, 0x1D,             // pointer to foo.example.com.
        0x0, 0x1C,              // TYPE=AAAA
        0x0, 0x1,               // CLASS=IN
        0xAA, 0xBB, 0xCC, 0xDD, // TTL=0xAABBCCDD
        0x0, 0x10,              // RDLENGTH=16
        // The following 16 bytes of RDATA hold IPv6 address: 2001:db8:1::1.
        0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
    };

    // Create an object to be used to decode the message from the wire format.
    D2UpdateMessage msg(D2UpdateMessage::INBOUND);

    // Decode the message.
    ASSERT_NO_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)));

    // Check that the message header is valid.
    EXPECT_EQ(0x05AF, msg.getId());
    EXPECT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
    EXPECT_EQ(Rcode::YXDOMAIN_CODE, msg.getRcode().getCode());

    // The ZOCOUNT must contain exactly one zone. If it does, we should get
    // the name, class and type of the zone and verify they are valid.
    ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_ZONE));
    D2ZonePtr zone = msg.getZone();
    ASSERT_TRUE(zone);
    EXPECT_EQ("example.com.", zone->getName().toText());
    EXPECT_EQ(RRClass::IN().getCode(), zone->getClass().getCode());

    // Check the Prerequisite section. It should contain two records.
    ASSERT_EQ(2, msg.getRRCount(D2UpdateMessage::SECTION_PREREQUISITE));

    // Proceed to the first prerequisite.
    RRsetIterator rrset_it =
        msg.beginSection(D2UpdateMessage::SECTION_PREREQUISITE);
    RRsetPtr prereq1 = *rrset_it;
    ASSERT_TRUE(prereq1);
    // Check record fields.
    EXPECT_EQ("foo.example.com.", prereq1->getName().toText()); // NAME
    EXPECT_EQ(RRType::AAAA().getCode(), prereq1->getType().getCode()); // TYPE
    EXPECT_EQ(RRClass::NONE().getCode(),
              prereq1->getClass().getCode()); // CLASS
    EXPECT_EQ(0, prereq1->getTTL().getValue()); // TTL
    EXPECT_EQ(0, prereq1->getRdataCount()); // RDLENGTH

    // Move to next prerequisite section.
    ++rrset_it;
    RRsetPtr prereq2 = *rrset_it;
    ASSERT_TRUE(prereq2);
    // Check record fields.
    EXPECT_EQ("bar.example.com.", prereq2->getName().toText()); // NAME
    EXPECT_EQ(RRType::AAAA().getCode(), prereq2->getType().getCode()); // TYPE
    EXPECT_EQ(RRClass::ANY().getCode(), prereq2->getClass().getCode()); // CLASS
    EXPECT_EQ(0, prereq2->getTTL().getValue()); // TTL
    EXPECT_EQ(0, prereq2->getRdataCount()); // RDLENGTH

    // Check the Update section. There is only one record, so beginSection()
    // should return the pointer to this sole record.
    ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_UPDATE));
    rrset_it = msg.beginSection(D2UpdateMessage::SECTION_UPDATE);
    RRsetPtr update = *rrset_it;
    ASSERT_TRUE(update);
    // Check the record fields.
    EXPECT_EQ("foo.example.com.", update->getName().toText()); // NAME
    EXPECT_EQ(RRType::AAAA().getCode(), update->getType().getCode()); // TYPE
    EXPECT_EQ(RRClass::IN().getCode(), update->getClass().getCode()); // CLASS
    EXPECT_EQ(0xAABBCCDD, update->getTTL().getValue()); // TTL
    // There should be exactly one record holding the IPv6 address.
    // This record can be accessed using RdataIterator. This record
    // can be compared with the reference record, holding expected IPv6
    // address using compare function.
    ASSERT_EQ(1, update->getRdataCount());
    RdataIteratorPtr rdata_it = update->getRdataIterator();
    ASSERT_TRUE(rdata_it);
    in::AAAA rdata_ref("2001:db8:1::1");
    EXPECT_EQ(0, rdata_ref.compare(rdata_it->getCurrent()));

    // @todo: at this point we don't test Additional Data records. We may
    // consider implementing tests for it in the future.
}

// This test verifies that the fromWire function throws appropriate exception
// if the message being parsed comprises invalid Opcode (is not a DNS Update).
TEST_F(D2UpdateMessageTest, fromWireInvalidOpcode) {
    // This is a binary representation of the DNS message.
    // It comprises invalid Opcode=3, expected value is 6
    // (Update).
    const uint8_t bin_msg[] = {
        0x05, 0xAF, // ID=0x05AF
        0x98, 0x6,  // QR=1, Opcode=3, RCODE=YXDOMAIN
        0x0, 0x0,   // ZOCOUNT=0
        0x0, 0x0,   // PRCOUNT=0
        0x0, 0x0,   // UPCOUNT=0
        0x0, 0x0    // ADCOUNT=0
    };
    // The 'true' argument passed to the constructor turns the
    // message into the parse mode in which the fromWire function
    // can be used to decode the binary message data.
    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
    // When using invalid Opcode, the fromWire function should
    // throw NotUpdateMessage exception.
    EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
                 isc::d2::NotUpdateMessage);
}

// This test verifies that the fromWire function throws appropriate exception
// if the message being parsed comprises invalid QR flag. The QR bit is
// expected to be set to indicate that the message is a RESPONSE.
TEST_F(D2UpdateMessageTest, fromWireInvalidQRFlag) {
    // This is a binary representation of the DNS message.
    // It comprises invalid QR flag = 0.
    const uint8_t bin_msg[] = {
        0x05, 0xAF, // ID=0x05AF
        0x28, 0x6,  // QR=0, Opcode=6, RCODE=YXDOMAIN
        0x0, 0x0,   // ZOCOUNT=0
        0x0, 0x0,   // PRCOUNT=0
        0x0, 0x0,   // UPCOUNT=0
        0x0, 0x0    // ADCOUNT=0
    };
    // The 'true' argument passed to the constructor turns the
    // message into the parse mode in which the fromWire function
    // can be used to decode the binary message data.
    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
    // When using invalid QR flag, the fromWire function should
    // throw InvalidQRFlag exception.
    EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
                              isc::d2::InvalidQRFlag);
}

// This test verifies that the fromWire function throws appropriate exception
// if the message being parsed comprises more than one (two in this case)
// Zone records.
TEST_F(D2UpdateMessageTest, fromWireTooManyZones) {
    // This is a binary representation of the DNS message. This message
    // comprises two Zone records.
    const uint8_t bin_msg[] = {
        0x05, 0xAF, // ID=0x05AF
        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
        0x0, 0x2,   // ZOCOUNT=2
        0x0, 0x0,   // PRCOUNT=0
        0x0, 0x0,   // UPCOUNT=0
        0x0, 0x0,   // ADCOUNT=0

        // Start first Zone record.
        0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
        0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
        0x0,      // NULL character terminates the Zone name.
        0x0, 0x6, // ZTYPE='SOA'
        0x0, 0x1, // ZCLASS='IN'

        // Start second Zone record. Presence of this record should result
        // in error when parsing this message.
        0x3, 0x63, 0x6F, 0x6D, // com. (0x3 is a length)
        0x0,      // NULL character terminates the Zone name.
        0x0, 0x6, // ZTYPE='SOA'
        0x0, 0x1  // ZCLASS='IN'
    };

    // The 'true' argument passed to the constructor turns the
    // message into the parse mode in which the fromWire function
    // can be used to decode the binary message data.
    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
    // When parsing a message with more than one Zone record,
    // exception should be thrown.
    EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
                 isc::d2::InvalidZoneSection);
}

// This test verifies that the wire format of the message is produced
// in the render mode.
TEST_F(D2UpdateMessageTest, toWire) {
    D2UpdateMessage msg;
    // Set message ID.
    msg.setId(0x1234);
    // Rcode to NOERROR.
    msg.setRcode(Rcode(Rcode::NOERROR_CODE));

    // Set Zone section. This section must comprise exactly
    // one Zone. toWire function would fail if Zone is not set.
    msg.setZone(Name("example.com"), RRClass::IN());

    // Set prerequisites.

    // 'Name Is Not In Use' prerequisite (RFC 2136, section 2.4.5)
    RRsetPtr prereq1(new RRset(Name("foo.example.com"), RRClass::NONE(),
                               RRType::ANY(), RRTTL(0)));
    msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq1);

    // 'Name is In Use' prerequisite (RFC 2136, section 2.4.4)
    RRsetPtr prereq2(new RRset(Name("bar.example.com"), RRClass::ANY(),
                               RRType::ANY(), RRTTL(0)));
    msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq2);

    // Set Update Section.

    // Create RR holding a name being added. This RR is constructed
    // in conformance to RFC 2136, section 2.5.1.
    RRsetPtr updaterr1(new RRset(Name("foo.example.com"), RRClass::IN(),
                                 RRType::A(), RRTTL(10)));
    // RR record is of the type A, thus RDATA holds 4 octet Internet
    // address. This address is 10.10.1.1.
    char rdata1[] = {
        0xA, 0xA , 0x1, 0x1
    };
    InputBuffer buf_rdata1(rdata1, 4);
    updaterr1->addRdata(createRdata(RRType::A(), RRClass::IN(), buf_rdata1,
                                    buf_rdata1.getLength()));
    // Add the RR to the message.
    msg.addRRset(D2UpdateMessage::SECTION_UPDATE, updaterr1);

    // Render message into the wire format.
    MessageRenderer renderer;
    ASSERT_NO_THROW(msg.toWire(renderer));

    // Make sure that created packet is not truncated.
    ASSERT_EQ(77, renderer.getLength());

    // Create input buffer from the rendered data. InputBuffer
    // is handy to validate the byte contents of the rendered
    // message.
    InputBuffer buf(renderer.getData(), renderer.getLength());

    // Start validating the message header.

    // Verify message ID.
    EXPECT_EQ(0x1234, buf.readUint16());
    // The 2-bytes following message ID comprise the following fields:
    // - QR - 1 bit indicating that it is REQUEST. Should be 0.
    // - Opcode - 4 bits which should hold value of 5 indicating this is
    //            an Update message. Binary form is "0101".
    // - Z - These bits are unused for Update Message and should be 0.
    // - RCODE - Response code, set to NOERROR for REQUEST. It is 0.
    //8706391835
    // The binary value is:
    //   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
    // | QR|     Opcode    |           Z               |     RCODE     |
    // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
    // | 0 | 0   1   0   1 | 0   0   0   0   0   0   0 | 0   0   0   0 |
    // +---+---+---+-------+---+---+---+---+---+---+---+---+---+---+---+
    // and the hexadecimal representation is 0x2800.
    EXPECT_EQ(0x2800, buf.readUint16());

    // ZOCOUNT - holds the number of zones for the update. For Request
    // message it must be exactly one record (RFC2136, section 2.3).
    EXPECT_EQ(1, buf.readUint16());

    // PRCOUNT - holds the number of prerequisites. Earlier we have added
    // two prerequisites. Thus, expect that this counter is 2.
    EXPECT_EQ(2, buf.readUint16());

    // UPCOUNT - holds the number of RRs in the Update Section. We have
    // added 1 RR, which adds the name foo.example.com to the Zone.
    EXPECT_EQ(1, buf.readUint16());

    // ADCOUNT - holds the number of RRs in the Additional Data Section.
    EXPECT_EQ(0, buf.readUint16());

    // Start validating the Zone section. This section comprises the
    // following data:
    // - ZNAME
    // - ZTYPE
    // - ZCLASS

    // ZNAME holds 'example.com.' encoded as set of labels. Each label
    // is preceded by its length. The name is ended with the byte holding
    // zero value. This yields the total size of the name in wire format
    // of 13 bytes.

    // The simplest way to convert the name from wire format to a string
    // is to use dns::Name class. It should be ok to rely on the Name class
    // to decode the name, because it is unit tested elsewhere.
    std::string zone_name = readNameFromWire(buf, 13);
    EXPECT_EQ("example.com.", zone_name);

    // ZTYPE of the Zone section must be SOA according to RFC 2136,
    // section 2.3.
    EXPECT_EQ(RRType::SOA().getCode(), buf.readUint16());

    // ZCLASS of the Zone section is IN.
    EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());

    // Start checks on Prerequisite section. Each prerequisite comprises
    // the following fields:
    // - NAME - name of the RR in wire format
    // - TYPE - two octets with one of the RR TYPE codes
    // - CLASS - two octets with one of the RR CLASS codes
    // - TTL - a 32-bit signed integer specifying Time-To-Live
    // - RDLENGTH - length of the RDATA field
    // - RDATA - a variable length string of octets containing
    //           resource data.
    // In case of this message, we expect to have two prerequisite RRs.
    // Their structure is checked below.

    // First prerequisite should comprise the 'Name is not in use prerequisite'
    // for 'foo.example.com'.

    // Check the name first. Message renderer is using compression for domain
    // names as described in RFC 1035, section 4.1.4. The name in this RR is
    // foo.example.com. The name of the zone is example.com and it has occurred
    // in this message already at offset 12 (the size of the header is 12).
    // Therefore, name of this RR is encoded as 'foo', followed by a pointer
    // to offset in this message where the remainder of this name was used.
    // This pointer has the following format:
    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    // | 1  1|                 OFFSET                  |
    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    // | 1  1| 0  0  0  0  0  0  0  0  0  0  1  1  0  0|
    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    // which has a following hexadecimal representation: 0xC00C

    // Let's read the non-compressed part first - 'foo.'
    std::string name_prereq1 = readNameFromWire(buf, 4, true);
    EXPECT_EQ("foo.", name_prereq1);
    // The remaining two bytes hold the pointer to 'example.com'.
    EXPECT_EQ(0xC00C, buf.readUint16());
    // TYPE is ANY
    EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
    // CLASS is NONE
    EXPECT_EQ(RRClass::NONE().getCode(), buf.readUint16());
    // TTL is a 32-but value, expecting 0
    EXPECT_EQ(0, buf.readUint32());
    // There is no RDATA, so RDLENGTH is 0
    EXPECT_EQ(0, buf.readUint16());

    // Start checking second prerequisite.

    std::string name_prereq2 = readNameFromWire(buf, 4, true);
    EXPECT_EQ("bar.", name_prereq2);
    // The remaining two bytes hold the pointer to 'example.com'.
    EXPECT_EQ(0xC00C, buf.readUint16());
    // TYPE is ANY
    EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
    // CLASS is ANY
    EXPECT_EQ(RRClass::ANY().getCode(), buf.readUint16());
    // TTL is a 32-but value, expecting 0
    EXPECT_EQ(0, buf.readUint32());
    // There is no RDATA, so RDLENGTH is 0
    EXPECT_EQ(0, buf.readUint16());

    // Start checking Update section. This section contains RRset with
    // one A RR.

    // The name of the RR is 'foo.example.com'. It is encoded in the
    // compressed format - as a pointer to the name of prerequisite 1.
    // This name is in offset 0x1D in this message.
    EXPECT_EQ(0xC01D, buf.readUint16());
    // TYPE is A
    EXPECT_EQ(RRType::A().getCode(), buf.readUint16());
    // CLASS is IN (same as zone class)
    EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
    // TTL is a 32-but value, set here to 10.
    EXPECT_EQ(10, buf.readUint32());
    // For A records, the RDATA comprises the 4-byte Internet address.
    // So, RDLENGTH is 4.
    EXPECT_EQ(4, buf.readUint16());
    // We have stored the following address in RDATA field: 10.10.1.1
    // (which is 0A 0A 01 01) in hexadecimal format.
    EXPECT_EQ(0x0A0A0101, buf.readUint32());

    // @todo: consider extending this test to verify Additional Data
    // section.
}

// This test verifies that an attempt to call toWire function on the
// received message will result in an exception.
TEST_F(D2UpdateMessageTest, toWireInvalidQRFlag) {
    // This is a binary representation of the DNS message.
    // This message is valid and should be parsed with no
    // error.
    const uint8_t bin_msg[] = {
        0x05, 0xAF, // ID=0x05AF
        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
        0x0, 0x0,   // ZOCOUNT=0
        0x0, 0x0,   // PRCOUNT=0
        0x0, 0x0,   // UPCOUNT=0
        0x0, 0x0    // ADCOUNT=0
    };

    // The 'true' argument passed to the constructor turns the
    // message into the parse mode in which the fromWire function
    // can be used to decode the binary message data.
    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
    ASSERT_NO_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)));

    // The message is parsed. The QR Flag should now indicate that
    // it is a Response message.
    ASSERT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());

    // An attempt to call toWire on the Response message should
    // result in the InvalidQRFlag exception.
    MessageRenderer renderer;
    EXPECT_THROW(msg.toWire(renderer), isc::d2::InvalidQRFlag);
}

// TSIG test
TEST_F(D2UpdateMessageTest, validTSIG) {
    // Create a TSIG Key and context
    std::string secret("this key will match");
    D2TsigKeyPtr right_key;
    ASSERT_NO_THROW(right_key.reset(new
                                    D2TsigKey(Name("right.com"),
                                              TSIGKey::HMACMD5_NAME(),
                                              secret.c_str(), secret.size())));

    D2TsigKeyPtr wrong_key;
    secret = "this key will not match";
    ASSERT_NO_THROW(wrong_key.reset(new
                                    D2TsigKey(Name("wrong.com"),
                                              TSIGKey::HMACMD5_NAME(),
                                              secret.c_str(), secret.size())));

    // Build a request message
    D2UpdateMessage msg;
    msg.setId(0x1234);
    msg.setRcode(Rcode(Rcode::NOERROR_CODE));
    msg.setZone(Name("example.com"), RRClass::IN());
    RRsetPtr prereq1(new RRset(Name("foo.example.com"), RRClass::NONE(),
                               RRType::ANY(), RRTTL(0)));
    msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq1);
    RRsetPtr prereq2(new RRset(Name("bar.example.com"), RRClass::ANY(),
                               RRType::ANY(), RRTTL(0)));
    msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq2);
    RRsetPtr updaterr1(new RRset(Name("foo.example.com"), RRClass::IN(),
                                 RRType::A(), RRTTL(10)));
    char rdata1[] = {
        0xA, 0xA , 0x1, 0x1
    };
    InputBuffer buf_rdata1(rdata1, 4);
    updaterr1->addRdata(createRdata(RRType::A(), RRClass::IN(), buf_rdata1,
                                    buf_rdata1.getLength()));
    msg.addRRset(D2UpdateMessage::SECTION_UPDATE, updaterr1);

    // Make a context to send the message with and use it to render
    // the message into the wire format.
    TSIGContextPtr context;
    ASSERT_NO_THROW(context.reset(new TSIGContext(*right_key)));
    MessageRenderer renderer;
    ASSERT_NO_THROW(msg.toWire(renderer, context.get()));

    // Grab the wire data from the signed message.
    const void* wire_data = renderer.getData();
    const size_t wire_size = renderer.getLength();

    // Make a context with the wrong key and use it to convert the wired data.
    // Verification should fail.
    D2UpdateMessage msg2(D2UpdateMessage::INBOUND);
    ASSERT_NO_THROW(context.reset(new TSIGContext(*wrong_key)));
    ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
                 TSIGVerifyError);

    // Now make a context with the correct key and try again.
    // If the message passes TSIG verification, then the QR Flag test in
    // the subsequent call to D2UpdateMessage::validateResponse should
    // fail because this isn't really received message.
    ASSERT_NO_THROW(context.reset(new TSIGContext(*right_key)));
    ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
                 InvalidQRFlag);
}

// Tests message signing and verification for all supported algorithms.
TEST_F(D2UpdateMessageTest, allValidTSIG) {
    std::vector<std::string>algorithms;
    algorithms.push_back(TSIGKeyInfo::HMAC_MD5_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA1_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA224_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA256_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA384_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA512_STR);

    dns::Name key_name("test_key");
    std::string secret("random text for secret");
    for (int i = 0; i < algorithms.size(); ++i) {
        D2TsigKey key(key_name,
                      TSIGKeyInfo::stringToAlgorithmName(algorithms[i]),
                      secret.c_str(), secret.size());

        // Build a request message
        D2UpdateMessage msg;
        msg.setId(0x1234);
        msg.setRcode(Rcode(Rcode::NOERROR_CODE));
        msg.setZone(Name("example.com"), RRClass::IN());

        // Make a context to send the message with and use it to render
        // the message into the wire format.
        TSIGContextPtr context;
        ASSERT_NO_THROW(context.reset(new TSIGContext(key)));
        MessageRenderer renderer;
        ASSERT_NO_THROW(msg.toWire(renderer, context.get()));

        // Grab the wire data from the signed message.
        const void* wire_data = renderer.getData();
        const size_t wire_size = renderer.getLength();

        // Create a fresh context to "receive" the message. (We can't use the
        // one we signed it with, as its expecting a signed response to its
        // request. Here we are acting like the server).
        // If the message passes TSIG verification, then the QR Flag test in
        // the subsequent call to D2UpdateMessage::validateResponse should
        // fail because this isn't really received message.
        ASSERT_NO_THROW(context.reset(new TSIGContext(key)));
        D2UpdateMessage msg2(D2UpdateMessage::INBOUND);
        ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
                                   InvalidQRFlag);
    }
}

} // End of anonymous namespace

ext: cc

max_stars_repo_name: oss-mirror/isc-kea

max_issues_repo_name: oss-mirror/isc-kea

max_forks_repo_name: oss-mirror/isc-kea


text:
/// @file partition.hpp A set of tracks and clutter observations which partition all observed features

#ifndef BIGGLES_PARTITION_HPP__
#define BIGGLES_PARTITION_HPP__

#include <boost/shared_ptr.hpp>
#include <boost/any.hpp>
#include <deque>
#include <Eigen/Dense>
#include <iterator>

#include "observation_collection.hpp"
#include "track_collection.hpp"
#include "detail/possible_move_recorder.hpp"
#include "detail/observation_reservoir.hpp"
#include "clutter.hpp"

namespace biggles
{

typedef boost::shared_ptr<const clutter_t> const_clutter_ptr;
typedef boost::shared_ptr<clutter_t> clutter_ptr;

/// @brief A configuration of tracks and spurious observations.
///
/// A full configuration within Biggles consists of a collection of tracks for observations assigned to molecules and a
/// collection of observations deemed to be spurious detections. These spurious detections are called the 'clutter'.
///
/// A partition instance maintains a \c boost::shared_ptr pointing to a const biggles::track_collection and const
/// biggles::observation_collection. In this way, partitions are semi-immutable. They are designed to be lightweight and
/// used essentially to refer to a particular pair of track collections and clutter.
///
/// They are thus immutable by design except that they may be assigned to. The track collections and clutter within them
/// cannot be changed.
///
/// A partition's immutability allows it to maintain a few cached parameters, such as minimum and maximum time stamps.
class partition
{
public:

    /// \brief The spatial expansion of a partition
    ///
    /// This is required to calculate the clutter likelihood
    struct expansion_2d {
        float x_lo, x_hi, y_lo, y_hi;
        expansion_2d(float xlo, float xhi, float ylo, float yhi) : x_lo(xlo), x_hi(xhi), y_lo(ylo), y_hi(yhi) {}
        expansion_2d(const expansion_2d& e) : x_lo(e.x_lo), x_hi(e.x_hi), y_lo(e.y_lo), y_hi(e.y_hi) {}
        /// \brief Increases the expansion, if \p min_location or \p max_location are not included in it
        void grow(const point_2d &min_location, const point_2d &max_location) {
            x_lo = std::min(x_lo, min_location.get<0>());
            y_lo = std::min(y_lo, min_location.get<1>());
            x_hi = std::max(x_hi, max_location.get<0>());
            y_hi = std::max(y_hi, max_location.get<1>());
        }
        /// \brief Increases the expansion, if \p location is not included in it
        void grow(const point_2d &location){
            x_lo = std::min(x_lo, location.get<0>());
            y_lo = std::min(y_lo, location.get<1>());
            x_hi = std::max(x_hi, location.get<0>());
            y_hi = std::max(y_hi, location.get<1>());
        }
    private:
        /// \brief default construction is not allowed
        expansion_2d();
    };

    /// @brief A shared pointer to a biggles::track_collection.
    typedef boost::shared_ptr<const track_collection> track_collection_ptr;

    /// @brief A shared pointer to a biggles::observation_collection.
    typedef boost::shared_ptr<const observation_collection> observation_collection_ptr;

    typedef boost::shared_ptr<const ObservationReservoir> observation_pool_ptr;

    /// @brief Default constructor.
    partition()
        : tracks_(new track_collection())
        , clutter_(new clutter_t())
        , observation_pool_(new ObservationReservoir())
        , first_time_stamp_(0)
        , last_time_stamp_(0)
        , expansion_(0.f, 0.f, 0.f, 0.f)
        , observation_count_(0)
    { }

    /// @brief Fully initialise a partition.
    ///
    /// @param tracks
    /// @param clutter
    partition(const track_collection_ptr& tracks,
        const const_clutter_ptr& clutter);

    partition(const track_collection_ptr& tracks, const const_clutter_ptr& clutter, const expansion_2d& expansion);
    partition(const observation_pool_ptr& pool, const track_collection_ptr& tracks, const const_clutter_ptr& clutter);

    partition(const observation_pool_ptr& pool, const track_collection_ptr& tracks,
        const const_clutter_ptr& clutter, const expansion_2d& expansion)
        : tracks_(tracks), clutter_(clutter), observation_pool_(pool), expansion_(expansion)
    {
        update_boundary_cache();
        update_observation_count();
    }
    /// @brief Copy constructor.
    ///
    /// @param p A reference to a partition to copy from.
    partition(const partition& p)
        : tracks_(p.tracks_)
        , clutter_(p.clutter_)
        , observation_pool_(p.observation_pool_)
        , first_time_stamp_(p.first_time_stamp_)
        , last_time_stamp_(p.last_time_stamp_)
        , expansion_(p.expansion_)
        , observation_count_(p.observation_count_)
    { }

    /// @brief Obtain a reference to the track collection for this partition.
    const track_collection& tracks() const { return *tracks_; }

    /// @brief Obtain a reference to the clutter observations for this partition.
    const clutter_t& clutter() const { return *clutter_; }

    /// @brief Obtain a reference to the track collection shared pointer.
    const track_collection_ptr& tracks_ptr() const { return tracks_; }

    /// @brief Obtain a reference to the clutter shared pointer.
    const const_clutter_ptr& clutter_ptr() const { return clutter_; }

    /// @brief The first time stamp covered by this partition.
    time_stamp first_time_stamp() const { return first_time_stamp_; }
    void set_first_time_stamp(const time_stamp& new_time_stamp) { first_time_stamp_ = new_time_stamp; }

    /// @brief The first time stamp not covered by this partition after first_time_stamp().
    time_stamp last_time_stamp() const { return last_time_stamp_; }
    void set_last_time_stamp(const time_stamp& new_time_stamp) { last_time_stamp_ = new_time_stamp; }

    /// @brief The number of frames covered by this partition.
    time_stamp duration() const { return last_time_stamp() - first_time_stamp(); }

    /// @brief Assignment operator.
    ///
    /// @param p A reference to a partition to assign from.
    ///
    /// @return A const reference to \c this.
    const partition& operator = (const partition& p) {
        if (this == &p) return *this;
        tracks_ = p.tracks_;
        clutter_ = p.clutter_;
        observation_pool_ = p.observation_pool_;
        first_time_stamp_ = p.first_time_stamp_;
        last_time_stamp_ = p.last_time_stamp_;
        expansion_ = p.expansion_;
        observation_count_ = p.observation_count_;
        return *this;
    }

    /// \brief Constant access to the expansion
    const expansion_2d& expansion() const { return expansion_; }
    /// \brief Set the expansion from the minimal and maximal coordinates
    void set_expansion(const float xlo, const float xhi, const float ylo, const float yhi) {
        expansion_ = expansion_2d(xlo, xhi, ylo, yhi);
    }
    /// \brief The area of the rectangle which is the expansion
    float volume() const {
        return (expansion_.x_hi - expansion_.x_lo) * (expansion_.y_hi - expansion_.y_lo);
    }

    std::string string_from_track(const shared_const_track_ptr& track_p) const;
    std::string as_string() const;

    const std::string str() const { return "track_partition"; }

    /// \brief the total number of observations
    const size_t observation_count() const { return observation_count_; }

    /// \brief the observation pool
    const observation_pool_ptr& pool() const { return observation_pool_; }

    void set_capability_recorder(const capability_recorder_ptr &cr_ptr) { cr_ptr_ = cr_ptr; }
    capability_recorder_ptr get_capability_recorder() const { return cr_ptr_; }

    template<class VALUE_TYPE> void set_misc(const VALUE_TYPE& val) { misc_ = val; }
    boost::any misc() const { return misc_; }

protected:
    /// @brief The collection of tracks associated with this partition.
    track_collection_ptr tracks_;

    /// @brief The clutter observations associated with this partition.
    const_clutter_ptr clutter_;

    /// @brief A reference to the pool of all observations
    observation_pool_ptr observation_pool_;

    /// @brief The minimum time stamp observed in the partition.
    time_stamp first_time_stamp_;

    /// @brief A time stamp just beyond the last observed in the partition.
    time_stamp last_time_stamp_;

    /// @brief The spatial expantion
    expansion_2d expansion_;

    /// @brief The total number of observations
    size_t observation_count_;

    /// \brief This is ugly: pointer to capability recorder
    capability_recorder_ptr cr_ptr_;

    /// \brief Anything else
    boost::any misc_;

    /// @brief Update the partition boundary information from the tracks and clutter observations.
    void update_boundary_cache();

    /// @brief Update the total observation count from the tracks and clutter observations.
    void update_observation_count();

    void set_observation_pool_from_tracks_clutter() {
        std::deque<observation> all_obs;
        ingest_observations(all_obs, *clutter_);
        ingest_observations(all_obs, *tracks_);
        observation_pool_ = observation_pool_ptr(new ObservationReservoir(all_obs.begin(), all_obs.end()));
    }

};

struct internal_containers_observer {
    size_t size_cross_over_pairs;
    size_t size_extendible_tracks;
    size_t size_mergeable_pairs;
    size_t size_transfer_pairs;

    const internal_containers_observer& update(const capability_recorder& p) {
        size_cross_over_pairs = p._size_cross_over_pairs_();
        size_extendible_tracks = p._size_extendible_tracks_();
        size_mergeable_pairs = p._size_mergeable_pairs_();
        size_transfer_pairs = p._size_transfer_pairs_();
        return *this;
    }

    std::deque<uint64_t> get() const {
        std::deque<uint64_t> values(4);
        values[0] = size_mergeable_pairs;
        values[1] = size_cross_over_pairs;
        values[2] = size_transfer_pairs;
        values[3] = size_extendible_tracks;
        return values;
    }
};

typedef boost::shared_ptr<partition> partition_ptr_t;

/// \brief creates a smallest expansion that contains all \p tracks and all \p clutter observations
partition::expansion_2d expansion_from_observations(const track_collection& tracks,
                        const clutter_t& clutter);

partition value_of(const partition_ptr_t& partition_p);

inline capability_recorder_ptr new_cap_recorder(const partition& part) {
    return capability_recorder_ptr(
        new capability_recorder(part.first_time_stamp(), part.last_time_stamp(), part.tracks()));
}

/// \brief checks if a partition is valid
bool verify_partition(partition& p);
} // namespace biggles

#endif // BIGGLES_PARTITION_HPP__

ext: hpp

max_stars_repo_name: fbi-octopus/biggles

max_issues_repo_name: fbi-octopus/biggles

max_forks_repo_name: fbi-octopus/biggles


text:
#pragma once

#include <beauty/header.hpp>

#include <boost/beast/http.hpp>

namespace beast = boost::beast;

namespace beauty
{
namespace http = beast::http;

// --------------------------------------------------------------------------
class response : public beast::http::response<beast::http::string_body>
{
public:
    using beast::http::response<beast::http::string_body>::response;
    using beast::http::response<beast::http::string_body>::set;
    using beast::http::response<beast::http::string_body>::operator=;

    void set(const beauty::header::content_type& ct) {
        set(beast::http::field::content_type, ct.value);
    }

    void set_header(beast::http::field name, const std::string& value) {
        set(name, value);
    }

    bool is_postponed() const { return _is_postponed; }
    void postpone() { _is_postponed = true; }

    void done() { _cb(); }

    void on_done(std::function<void()>&& cb) {
        _cb = std::move(cb);
    }

    // Result alias and a common helper for no error
    http::status status() const { return result(); }
    bool is_status_ok() const { return status() == http::status::ok; }

private:
    bool    _is_postponed = false;
    std::function<void()> _cb = [](){};
};
}

max_stars_repo_name: nside/beauty

max_issues_repo_name: nside/beauty

max_forks_repo_name: nside/beauty


text:
/*
 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#define BOOST_TEST_MAIN

#define BOOST_TEST_MODULE akimaspline_test

// Standard includes
#include <iostream>

// Third party includes
#include <boost/test/unit_test.hpp>

// Local VOTCA includes
#include "votca/tools/akimaspline.h"

using namespace votca::tools;

BOOST_AUTO_TEST_SUITE(akimaspline_test)

BOOST_AUTO_TEST_CASE(interpolate_test) {

  int size = 80;
  Eigen::VectorXd x = Eigen::VectorXd::Zero(size);
  Eigen::VectorXd y = Eigen::VectorXd::Zero(size);
  for (int i = 0; i < size; ++i) {
    x(i) = 0.25 * i;
    y(i) = std::sin(x(i));
  }
  AkimaSpline cspline;
  cspline.setBCInt(0);
  cspline.Interpolate(x, y);

  Eigen::VectorXd rs = Eigen::VectorXd::Zero(10);
  rs << 0.45, 0.47, 0.8, 0.75, 0.6, 0.4, 0.9, 0.55, 0, 0;
  Eigen::VectorXd values_ref = Eigen::VectorXd::Zero(10);
  values_ref << 0.434362, 0.452449, 0.717761, 0.681639, 0.564937, 0.388734,
      0.783279, 0.52316, 0, 0;
  Eigen::VectorXd derivatives_ref = Eigen::VectorXd::Zero(10);
  derivatives_ref << 0.906562, 0.902216, 0.698382, 0.747323, 0.818045, 0.918909,
      0.615268, 0.854075, 1.02038, 1.02038;
  Eigen::VectorXd values = cspline.Calculate(rs);
  Eigen::VectorXd derivatives = cspline.CalculateDerivative(rs);

  bool equal_val = values_ref.isApprox(values, 1e-5);

  if (!equal_val) {
    std::cout << "result value" << std::endl;
    std::cout << values.transpose() << std::endl;
    std::cout << "ref value" << std::endl;
    std::cout << values_ref.transpose() << std::endl;
  }
  BOOST_CHECK_EQUAL(equal_val, true);

  bool equal_derivative = derivatives_ref.isApprox(derivatives, 1e-5);

  if (!equal_derivative) {
    std::cout << "result value" << std::endl;
    std::cout << derivatives.transpose() << std::endl;
    std::cout << "ref value" << std::endl;
    std::cout << derivatives_ref.transpose() << std::endl;
  }
  BOOST_CHECK_EQUAL(equal_derivative, true);
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: MrTheodor/tools

max_issues_repo_name: MrTheodor/tools

max_forks_repo_name: MrTheodor/tools


text:
#include "image_reader.h"

#include "itkImageFileReader.h"
#include "itkImageSeriesReader.h"

#include <ostream>

#include <boost/filesystem.hpp>
#include <boost/regex.hpp>

#include "log4cxx/logger.h"

typedef itk::ImageFileReader< ImageType > ITKImageReader;
typedef itk::ImageSeriesReader< ImageType > ITKImageSeriesReader;

ImageType::Pointer ImageReader::read(const std::string filename)
{
	log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("main"));

	LOG4CXX_INFO(logger, "Reading image \"" << filename << "\"");

	try
	{
		boost::filesystem::path path(filename);

		if(boost::filesystem::exists(path)) {
			ImageType::Pointer img;

			if(boost::filesystem::is_directory(path))
			{
				LOG4CXX_DEBUG(logger, path << " is a folder");

				img = readImageSerie(filename);
			} else {
				LOG4CXX_DEBUG(logger, path << " is a file");

				img = readImage(filename);
			}

			LOG4CXX_INFO(logger, "Image " << path << " loaded");

			return img;
		} else {
			std::stringstream err;
			err << "\"" << filename << "\" does not exists";

			LOG4CXX_FATAL(logger, err.str());

			throw ImageReadingException(err.str());
		}
	} catch(boost::filesystem::filesystem_error &ex) {
		std::stringstream err;
		err << filename << " cannot be read (" << ex.what() << ")" << std::endl;
		throw ImageReadingException(err.str());
	}
}

ImageType::Pointer ImageReader::readImage(const std::string filename)
{
	typename ITKImageReader::Pointer reader = ITKImageReader::New();

	reader->SetFileName(filename);

	try {
		reader->Update();
	}
	catch( itk::ExceptionObject &ex )
	{
		std::stringstream err;
		err << "ITK is unable to read the image \"" << filename << "\" (" << ex.what() << ")";

		throw ImageReadingException(err.str());
	}

	return reader->GetOutput();
}

ImageType::Pointer ImageReader::readImageSerie(const std::string filename)
{
	typename ITKImageSeriesReader::Pointer reader = ITKImageSeriesReader::New();

	typename ITKImageSeriesReader::FileNamesContainer filenames;

	log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("main"));

	try
	{
		boost::filesystem::path path(filename);
		boost::filesystem::directory_iterator end_iter;
		boost::regex pattern(".*\\.((?:png)|(?:bmp)|(?:jpe?g))", boost::regex::icase);

		for( boost::filesystem::directory_iterator dir_iter(path) ; dir_iter != end_iter ; ++dir_iter)
		{
			boost::smatch match;
			if( !boost::regex_match( dir_iter->path().filename().string(), match, pattern ) ) continue;

			LOG4CXX_DEBUG(logger, "Reading \"" << boost::filesystem::absolute(dir_iter->path()).string() << "\"");

			filenames.push_back(boost::filesystem::absolute(dir_iter->path()).string());
		}
	}
	catch(boost::filesystem::filesystem_error &ex) {
		std::stringstream err;
		err << filename << " cannot be read (" << ex.what() << ")" << std::endl;

		throw ImageReadingException(err.str());
	}

	std::sort(filenames.begin(), filenames.end());

	reader->SetFileNames(filenames);

	try {
		reader->Update();
	}
	catch( itk::ExceptionObject &ex )
	{
		std::stringstream err;
		err << "ITK is unable to read the image serie located in \"" << filename << "\" (" << ex.what() << ")";

		throw ImageReadingException(err.str());
	}

	return reader->GetOutput();
}

ext: cpp

max_stars_repo_name: Sigill/itk-noise-utils

max_issues_repo_name: Sigill/itk-noise-utils

max_forks_repo_name: Sigill/itk-noise-utils


text:
#pragma once

#include <boost/container/pmr/monotonic_buffer_resource.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <unordered_set>

#include "base_column_statistics.hpp"
#include "column_statistics.hpp"
#include "resolve_type.hpp"
#include "storage/segment_iterate.hpp"
#include "storage/table.hpp"

namespace opossum {

/**
 * Generate the statistics of a single column. Used by generate_table_statistics()
 */
template <typename ColumnDataType>
std::shared_ptr<BaseColumnStatistics> generate_column_statistics(const Table& table, const ColumnID column_id) {
  // distinct_set is thrown away at the end of this method, so we don't want proper heap allocations for the strings
  // stored within. The initial size of the buffer is a completely random guess, but better than zero.

  auto temp_buffer = boost::container::pmr::monotonic_buffer_resource(table.row_count() * sizeof(ColumnDataType));
  auto distinct_set =
      std::unordered_set<ColumnDataType, std::hash<ColumnDataType>, std::equal_to<ColumnDataType>,
                         PolymorphicAllocator<ColumnDataType>>(PolymorphicAllocator<ColumnDataType>{&temp_buffer});
  distinct_set.reserve(table.row_count());

  auto null_value_count = size_t{0};

  auto min = std::numeric_limits<ColumnDataType>::max();
  auto max = std::numeric_limits<ColumnDataType>::lowest();

  for (ChunkID chunk_id{0}; chunk_id < table.chunk_count(); ++chunk_id) {
    const auto base_segment = table.get_chunk(chunk_id)->get_segment(column_id);

    segment_iterate<ColumnDataType>(*base_segment, [&](const auto& position) {
      if (position.is_null()) {
        ++null_value_count;
      } else {
        distinct_set.insert(position.value());
        min = std::min(min, position.value());
        max = std::max(max, position.value());
      }
    });
  }

  const auto null_value_ratio =
      table.row_count() > 0 ? static_cast<float>(null_value_count) / static_cast<float>(table.row_count()) : 0.0f;
  const auto distinct_count = static_cast<float>(distinct_set.size());

  if (distinct_count == 0.0f) {
    min = std::numeric_limits<ColumnDataType>::min();
    max = std::numeric_limits<ColumnDataType>::max();
  }

  return std::make_shared<ColumnStatistics<ColumnDataType>>(null_value_ratio, distinct_count, min, max);
}

template <>
std::shared_ptr<BaseColumnStatistics> generate_column_statistics<std::string>(const Table& table,
                                                                              const ColumnID column_id);

}  // namespace opossum

max_stars_repo_name: nilsthamm/hyrise

max_issues_repo_name: nilsthamm/hyrise

max_forks_repo_name: nilsthamm/hyrise