Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Primitive layout #22

Merged
merged 8 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
270 changes: 270 additions & 0 deletions include/sparrow/layout.hpp
Original file line number Diff line number Diff line change
@@ -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 <algorithm>
#include <concepts>
#include <cstdint>

#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 T, bool is_const>
class primitive_layout_iterator
jjerphan marked this conversation as resolved.
Show resolved Hide resolved
: public iterator_base
<
primitive_layout_iterator<T, is_const>,
mpl::constify_t<T, is_const>,
std::contiguous_iterator_tag
>
{
public:

using self_type = primitive_layout_iterator<T, is_const>;
using base_type = iterator_base
<
self_type,
mpl::constify_t<T, is_const>,
std::contiguous_iterator_tag
>;
using pointer = typename base_type::pointer;

primitive_layout_iterator() = default;
jjerphan marked this conversation as resolved.
Show resolved Hide resolved
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 T>
class primitive_layout
{
public:

using self_type = primitive_layout<T>;
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<T, false>;
using const_iterator = primitive_layout_iterator<T, true>;

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 <class T, bool is_const>
primitive_layout_iterator<T, is_const>::primitive_layout_iterator(pointer pointer)
: m_pointer(pointer)
{
}

template <class T, bool is_const>
auto primitive_layout_iterator<T, is_const>::dereference() const -> reference
{
return *m_pointer;
}

template <class T, bool is_const>
void primitive_layout_iterator<T, is_const>::increment()
{
++m_pointer;
}

template <class T, bool is_const>
void primitive_layout_iterator<T, is_const>::decrement()
{
--m_pointer;
}

template <class T, bool is_const>
void primitive_layout_iterator<T, is_const>::advance(difference_type n)
{
m_pointer += n;
}

template <class T, bool is_const>
auto primitive_layout_iterator<T, is_const>::distance_to(const self_type& rhs) const -> difference_type
{
return rhs.m_pointer - m_pointer;
}

template <class T, bool is_const>
bool primitive_layout_iterator<T, is_const>::equal(const self_type& rhs) const
{
return distance_to(rhs) == 0;
}

template <class T, bool is_const>
bool primitive_layout_iterator<T, is_const>::less_than(const self_type& rhs) const
{
return distance_to(rhs) > 0;
}

/***********************************
* primitive_layout implementation *
* ********************************/

template <class T>
primitive_layout<T>::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 <class T>
auto primitive_layout<T>::size() const -> size_type
{
assert(m_data.buffers.size() > 0);
return m_data.buffers[0].size();
}

template <class T>
auto primitive_layout<T>::element(size_type i) -> reference
{
assert(pos < size());
return i[data()];
}

template <class T>
auto primitive_layout<T>::element(size_type i) const -> const_reference
{
assert(pos < size());
return i[data()];
}

template <class T>
auto primitive_layout<T>::begin() -> iterator
{
return iterator{data()};
}

template <class T>
auto primitive_layout<T>::end() -> iterator
{
return iterator{data() + self_type::size()};
}

template <class T>
auto primitive_layout<T>::begin() const -> const_iterator
{
return cbegin();
}

template <class T>
auto primitive_layout<T>::end() const -> const_iterator
{
return cend();
}

template <class T>
auto primitive_layout<T>::cbegin() const -> const_iterator
{
return const_iterator{data()};
}

template <class T>
auto primitive_layout<T>::cend() const -> const_iterator
{
return const_iterator{data() + self_type::size()};
}

template <class T>
auto primitive_layout<T>::data() -> pointer
{
assert(m_data.buffers.size() > 0);
return m_data.buffers[0].data();
jjerphan marked this conversation as resolved.
Show resolved Hide resolved
}

template <class T>
auto primitive_layout<T>::data() const -> const_pointer
{
assert(m_data.buffers.size() > 0);
return m_data.buffers[0].data();
}

} // namespace sparrow


1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
85 changes: 85 additions & 0 deletions test/test_layout.cpp
Original file line number Diff line number Diff line change
@@ -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 <iostream>
#include <numeric>

#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<primitive_layout<uint8_t>>);
static_assert(std::contiguous_iterator<primitive_layout_iterator<uint8_t, true>>);
static_assert(std::contiguous_iterator<primitive_layout_iterator<uint8_t, false>>);

using layout_test_type = primitive_layout<uint8_t>;

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

}

jjerphan marked this conversation as resolved.
Show resolved Hide resolved
}
Loading