diff --git a/include/sparrow/layout.hpp b/include/sparrow/layout.hpp new file mode 100644 index 00000000..68af7cca --- /dev/null +++ b/include/sparrow/layout.hpp @@ -0,0 +1,270 @@ +// Copyright 2024 Man Group Operations Limited +// +// 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 mplied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +#include "sparrow/array_data.hpp" +#include "sparrow/iterator.hpp" +#include "sparrow/buffer.hpp" + +namespace sparrow +{ + /** + * An iterator for `primitive_layout` operating on contiguous data only. + * + * @tparam T The type of the elements in the layout's data buffer. + * @tparam is_const A boolean indicating whether the iterator is const. + * + * @note This class is not thread-safe, exception-safe, copyable, movable, equality comparable. + */ + template + class primitive_layout_iterator + : public iterator_base + < + primitive_layout_iterator, + mpl::constify_t, + std::contiguous_iterator_tag + > + { + public: + + using self_type = primitive_layout_iterator; + using base_type = iterator_base + < + self_type, + mpl::constify_t, + std::contiguous_iterator_tag + >; + using pointer = typename base_type::pointer; + + primitive_layout_iterator() = default; + explicit primitive_layout_iterator(pointer p); + + private: + + // TODO: use reference proxies with an API similar to `std::optional` instead. + using reference = typename base_type::reference; + using difference_type = typename base_type::difference_type; + using size_type = std::size_t; + + reference dereference() const; + void increment(); + void decrement(); + void advance(difference_type n); + difference_type distance_to(const self_type& rhs) const; + bool equal(const self_type& rhs) const; + bool less_than(const self_type& rhs) const; + + pointer m_pointer = nullptr; + + friend class iterator_access; + }; + + /** + * A contiguous layout for primitive types. + * + * This class provides a contiguous layout for primitive types, such as `uint8_t`, `int32_t`, etc. + * It iterates over the first buffer in the array_data, and uses the bitmap to skip over null. + * The bitmap is assumed to be present in the array_data. + * + * @tparam T The type of the elements in the layout's data buffer. + * A primitive type or a fixed size type. + * + * @note This class is not thread-safe, exception-safe, copyable, movable, equality comparable. + */ + template + class primitive_layout + { + public: + + using self_type = primitive_layout; + using value_type = T; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + explicit primitive_layout(array_data p); + + using iterator = primitive_layout_iterator; + using const_iterator = primitive_layout_iterator; + + size_type size() const; + reference element(size_type i); + const_reference element(size_type i) const; + + iterator begin(); + iterator end(); + + const_iterator begin() const; + const_iterator end() const; + + const_iterator cbegin() const; + const_iterator cend() const ; + + private: + // We only use the first buffer and the bitmap. + array_data m_data; + + pointer data(); + const_pointer data() const; + + }; + + /******************************************** + * primitive_layout_iterator implementation * + *******************************************/ + + template + primitive_layout_iterator::primitive_layout_iterator(pointer pointer) + : m_pointer(pointer) + { + } + + template + auto primitive_layout_iterator::dereference() const -> reference + { + return *m_pointer; + } + + template + void primitive_layout_iterator::increment() + { + ++m_pointer; + } + + template + void primitive_layout_iterator::decrement() + { + --m_pointer; + } + + template + void primitive_layout_iterator::advance(difference_type n) + { + m_pointer += n; + } + + template + auto primitive_layout_iterator::distance_to(const self_type& rhs) const -> difference_type + { + return rhs.m_pointer - m_pointer; + } + + template + bool primitive_layout_iterator::equal(const self_type& rhs) const + { + return distance_to(rhs) == 0; + } + + template + bool primitive_layout_iterator::less_than(const self_type& rhs) const + { + return distance_to(rhs) > 0; + } + + /*********************************** + * primitive_layout implementation * + * ********************************/ + + template + primitive_layout::primitive_layout(array_data ad) + : m_data(ad) + { + // We only require the presence of the bitmap and the first buffer. + assert(m_data.buffers.size() > 0); + assert(m_data.length == m_data.buffers[0].size()); + assert(m_data.length == m_data.bitmap.size()); + } + + template + auto primitive_layout::size() const -> size_type + { + assert(m_data.buffers.size() > 0); + return m_data.buffers[0].size(); + } + + template + auto primitive_layout::element(size_type i) -> reference + { + assert(pos < size()); + return i[data()]; + } + + template + auto primitive_layout::element(size_type i) const -> const_reference + { + assert(pos < size()); + return i[data()]; + } + + template + auto primitive_layout::begin() -> iterator + { + return iterator{data()}; + } + + template + auto primitive_layout::end() -> iterator + { + return iterator{data() + self_type::size()}; + } + + template + auto primitive_layout::begin() const -> const_iterator + { + return cbegin(); + } + + template + auto primitive_layout::end() const -> const_iterator + { + return cend(); + } + + template + auto primitive_layout::cbegin() const -> const_iterator + { + return const_iterator{data()}; + } + + template + auto primitive_layout::cend() const -> const_iterator + { + return const_iterator{data() + self_type::size()}; + } + + template + auto primitive_layout::data() -> pointer + { + assert(m_data.buffers.size() > 0); + return m_data.buffers[0].data(); + } + + template + auto primitive_layout::data() const -> const_pointer + { + assert(m_data.buffers.size() > 0); + return m_data.buffers[0].data(); + } + +} // namespace sparrow + + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 62d3658e..e91174a8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -40,6 +40,7 @@ set(SPARROW_TESTS test_buffer.cpp test_dynamic_bitset.cpp test_iterator.cpp + test_layout.cpp ) set(test_target "test_sparrow_lib") add_executable(${test_target} ${SPARROW_TESTS}) diff --git a/test/test_layout.cpp b/test/test_layout.cpp new file mode 100644 index 00000000..a8ed8406 --- /dev/null +++ b/test/test_layout.cpp @@ -0,0 +1,85 @@ +// Copyright 2024 Man Group Operations Limited +// +// 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. +#include +#include + +#include "doctest/doctest.h" + +#include "sparrow/layout.hpp" + +namespace sparrow +{ + // TODO: Test all the other base types once #15 is addressed. + static_assert(std::ranges::contiguous_range>); + static_assert(std::contiguous_iterator>); + static_assert(std::contiguous_iterator>); + + using layout_test_type = primitive_layout; + + namespace + { + array_data make_test_array_data() + { + size_t n = 10; + array_data ad; + + ad.type = data_descriptor(data_type::UINT8); + ad.bitmap = dynamic_bitset(n); + buffer b(n); + std::iota(b.begin(), b.end(), 0); + + ad.buffers.push_back(b); + ad.length = n; + ad.offset = 0; + ad.child_data.push_back(array_data()); + return ad; + } + + } + + TEST_SUITE("primitive_layout") + { + TEST_CASE("constructors") + { + array_data ad = make_test_array_data(); + layout_test_type lt(ad); + REQUIRE(lt.size() == ad.length); + } + + TEST_CASE("iterator_ordering") + { + layout_test_type lt(make_test_array_data()); + layout_test_type::iterator it = lt.begin(); + layout_test_type::const_iterator cit = lt.cbegin(); + REQUIRE(it < lt.end()); + REQUIRE(cit < lt.cend()); + } + + TEST_CASE("iterator_equality") + { + layout_test_type lt(make_test_array_data()); + auto iter = lt.begin(); + auto citer = lt.cbegin(); + for (std::size_t i = 0; i < lt.size(); ++i) + { + CHECK_EQ(*iter++, lt.element(i)); + CHECK_EQ(*citer++, lt.element(i)); + } + CHECK_EQ(iter, lt.end()); + CHECK_EQ(citer, lt.cend()); + } + + } + +}