diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 1af676f..cbdfad8 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -17,9 +17,11 @@ endif() if(AARE_TESTS) set(TestSources - ${CMAKE_CURRENT_SOURCE_DIR}/src/defs.test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/ProducerConsumerQueue.test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/CircularFifo.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test/defs.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test/ProducerConsumerQueue.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test/CircularFifo.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test/wrappers.test.cpp + ) target_sources(tests PRIVATE ${TestSources} ) target_link_libraries(tests PRIVATE core) diff --git a/core/include/aare/DataSpan.hpp b/core/include/aare/DataSpan.hpp new file mode 100644 index 0000000..7df8edc --- /dev/null +++ b/core/include/aare/DataSpan.hpp @@ -0,0 +1,125 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + + +template ssize_t element_offset(const Strides &) { return 0; } + +template +ssize_t element_offset(const Strides &strides, ssize_t i, Ix... index) { + return i * strides[Dim] + element_offset(strides, index...); +} + +template std::array c_strides(const std::array &shape) { + std::array strides; + std::fill(strides.begin(), strides.end(), 1); + for (ssize_t i = Ndim - 1; i > 0; --i) { + strides[i - 1] = strides[i] * shape[i]; + } + return strides; +} + +template std::array make_array(const std::vector &vec) { + assert(vec.size() == Ndim); + std::array arr; + std::copy_n(vec.begin(), Ndim, arr.begin()); + return arr; +} + +template class DataSpan { + public: + DataSpan(){}; + + DataSpan(T* buffer, std::array shape) { + buffer_ = buffer; + strides_ = c_strides(shape); + shape_ = shape; + size_ = std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies()); + } + + DataSpan(T *buffer, const std::vector &shape) { + buffer_ = buffer; + strides_ = c_strides(make_array(shape)); + shape_ = make_array(shape); + size_ = std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies()); + + } + + DataSpan(Frame& frame) { + assert(Ndim == 2); + buffer_ = reinterpret_cast(frame._get_data()); + strides_ = c_strides({frame.rows(), frame.cols()}); + shape_ = {frame.rows(), frame.cols()}; + size_ = frame.rows() * frame.cols(); + } + + template typename std::enable_if::type operator()(Ix... index) { + return buffer_[element_offset(strides_, index...)]; + } + + template typename std::enable_if::type operator()(Ix... index) const { + return buffer_[element_offset(strides_, index...)]; + } + + ssize_t size() const { return size_; } + + DataSpan(const DataSpan &) = default; + DataSpan(DataSpan &&) = default; + + T *begin() { return buffer_; } + T *end() { return buffer_ + size_; } + T &operator()(ssize_t i) { return buffer_[i]; } + T &operator[](ssize_t i) { return buffer_[i]; } + + DataSpan &operator+=(const T val) { return elemenwise(val, std::plus()); } + DataSpan &operator-=(const T val) { return elemenwise(val, std::minus()); } + DataSpan &operator*=(const T val) { return elemenwise(val, std::multiplies()); } + DataSpan &operator/=(const T val) { return elemenwise(val, std::divides()); } + + DataSpan &operator/=(const DataSpan &other) { return elemenwise(other, std::divides()); } + + DataSpan &operator=(const T val) { + for (auto it = begin(); it != end(); ++it) + *it = val; + return *this; + } + + DataSpan &operator=(const DataSpan &other) { + shape_ = other.shape_; + strides_ = other.strides_; + size_ = other.size_; + buffer_ = other.buffer_; + return *this; + } + auto &shape() { return shape_; } + auto shape(ssize_t i) const { return shape_[i]; } + + T *data() { return buffer_; } + + private: + T *buffer_{nullptr}; + std::array strides_{}; + std::array shape_{}; + ssize_t size_{}; + + template DataSpan &elemenwise(T val, BinaryOperation op) { + for (ssize_t i = 0; i != size_; ++i) { + buffer_[i] = op(buffer_[i], val); + } + return *this; + } + template DataSpan &elemenwise(const DataSpan &other, BinaryOperation op) { + for (ssize_t i = 0; i != size_; ++i) { + buffer_[i] = op(buffer_[i], other.buffer_[i]); + } + return *this; + } +}; + +template class DataSpan; + diff --git a/core/include/aare/Frame.hpp b/core/include/aare/Frame.hpp index abf9150..bf33b07 100644 --- a/core/include/aare/Frame.hpp +++ b/core/include/aare/Frame.hpp @@ -23,6 +23,8 @@ class Frame { Frame(ssize_t rows, ssize_t cols,ssize_t m_bitdepth); Frame(std::byte *fp, ssize_t rows, ssize_t cols,ssize_t m_bitdepth); std::byte* get(int row, int col); + template + void set(int row, int col,T data); // std::vector> get_array(); ssize_t rows() const{ return m_rows; diff --git a/core/src/Frame.cpp b/core/src/Frame.cpp index 7974b5c..7a9e53a 100644 --- a/core/src/Frame.cpp +++ b/core/src/Frame.cpp @@ -1,6 +1,6 @@ #include "aare/Frame.hpp" #include - +#include Frame::Frame(std::byte* bytes, ssize_t rows, ssize_t cols, ssize_t bitdepth): m_rows(rows), m_cols(cols), m_bitdepth(bitdepth) { m_data = new std::byte[rows*cols*bitdepth/8]; @@ -22,6 +22,19 @@ std::byte* Frame::get(int row, int col) { return m_data+(row*m_cols + col)*(m_bitdepth/8); } +template +void Frame::set(int row, int col, T data) { + assert(sizeof(T) == m_bitdepth/8); + if (row < 0 || row >= m_rows || col < 0 || col >= m_cols) { + std::cerr << "Invalid row or column index" << std::endl; + return; + } + std::memcpy(m_data+(row*m_cols + col)*(m_bitdepth/8), &data, m_bitdepth/8); +} + +template void Frame::set(int row, int col, uint16_t data); + + // std::vector> Frame::get_array() { // std::vector> array; // for (int i = 0; i < m_rows; i++) { diff --git a/core/src/CircularFifo.test.cpp b/core/test/CircularFifo.test.cpp similarity index 100% rename from core/src/CircularFifo.test.cpp rename to core/test/CircularFifo.test.cpp diff --git a/core/src/ProducerConsumerQueue.test.cpp b/core/test/ProducerConsumerQueue.test.cpp similarity index 100% rename from core/src/ProducerConsumerQueue.test.cpp rename to core/test/ProducerConsumerQueue.test.cpp diff --git a/core/src/defs.test.cpp b/core/test/defs.test.cpp similarity index 100% rename from core/src/defs.test.cpp rename to core/test/defs.test.cpp diff --git a/core/test/wrappers.test.cpp b/core/test/wrappers.test.cpp new file mode 100644 index 0000000..fc29af2 --- /dev/null +++ b/core/test/wrappers.test.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +TEST_CASE("Frame") { + auto data = new uint16_t[100]; + for (int i = 0; i < 100; i++) { + data[i] = i; + } + Frame f(reinterpret_cast(data), 10, 10, 16); + for (int i = 0; i < 100; i++) { + REQUIRE((uint16_t)*f.get(i / 10, i % 10) == data[i]); + } + REQUIRE(f.rows() == 10); + REQUIRE(f.cols() == 10); + REQUIRE(f.bitdepth() == 16); + + uint16_t i = 44; + f.set(0, 0, i); + REQUIRE((uint16_t)*f.get(0, 0) == i); + delete[] data; +} + +TEST_CASE("DataSpan") { + auto data = new uint16_t[100]; + for (int i = 0; i < 100; i++) { + data[i] = i; + } + SECTION("constructors") { + DataSpan ds(data, std::vector({10, 10})); + for (int i = 0; i < 100; i++) { + REQUIRE(ds(i / 10, i % 10) == data[i]); + } + } + SECTION("from Frame") { + Frame f(reinterpret_cast(data), 10, 10, 16); + DataSpan ds(f); + for (int i = 0; i < 100; i++) { + REQUIRE(ds(i / 10, i % 10) == data[i]); + } + + f.set(0, 0, (uint16_t)44); + REQUIRE((uint16_t)*f.get(0, 0) == 44); // check that set worked + REQUIRE(ds(0, 0) == 44);// check that ds is updated + REQUIRE(data[0] == 0); // check that data is not updated + } + delete[] data; +} \ No newline at end of file