Program Listing for File packetContainer.hpp

Return to documentation for file (include/libcaercpp/events/packetContainer.hpp)

#ifndef LIBCAER_EVENTS_PACKETCONTAINER_HPP_
#define LIBCAER_EVENTS_PACKETCONTAINER_HPP_

#include "../../libcaer/events/packetContainer.h"

#include "common.hpp"
#include "utils.hpp"

#include <memory>
#include <utility>
#include <vector>

namespace libcaer {
namespace events {

template<class InteralIterator, class SharedPtrType>
class EventPacketContainerCopyIterator {
private:
    // Original vector iterator or const_iterator.
    InteralIterator eventPacketsIterator;

    // currElement acts as a kind of cache: not only does it allow us
    // to add deep-constness (when needed), but it also stores a copy of
    // the shared_ptr we're iterating over, effectively increasing its
    // reference count by one while it is in use by the iterator and its
    // user, thus ensuring the object can never disappear from under us.
    mutable SharedPtrType currElement;

public:
    // Iterator traits.
    using iterator_category = typename InteralIterator::iterator_category;
    using value_type        = SharedPtrType;
    using pointer           = const SharedPtrType *;
    using reference         = const SharedPtrType &;
    using difference_type   = typename InteralIterator::difference_type;
    using size_type         = typename InteralIterator::difference_type;

    // Constructors.
    EventPacketContainerCopyIterator() {
        // Empty constructor fine here, results in calls to default
        // constructors for members:
        // - eventPacketsIterator() => empty/nothing iterator
        // - currElement() => empty/nullptr shared_ptr
    }

    EventPacketContainerCopyIterator(InteralIterator _eventPacketsIterator) :
        eventPacketsIterator(_eventPacketsIterator) {
        // Don't initialize currElement, it is initialized/updated
        // right before every use.
    }

    // Data access operators.
    reference operator*() const noexcept {
        currElement = *eventPacketsIterator;
        return (currElement);
    }

    pointer operator->() const noexcept {
        currElement = *eventPacketsIterator;
        return (&currElement);
    }

    reference operator[](size_type idx) const noexcept {
        currElement = eventPacketsIterator[idx];
        return (currElement);
    }

    // Comparison operators.
    bool operator==(const EventPacketContainerCopyIterator &rhs) const noexcept {
        return (eventPacketsIterator == rhs.eventPacketsIterator);
    }

    bool operator!=(const EventPacketContainerCopyIterator &rhs) const noexcept {
        return (eventPacketsIterator != rhs.eventPacketsIterator);
    }

    bool operator<(const EventPacketContainerCopyIterator &rhs) const noexcept {
        return (eventPacketsIterator < rhs.eventPacketsIterator);
    }

    bool operator>(const EventPacketContainerCopyIterator &rhs) const noexcept {
        return (eventPacketsIterator > rhs.eventPacketsIterator);
    }

    bool operator<=(const EventPacketContainerCopyIterator &rhs) const noexcept {
        return (eventPacketsIterator <= rhs.eventPacketsIterator);
    }

    bool operator>=(const EventPacketContainerCopyIterator &rhs) const noexcept {
        return (eventPacketsIterator >= rhs.eventPacketsIterator);
    }

    // Prefix increment.
    EventPacketContainerCopyIterator &operator++() noexcept {
        ++eventPacketsIterator;
        return (*this);
    }

    // Postfix increment.
    EventPacketContainerCopyIterator operator++(int) noexcept {
        InteralIterator currIterator = eventPacketsIterator;
        ++eventPacketsIterator;
        return (EventPacketContainerCopyIterator(currIterator));
    }

    // Prefix decrement.
    EventPacketContainerCopyIterator &operator--() noexcept {
        --eventPacketsIterator;
        return (*this);
    }

    // Postfix decrement.
    EventPacketContainerCopyIterator operator--(int) noexcept {
        InteralIterator currIterator = eventPacketsIterator;
        --eventPacketsIterator;
        return (EventPacketContainerCopyIterator(currIterator));
    }

    // Iter += N.
    EventPacketContainerCopyIterator &operator+=(size_type add) noexcept {
        eventPacketsIterator += add;
        return (*this);
    }

