Program Listing for File common.hpp

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

#ifndef LIBCAER_EVENTS_COMMON_HPP_
#define LIBCAER_EVENTS_COMMON_HPP_

#include "../libcaer.hpp"

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

#include <cassert>
#include <memory>
#include <utility>

namespace libcaer {
namespace events {

template<class T>
class EventPacketIterator {
private:
    // Select proper pointer type (const or not) depending on template type.
    using eventPtrType = typename std::conditional<std::is_const<T>::value, const uint8_t *, uint8_t *>::type;

    eventPtrType eventPtr;
    size_t eventSize;

public:
    // Iterator traits.
    using iterator_category = std::random_access_iterator_tag;
    using value_type        = typename std::remove_cv<T>::type;
    using pointer           = T *;
    using reference         = T &;
    using difference_type   = ptrdiff_t;
    using size_type         = int32_t;

    // Constructors.
    EventPacketIterator() : eventPtr(nullptr), eventSize(0) {
    }

    EventPacketIterator(eventPtrType _eventPtr, size_t _eventSize) : eventPtr(_eventPtr), eventSize(_eventSize) {
    }

    // Data access operators.
    reference operator*() const noexcept {
        return (*reinterpret_cast<pointer>(eventPtr));
    }

    pointer operator->() const noexcept {
        return (reinterpret_cast<pointer>(eventPtr));
    }

    reference operator[](size_type index) const noexcept {
        return (*reinterpret_cast<pointer>(eventPtr + (index * eventSize)));
    }

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

    bool operator!=(const EventPacketIterator &rhs) const noexcept {
        return (eventPtr != rhs.eventPtr);
    }

    bool operator<(const EventPacketIterator &rhs) const noexcept {
        return (eventPtr < rhs.eventPtr);
    }

    bool operator>(const EventPacketIterator &rhs) const noexcept {
        return (eventPtr > rhs.eventPtr);
    }

    bool operator<=(const EventPacketIterator &rhs) const noexcept {
        return (eventPtr <= rhs.eventPtr);
    }

    bool operator>=(const EventPacketIterator &rhs) const noexcept {
        return (eventPtr >= rhs.eventPtr);
    }

    // Prefix increment.
    EventPacketIterator &operator++() noexcept {
        eventPtr += eventSize;
        return (*this);
    }

    // Postfix increment.
    EventPacketIterator operator++(int) noexcept {
        eventPtrType currPtr = eventPtr;
        eventPtr += eventSize;
        return (EventPacketIterator(currPtr, eventSize));
    }

    // Prefix decrement.
    EventPacketIterator &operator--() noexcept {
        eventPtr -= eventSize;
        return (*this);
    }

    // Postfix decrement.
    EventPacketIterator operator--(int) noexcept {
        eventPtrType currPtr = eventPtr;
        eventPtr -= eventSize;
        return (EventPacketIterator(currPtr, eventSize));
    }

    // Iter += N.
    EventPacketIterator &operator+=(size_type add) noexcept {
        eventPtr += (eventSize * add);
        return (*this);
    }

    // Iter + N.
    EventPacketIterator operator+(size_type add) const noexcept {
        return (EventPacketIterator(eventPtr + (eventSize * add), eventSize));
    }

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

    // Iter -= N.
    EventPacketIterator &operator-=(size_type sub) noexcept {
        eventPtr -= (eventSize * sub);
        return (*this);
    }

    // Iter - N. (N - Iter doesn't make sense!)
    EventPacketIterator operator-(size_type sub) const noexcept {
        return (EventPacketIterator(eventPtr - (eventSize * sub), eventSize));
    }

    // Iter - Iter. (Iter + Iter doesn't make sense!)
    difference_type operator-(const EventPacketIterator &rhs) const noexcept {
        // Distance in pointed-to-elements, so of eventSize size, hence
        // why the division by eventSize is necessary.
        return ((eventPtr - rhs.eventPtr) / eventSize);
    }

    // Swap two iterators.
    void swap(EventPacketIterator &rhs) noexcept {
        std::swap(eventPtr, rhs.eventPtr);
        std::swap(eventSize, rhs.eventSize);
    }
};

class EventPacket {
protected:
    caerEventPacketHeader header;
    bool isMemoryOwner;

