Files
sf_daq_buffer/jf-zmqstreamer/include/PacketBuffer.hpp
T
2021-09-14 11:31:10 +02:00

118 lines
3.9 KiB
C++

#ifndef CIRCULAR_BUFFER_TEMPLATE_HPP
#define CIRCULAR_BUFFER_TEMPLATE_HPP
#include <cstddef>
#include <stdexcept>
#include <iostream>
#include <mutex>
#include <cstdint>
#if defined(WIN32) || defined(_WIN32) || defined(MINGW32)
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#endif // defined
/** Linear data buffer (NOT FIFO)
Simplified data buffer that provides pop and push operations and
bundles the actual container with metadata required by <sockets.h>.
It stores the actual data in an accessible C-style array. **/
template <typename T, size_t CAPACITY>
class PacketBuffer{
public:
PacketBuffer() {
// Initialize C-structures as expected by <sockets.h>
for (int i = 0; i < CAPACITY; i++) {
m_recv_buff_ptr[i].iov_base = (void*) &(m_container[i]);
m_recv_buff_ptr[i].iov_len = sizeof(T);
m_msgs[i].msg_hdr.msg_iov = &m_recv_buff_ptr[i];
m_msgs[i].msg_hdr.msg_iovlen = 1;
m_msgs[i].msg_hdr.msg_name = &m_sock_from[i];
m_msgs[i].msg_hdr.msg_namelen = sizeof(sockaddr_in);
}
};
// ~PacketBuffer() {};
/**Diagnostics**/
int size() const { return ( idx_write-idx_read ); }
int capacity() const { return m_capacity; }
bool is_full() const { return bool(idx_write >= m_capacity); }
bool is_empty() const { return bool(idx_write <= idx_read); }
/**Operators**/
void reset(){ idx_write = 0; idx_read = 0; }; // Reset the buffer
T& container(){ return m_container; }; // Direct container reference
mmsghdr& msgs(){ return m_msgs; };
/**Element access**/
T& pop_front(); //Destructive read
const T& peek_front(); //Non-destructive read
void push_back(T item); //Write new element to buffer
/**Fill from UDP receiver (threadsafe)**/
template <typename TY>
void fill_from(TY& recv){
std::lock_guard<std::mutex> g_guard(m_mutex);
this->idx_write = recv.receive_many(m_msgs, this->capacity());
// Returns -1 with errno=11 if no data received
if(idx_write==-1){ idx_write = 0; }
this->idx_read = 0;
}
private:
// Main container
T m_container[CAPACITY];
const size_t m_capacity = CAPACITY;
/**Guards**/
std::mutex m_mutex;
/**Read and write index**/
int idx_write = 0;
int idx_read = 0;
// C-structures as expected by <sockets.h>
mmsghdr m_msgs[CAPACITY];
iovec m_recv_buff_ptr[CAPACITY];
sockaddr_in m_sock_from[CAPACITY];
};
/*********************************************************************/
/*********************************************************************/
/*********************************************************************/
/** Destructive read
Standard read access to queues (i.e. progress the read pointer).
Throws 'std::length_error' if container is empty. **/
template <typename T, size_t CAPACITY>
T& PacketBuffer<T, CAPACITY>::pop_front(){
std::lock_guard<std::mutex> g_guard(m_mutex);
if(this->is_empty()) { throw std::out_of_range("Attempted to read empty queue!"); }
idx_read++;
return m_container[idx_read-1];
}
/** Non-destructive read
Standard, non-destructive read access (does not progress the read pointer).
Throws 'std::length_error' if container is empty. **/
template <typename T, size_t CAPACITY>
const T& PacketBuffer<T, CAPACITY>::peek_front(){
std::lock_guard<std::mutex> g_guard(m_mutex);
if(this->is_empty()) { throw std::out_of_range("Attempted to read empty queue!"); }
return m_container[idx_read];
}
/** Push an element into the end of the buffer**/
template <typename T, size_t CAPACITY>
void PacketBuffer<T, CAPACITY>::push_back(T item){
std::lock_guard<std::mutex> g_guard(m_mutex);
if(this->is_full()) { throw std::out_of_range("Attempted to write a full buffer!"); }
m_container[idx_write] = item;
idx_write++;
}
#endif // CIRCULAR_BUFFER_TEMPLATE_HPP