    // Iter + N.
    EventPacketContainerCopyIterator operator+(size_type add) const noexcept {
        return (EventPacketContainerCopyIterator(eventPacketsIterator + add));
    }

    // N + Iter. Must be friend as Iter is right-hand-side.
    friend EventPacketContainerCopyIterator operator+(
        size_type lhs, const EventPacketContainerCopyIterator &rhs) noexcept {
        return (EventPacketContainerCopyIterator(rhs.eventPacketsIterator + lhs));
    }

    // Iter -= N.
    EventPacketContainerCopyIterator &operator-=(size_type sub) noexcept {
        eventPacketsIterator -= sub;
        return (*this);
    }

    // Iter - N. (N - Iter doesn't make sense!)
    EventPacketContainerCopyIterator operator-(size_type sub) const noexcept {
        return (EventPacketContainerCopyIterator(eventPacketsIterator - sub));
    }

    // Iter - Iter. (Iter + Iter doesn't make sense!)
    difference_type operator-(const EventPacketContainerCopyIterator &rhs) const noexcept {
        // Distance in pointed-to-elements.
        return (eventPacketsIterator - rhs.eventPacketsIterator);
    }

    // Swap two iterators.
    void swap(EventPacketContainerCopyIterator &rhs) noexcept {
        std::swap(eventPacketsIterator, rhs.eventPacketsIterator);
        std::swap(currElement, rhs.currElement);
    }
};

class EventPacketContainer {
private:
    int64_t lowestEventTimestamp;
    int64_t highestEventTimestamp;
    int32_t eventsNumber;
    int32_t eventsValidNumber;
    std::vector<std::shared_ptr<EventPacket>> eventPackets;

public:
    // Container traits (not really STL compatible).
    using value_type       = std::shared_ptr<EventPacket>;
    using const_value_type = std::shared_ptr<const EventPacket>;
    using size_type        = int32_t;
    using difference_type  = ptrdiff_t;

    EventPacketContainer() :
        lowestEventTimestamp(-1), highestEventTimestamp(-1), eventsNumber(0), eventsValidNumber(0) {
    }

    EventPacketContainer(size_type eventPacketsNumber) :
        lowestEventTimestamp(-1), highestEventTimestamp(-1), eventsNumber(0), eventsValidNumber(0) {
        if (eventPacketsNumber <= 0) {
            throw std::invalid_argument("Negative or zero capacity not allowed on explicit construction.");
        }

        // Initialize and fill vector after having checked size value.
        eventPackets.reserve(static_cast<size_t>(eventPacketsNumber));

        for (size_type i = 0; i < eventPacketsNumber; i++) {
            eventPackets.emplace_back(); // Call empty constructor.
        }
    }

    EventPacketContainer(caerEventPacketContainer packetContainer, bool takeMemoryOwnership = true) {
        if (packetContainer == nullptr) {
            throw std::runtime_error("Failed to initialize event packet container: null pointer.");
        }

        lowestEventTimestamp  = caerEventPacketContainerGetLowestEventTimestamp(packetContainer);
        highestEventTimestamp = caerEventPacketContainerGetHighestEventTimestamp(packetContainer);
        eventsNumber          = caerEventPacketContainerGetEventsNumber(packetContainer);
        eventsValidNumber     = caerEventPacketContainerGetEventsValidNumber(packetContainer);

        // Initialize and fill vector.
        int32_t eventPacketsNumber = caerEventPacketContainerGetEventPacketsNumber(packetContainer);

        eventPackets.reserve(static_cast<size_t>(eventPacketsNumber));

        for (size_type i = 0; i < eventPacketsNumber; i++) {
            caerEventPacketHeader packet = caerEventPacketContainerGetEventPacket(packetContainer, i);

            if (packet != nullptr) {
                eventPackets.push_back(libcaer::events::utils::makeSharedFromCStruct(packet, takeMemoryOwnership));
            }
            else {
                eventPackets.emplace_back(); // Call empty constructor.
            }
        }
    }

    // The default destructor is fine here, as it will call the vector's
    // destructor, which will call all of its content's destructors; those
    // are shared_ptr, so if their count reaches zero it will then call the
    // EventPacketHeader destructor, and everything is fine.
    // Same for copy/move assignment/constructors, the defaults are fine,
    // as vector and shared_ptr take care of all the book-keeping.