    // Constructors.
    EventPacket() : header(nullptr), isMemoryOwner(true) {
    }

public:
    EventPacket(caerEventPacketHeader packetHeader, bool takeMemoryOwnership = true) {
        constructorCheckNullptr(packetHeader);

        if (caerEventPacketHeaderGetEventType(packetHeader) < CAER_DEFAULT_EVENT_TYPES_COUNT) {
            throw std::runtime_error("Failed to initialize EventPacketHeader from existing C packet header: default "
                                     "event types are not allowed. "
                                     "Always call the proper specialized <Type>EventPacket constructor, to guarantee "
                                     "proper RTTI initialization.");
        }

        header        = packetHeader;
        isMemoryOwner = takeMemoryOwnership;
    }

    // Destructor.
    virtual ~EventPacket() {
        // Support not freeing memory, when this packet doesn't own the memory.
        if (isMemoryOwner) {
            // All EventPackets must have been allocated somewhere on the heap,
            // and can thus always be passed to free(). free(nullptr) does nothing.
            free(header);
        }
    }

    // Copy constructor.
    EventPacket(const EventPacket &rhs) {
        // Full copy.
        header        = internalCopy(rhs.header, copyTypes::FULL);
        isMemoryOwner = true; // Always memory owner on copy!
    }

    // Copy assignment.
    EventPacket &operator=(const EventPacket &rhs) {
        // If both the same, do nothing.
        if (this != &rhs) {
            // Different packets, so we need to check if they are the same type.
            if (getEventType() != rhs.getEventType()) {
                throw std::invalid_argument("Event type must be the same.");
            }

            // They are, so we can make a copy, and if successful, put it in place
            // of the old data. internalCopy() checks for nullptr.
            caerEventPacketHeader copy = internalCopy(rhs.header, copyTypes::FULL);

            // Destroy current data, only if actually owned.
            if (isMemoryOwner) {
                free(header);
            }

            header        = copy;
            isMemoryOwner = true; // Always memory owner on copy!
        }

        return (*this);
    }

    // Move constructor.
    EventPacket(EventPacket &&rhs) noexcept {
        // Moved-from object must remain in a valid state. We can define
        // valid-state-after-move to be nothing allowed but a destructor
        // call, which is what normally happens, and helps us a lot here.

        // Move data here.
        header        = rhs.header;
        isMemoryOwner = rhs.isMemoryOwner; // Move memory ownership too!

        // Reset old data (ready for destruction).
        rhs.header = nullptr;
    }

    // Move assignment.
    EventPacket &operator=(EventPacket &&rhs) {
        assert(this != &rhs);

        // Different packets, so we need to check if they are the same type.
        if (getEventType() != rhs.getEventType()) {
            throw std::invalid_argument("Event type must be the same.");
        }

        // Moved-from object must remain in a valid state. We can define
        // valid-state-after-move to be nothing allowed but a destructor
        // call, which is what normally happens, and helps us a lot here.

        // Destroy current data, only if actually owned.
        if (isMemoryOwner) {
            free(header);
        }

        // Move data here.
        header        = rhs.header;
        isMemoryOwner = rhs.isMemoryOwner; // Move memory ownership too!

        // Reset old data (ready for destruction).
        rhs.header = nullptr;

        return (*this);
    }

    // Comparison operators.
    bool operator==(const EventPacket &rhs) const noexcept {
        return (caerEventPacketEquals(header, rhs.header));
    }

    bool operator!=(const EventPacket &rhs) const noexcept {
        return (!caerEventPacketEquals(header, rhs.header));
    }

    // Header data methods.
    int16_t getEventType() const noexcept {
        return (caerEventPacketHeaderGetEventType(header));
    }

    void setEventType(int16_t eventType) {
        if (eventType < 0) {
            throw std::invalid_argument("Negative event type not allowed.");
        }

        return (caerEventPacketHeaderSetEventType(header, eventType));
    }

    int16_t getEventSource() const noexcept {
        return (caerEventPacketHeaderGetEventSource(header));
    }

    void setEventSource(int16_t eventSource) {
        if (eventSource < 0) {
            throw std::invalid_argument("Negative event source not allowed.");
        }

        return (caerEventPacketHeaderSetEventSource(header, eventSource));
    }

    int32_t getEventSize() const noexcept {
        return (caerEventPacketHeaderGetEventSize(header));
    }

    void setEventSize(int32_t eventSize) {
        if (eventSize < 0) {
            throw std::invalid_argument("Negative event size not allowed.");
        }

        return (caerEventPacketHeaderSetEventSize(header, eventSize));
    }