    // EventPackets vector accessors.
    size_type capacity() const noexcept {
        return (static_cast<size_type>(eventPackets.capacity()));
    }

    size_type size() const noexcept {
        return (static_cast<size_type>(eventPackets.size()));
    }

    bool empty() const noexcept {
        return (eventPackets.empty());
    }

    void clear() noexcept {
        eventPackets.clear();
    }

    value_type getEventPacket(size_type index) {
        // Support negative indexes to go from the end of the event packet container.
        if (index < 0) {
            index = size() + index;
        }

        if (index < 0 || index >= size()) {
            throw std::out_of_range("Index out of range.");
        }

        return (eventPackets[static_cast<size_t>(index)]);
    }

    value_type operator[](size_type index) {
        return (getEventPacket(index));
    }

    const_value_type getEventPacket(size_type index) const {
        // Support negative indexes to go from the end of the event packet container.
        if (index < 0) {
            index = size() + index;
        }

        if (index < 0 || index >= size()) {
            throw std::out_of_range("Index out of range.");
        }

        return (eventPackets[static_cast<size_t>(index)]);
    }

    const_value_type operator[](size_type index) const {
        return (getEventPacket(index));
    }

    void setEventPacket(size_type index, value_type packetHeader) {
        // Support negative indexes to go from the end of the event packet container.
        if (index < 0) {
            index = size() + index;
        }

        if (index < 0 || index >= size()) {
            throw std::out_of_range("Index out of range.");
        }

        eventPackets[static_cast<size_t>(index)] = packetHeader;

        updateStatistics();
    }

    void addEventPacket(value_type packetHeader) {
        eventPackets.push_back(packetHeader);

        updateStatistics();
    }

    int64_t getLowestEventTimestamp() const noexcept {
        return (lowestEventTimestamp);
    }

    int64_t getHighestEventTimestamp() const noexcept {
        return (highestEventTimestamp);
    }

    int32_t getEventsNumber() const noexcept {
        return (eventsNumber);
    }

    int32_t getEventsValidNumber() const noexcept {
        return (eventsValidNumber);
    }

    void updateStatistics() noexcept {
        int64_t lowestTimestamp  = -1;
        int64_t highestTimestamp = -1;
        int32_t events           = 0;
        int32_t eventsValid      = 0;

        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            // If packet has no events, skip it, it contributes nothing to statistics.
            if (packet->getEventNumber() == 0) {
                continue;
            }

            // Get timestamps to update lowest/highest tracking.
            const auto firstEvent            = packet->genericGetEvent(0);
            int64_t currLowestEventTimestamp = firstEvent.getTimestamp64();

            const auto lastEvent              = packet->genericGetEvent(-1);
            int64_t currHighestEventTimestamp = lastEvent.getTimestamp64();

            // Update tracked timestamps (or initialize if needed).
            if ((lowestTimestamp == -1) || (lowestTimestamp > currLowestEventTimestamp)) {
                lowestTimestamp = currLowestEventTimestamp;
            }

            if ((highestTimestamp == -1) || (highestTimestamp < currHighestEventTimestamp)) {
                highestTimestamp = currHighestEventTimestamp;
            }

            events += packet->getEventNumber();
            eventsValid += packet->getEventValid();
        }

        lowestEventTimestamp  = lowestTimestamp;
        highestEventTimestamp = highestTimestamp;
        eventsNumber          = events;
        eventsValidNumber     = eventsValid;
    }

    value_type findEventPacketByType(int16_t typeID) {
        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            if (packet->getEventType() == typeID) {
                return (packet);
            }
        }

        return (nullptr);
    }

    std::unique_ptr<std::vector<value_type>> findEventPacketsByType(int16_t typeID) {
        auto results = std::unique_ptr<std::vector<value_type>>(new std::vector<value_type>());

        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            if (packet->getEventType() == typeID) {
                results->push_back(packet);
            }
        }