    int32_t getEventTSOffset() const noexcept {
        return (caerEventPacketHeaderGetEventTSOffset(header));
    }

    void setEventTSOffset(int32_t eventTSOffset) {
        if (eventTSOffset < 0) {
            throw std::invalid_argument("Negative event TS offset not allowed.");
        }

        return (caerEventPacketHeaderSetEventTSOffset(header, eventTSOffset));
    }

    int32_t getEventTSOverflow() const noexcept {
        return (caerEventPacketHeaderGetEventTSOverflow(header));
    }

    void setEventTSOverflow(int32_t eventTSOverflow) {
        if (eventTSOverflow < 0) {
            throw std::invalid_argument("Negative event TS overflow not allowed.");
        }

        return (caerEventPacketHeaderSetEventTSOverflow(header, eventTSOverflow));
    }

    int32_t getEventCapacity() const noexcept {
        return (caerEventPacketHeaderGetEventCapacity(header));
    }

    void setEventCapacity(int32_t eventCapacity) {
        if (eventCapacity < 0) {
            throw std::invalid_argument("Negative event capacity not allowed.");
        }

        return (caerEventPacketHeaderSetEventCapacity(header, eventCapacity));
    }

    int32_t getEventNumber() const noexcept {
        return (caerEventPacketHeaderGetEventNumber(header));
    }

    void setEventNumber(int32_t eventNumber) {
        if (eventNumber < 0) {
            throw std::invalid_argument("Negative event number not allowed.");
        }

        return (caerEventPacketHeaderSetEventNumber(header, eventNumber));
    }

    int32_t getEventValid() const noexcept {
        return (caerEventPacketHeaderGetEventValid(header));
    }

    void setEventValid(int32_t eventValid) {
        if (eventValid < 0) {
            throw std::invalid_argument("Negative event valid not allowed.");
        }

        return (caerEventPacketHeaderSetEventValid(header, eventValid));
    }

    // Generic Event definiton.
    struct GenericEvent {
        const void *event;
        caerEventPacketHeaderConst header;

        int32_t getTimestamp() const noexcept {
            return (caerGenericEventGetTimestamp(event, header));
        }

        int64_t getTimestamp64() const noexcept {
            return (caerGenericEventGetTimestamp64(event, header));
        }

        bool isValid() const noexcept {
            return (caerGenericEventIsValid(event));
        }

        // C variant.
        void copy(void *eventPtrDestination, caerEventPacketHeaderConst headerPtrDestination) const {
            if (caerEventPacketHeaderGetEventType(headerPtrDestination) != caerEventPacketHeaderGetEventType(header)) {
                throw std::invalid_argument("Event type must be the same.");
            }
            if (caerEventPacketHeaderGetEventSize(headerPtrDestination) != caerEventPacketHeaderGetEventSize(header)) {
                throw std::invalid_argument("Event size must be the same.");
            }
            if (caerEventPacketHeaderGetEventTSOverflow(headerPtrDestination)
                != caerEventPacketHeaderGetEventTSOverflow(header)) {
                throw std::invalid_argument("Event TS overflow must be the same.");
            }

            caerGenericEventCopy(eventPtrDestination, event, headerPtrDestination, header);
        }

        // C++ variants.
        void copy(struct GenericEvent &destinationEvent) const {
            copy(const_cast<void *>(destinationEvent.event), destinationEvent.header);
        }

        void copy(struct GenericEvent *destinationEvent) const {
            copy(const_cast<void *>(destinationEvent->event), destinationEvent->header);
        }
    };

    static_assert(std::is_standard_layout<GenericEvent>::value, "GenericEvent is not standard layout.");

    // Container traits.
    using value_type       = GenericEvent;
    using const_value_type = const GenericEvent;
    using pointer          = GenericEvent *;
    using const_pointer    = const GenericEvent *;
    using reference        = GenericEvent &;
    using const_reference  = const GenericEvent &;
    using size_type        = int32_t;
    using difference_type  = ptrdiff_t;

    // Generic Event access methods.
    const_value_type genericGetEvent(size_type index) const {
        // Accessing elements after size() but before capacity() doesn't
        // make any sense here for Generic Events, as we only support
        // reading/querying data from those events, and that would always
        // fail for those empty events.
        const void *evt = caerGenericEventGetEvent(header, getEventIndex(index, false));

        return (GenericEvent{evt, header});
    }

    // Generic Event Packet methods.
    int64_t getDataSize() const noexcept {
        return (caerEventPacketGetDataSize(header));
    }