        return (results);
    }

    const_value_type findEventPacketByType(int16_t typeID) const {
        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            if (packet->getEventType() == typeID) {
                return (packet);
            }
        }

        return (nullptr);
    }

    std::unique_ptr<std::vector<const_value_type>> findEventPacketsByType(int16_t typeID) const {
        auto results = std::unique_ptr<std::vector<const_value_type>>(new std::vector<const_value_type>());

        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            if (packet->getEventType() == typeID) {
                results->push_back(packet);
            }
        }

        return (results);
    }

    value_type findEventPacketBySource(int16_t sourceID) {
        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            if (packet->getEventSource() == sourceID) {
                return (packet);
            }
        }

        return (nullptr);
    }

    std::unique_ptr<std::vector<value_type>> findEventPacketsBySource(int16_t sourceID) {
        auto results = std::unique_ptr<std::vector<value_type>>(new std::vector<value_type>());

        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            if (packet->getEventSource() == sourceID) {
                results->push_back(packet);
            }
        }

        return (results);
    }

    const_value_type findEventPacketBySource(int16_t sourceID) const {
        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            if (packet->getEventSource() == sourceID) {
                return (packet);
            }
        }

        return (nullptr);
    }

    std::unique_ptr<std::vector<const_value_type>> findEventPacketsBySource(int16_t sourceID) const {
        auto results = std::unique_ptr<std::vector<const_value_type>>(new std::vector<const_value_type>());

        for (auto &packet : *this) {
            if (packet == nullptr) {
                continue;
            }

            if (packet->getEventSource() == sourceID) {
                results->push_back(packet);
            }
        }

        return (results);
    }

    std::unique_ptr<EventPacketContainer> copyAllEvents() const {
        std::unique_ptr<EventPacketContainer> newContainer
            = std::unique_ptr<EventPacketContainer>(new EventPacketContainer());

        for (auto &packet : *this) {
            if (packet == nullptr) {
                newContainer->addEventPacket(nullptr);
            }
            else {
                newContainer->addEventPacket(
                    std::shared_ptr<EventPacket>(packet->copy(EventPacket::copyTypes::EVENTS_ONLY)));
            }
        }

        return (newContainer);
    }

    std::unique_ptr<EventPacketContainer> copyValidEvents() const {
        std::unique_ptr<EventPacketContainer> newContainer
            = std::unique_ptr<EventPacketContainer>(new EventPacketContainer());

        for (auto &packet : *this) {
            if (packet == nullptr) {
                newContainer->addEventPacket(nullptr);
            }
            else {
                newContainer->addEventPacket(
                    std::shared_ptr<EventPacket>(packet->copy(EventPacket::copyTypes::VALID_EVENTS_ONLY)));
            }
        }

        return (newContainer);
    }

    // Iterator support (the returned shared_ptr are always read-only copies, so actual modifications to
    // what is pointed to can only happen through setEventPacket() and addEventPacket()).
    using iterator         = EventPacketContainerCopyIterator<std::vector<std::shared_ptr<EventPacket>>::iterator,
        std::shared_ptr<EventPacket>>;
    using const_iterator   = EventPacketContainerCopyIterator<std::vector<std::shared_ptr<EventPacket>>::const_iterator,
        std::shared_ptr<const EventPacket>>;
    using reverse_iterator = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

    iterator begin() noexcept {
        return (iterator(eventPackets.begin()));
    }

    iterator end() noexcept {
        return (iterator(eventPackets.end()));
    }

    const_iterator begin() const noexcept {
        return (cbegin());
    }

    const_iterator end() const noexcept {
        return (cend());
    }

    const_iterator cbegin() const noexcept {
        return (const_iterator(eventPackets.cbegin()));
    }

    const_iterator cend() const noexcept {
        return (const_iterator(eventPackets.cend()));
    }

    reverse_iterator rbegin() noexcept {
        return (reverse_iterator(end()));
    }

    reverse_iterator rend() noexcept {
        return (reverse_iterator(begin()));
    }

    const_reverse_iterator rbegin() const noexcept {
        return (crbegin());
    }

    const_reverse_iterator rend() const noexcept {
        return (crend());
    }

    const_reverse_iterator crbegin() const noexcept {
        return (const_reverse_iterator(cend()));
    }

    const_reverse_iterator crend() const noexcept {
        return (const_reverse_iterator(cbegin()));
    }
};
} // namespace events
} // namespace libcaer

#endif /* LIBCAER_EVENTS_PACKETCONTAINER_HPP_ */