    int64_t getSize() const noexcept {
        return (caerEventPacketGetSize(header));
    }

    int64_t getDataSizeEvents() const noexcept {
        return (caerEventPacketGetDataSizeEvents(header));
    }

    int64_t getSizeEvents() const noexcept {
        return (caerEventPacketGetSizeEvents(header));
    }

    void clear() noexcept {
        caerEventPacketClear(header);
    }

    void clean() noexcept {
        caerEventPacketClean(header);
    }

    void resize(size_type newEventCapacity) {
        if (newEventCapacity <= 0) {
            throw std::invalid_argument("Negative or zero event capacity not allowed.");
        }

        caerEventPacketHeader resizedPacket = caerEventPacketResize(header, newEventCapacity);
        if (resizedPacket == nullptr) {
            throw std::bad_alloc();
        }
        else {
            header = resizedPacket;
        }
    }

    void shrink_to_fit() {
        size_type shrunkSize = getEventValid();

        // Zero resize not allowed, must be at least one.
        if (shrunkSize == 0) {
            shrunkSize = 1;
        }

        resize(shrunkSize);
    }

    void grow(size_type newEventCapacity) {
        if (newEventCapacity <= 0) {
            throw std::invalid_argument("Negative or zero event capacity not allowed.");
        }
        if (newEventCapacity <= getEventCapacity()) {
            throw std::invalid_argument("New event capacity must be strictly bigger than old one.");
        }

        caerEventPacketHeader enlargedPacket = caerEventPacketGrow(header, newEventCapacity);
        if (enlargedPacket == nullptr) {
            throw std::bad_alloc();
        }
        else {
            header = enlargedPacket;
        }
    }

    void append(const EventPacket &appendPacket) {
        if (getEventType() != appendPacket.getEventType()) {
            throw std::invalid_argument("Event type must be the same.");
        }
        if (getEventSize() != appendPacket.getEventSize()) {
            throw std::invalid_argument("Event size must be the same.");
        }
        if (getEventTSOverflow() != appendPacket.getEventTSOverflow()) {
            throw std::invalid_argument("Event TS overflow must be the same.");
        }

        caerEventPacketHeader mergedPacket = caerEventPacketAppend(header, appendPacket.header);
        if (mergedPacket == nullptr) {
            throw std::bad_alloc();
        }
        else {
            header = mergedPacket;
        }
    }

    enum class copyTypes { FULL, EVENTS_ONLY, VALID_EVENTS_ONLY };

    std::unique_ptr<EventPacket> copy(copyTypes ct) const {
        return (virtualCopy(ct));
    }

    // Swap two event packets.
    void swap(EventPacket &rhs) {
        if (getEventType() != rhs.getEventType()) {
            throw std::invalid_argument("Event type must be the same.");
        }

        std::swap(header, rhs.header);
        std::swap(isMemoryOwner, rhs.isMemoryOwner);
    }

    // Direct underlying pointer access.
    caerEventPacketHeader getHeaderPointer() noexcept {
        return (header);
    }

    caerEventPacketHeaderConst getHeaderPointer() const noexcept {
        return (header);
    }

    // Memory ownership information.
    bool isPacketMemoryOwner() const noexcept {
        return (isMemoryOwner);
    }

    // Direct underlying pointer access for conversion back to C.
    caerEventPacketHeader getHeaderPointerForCOutput() noexcept {
        isMemoryOwner = false;

        return (header);
    }

    // Convenience methods.
    size_type capacity() const noexcept {
        return (getEventCapacity());
    }

    size_type size() const noexcept {
        return (getEventNumber());
    }

    bool empty() const noexcept {
        return (getEventNumber() == 0);
    }

protected:
    // Internal copy functions.
    virtual std::unique_ptr<EventPacket> virtualCopy(copyTypes ct) const {
        return (std::unique_ptr<EventPacket>(new EventPacket(internalCopy(header, ct))));
    }

    static caerEventPacketHeader internalCopy(caerEventPacketHeaderConst header, copyTypes ct) {
        caerEventPacketHeader packetCopy = nullptr;

        switch (ct) {
            case copyTypes::FULL:
                packetCopy = caerEventPacketCopy(header);
                break;

            case copyTypes::EVENTS_ONLY:
                packetCopy = caerEventPacketCopyOnlyEvents(header);
                break;

            case copyTypes::VALID_EVENTS_ONLY:
                packetCopy = caerEventPacketCopyOnlyValidEvents(header);
                break;
        }

        if (packetCopy == nullptr) {
            throw std::bad_alloc();
        }

        return (packetCopy);
    }

    // Constructor checks.
    static void constructorCheckCapacitySourceTSOverflow(
        size_type eventCapacity, int16_t eventSource, int32_t tsOverflow) {
        if (eventCapacity <= 0) {
            throw std::invalid_argument("Negative or zero event capacity not allowed on construction.");
        }
        if (eventSource < 0) {
            throw std::invalid_argument("Negative event source not allowed.");
        }
        if (tsOverflow < 0) {
            throw std::invalid_argument("Negative event TS overflow not allowed.");
        }
    }

    static void constructorCheckNullptr(const void *packet) {
        if (packet == nullptr) {
            throw std::runtime_error("Failed to initialize event packet: null pointer.");
        }
    }

    static void constructorCheckEventType(caerEventPacketHeaderConst packet, int16_t type) {
        if (caerEventPacketHeaderGetEventType(packet) != type) {
            throw std::runtime_error("Failed to initialize event packet: wrong type.");
        }
    }

    size_type getEventIndex(size_type index, bool limitIsCapacity) const {
        // Support negative indexes to go from the last existing/defined event
        // backwards (not from the capacity!).
        if (index < 0) {
            index = size() + index;
        }

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

        return (index);
    }
};

template<class PKT, class EVT>
class EventPacketCommon : public EventPacket {
public:
    // Container traits.
    using value_type       = EVT;
    using const_value_type = const EVT;
    using pointer          = EVT *;
    using const_pointer    = const EVT *;
    using reference        = EVT &;
    using const_reference  = const EVT &;
    using size_type        = int32_t;
    using difference_type  = ptrdiff_t;

    // Event access methods.
    reference getEvent(size_type index) {
        return (virtualGetEvent(getEventIndex(index, true)));
    }

    const_reference getEvent(size_type index) const {
        return (virtualGetEvent(getEventIndex(index, true)));
    }

    reference operator[](size_type index) {
        return (getEvent(index));
    }

    const_reference operator[](size_type index) const {
        return (getEvent(index));
    }

    reference front() {
        return (getEvent(0));
    }

    const_reference front() const {
        return (getEvent(0));
    }

    reference back() {
        // On empty packet, define back() as returning the same element
        // as front(), the first one, which always exists due to minimum
        // packet capacity being 1.
        if (size() == 0) {
            return (getEvent(0));
        }

        return (getEvent(-1));
    }

    const_reference back() const {
        // On empty packet, define back() as returning the same element
        // as front(), the first one, which always exists due to minimum
        // packet capacity being 1.
        if (size() == 0) {
            return (getEvent(0));
        }

        return (getEvent(-1));
    }

    std::unique_ptr<PKT> copy(copyTypes ct) const {
        return (std::unique_ptr<PKT>(static_cast<PKT *>(virtualCopy(ct).release())));
    }

    // Iterator support.
    using iterator               = EventPacketIterator<value_type>;
    using const_iterator         = EventPacketIterator<const_value_type>;
    using reverse_iterator       = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

    iterator begin() noexcept {
        return (iterator(reinterpret_cast<uint8_t *>(&front()), static_cast<size_t>(getEventSize())));
    }

    iterator end() noexcept {
        // Pointer must be to element one past the end!
        return (iterator(reinterpret_cast<uint8_t *>(&back()) + getEventSize(), static_cast<size_t>(getEventSize())));
    }

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

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

    const_iterator cbegin() const noexcept {
        return (const_iterator(reinterpret_cast<const uint8_t *>(&front()), static_cast<size_t>(getEventSize())));
    }

    const_iterator cend() const noexcept {
        // Pointer must be to element one past the end!
        return (const_iterator(
            reinterpret_cast<const uint8_t *>(&back()) + getEventSize(), static_cast<size_t>(getEventSize())));
    }

    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()));
    }

protected:
    std::unique_ptr<EventPacket> virtualCopy(copyTypes ct) const override {
        return (std::unique_ptr<PKT>(new PKT(internalCopy(header, ct))));
    }

    virtual reference virtualGetEvent(size_type index) noexcept = 0;

    virtual const_reference virtualGetEvent(size_type index) const noexcept = 0;
};
} // namespace events
} // namespace libcaer

#endif /* LIBCAER_EVENTS_COMMON_HPP_ */