Skip to content

Commit

Permalink
core: Add Array template class to abstract a fixed-size contiguous …
Browse files Browse the repository at this point in the history
…buffer whose size is determined at runtime. (y-scope#513)
  • Loading branch information
LinZhihao-723 authored Aug 18, 2024
1 parent 1b67fdb commit 86299ca
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 0 deletions.
2 changes: 2 additions & 0 deletions components/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ set(SOURCE_FILES_clp_s_unitTest
)

set(SOURCE_FILES_unitTest
src/clp/Array.hpp
src/clp/BufferedFileReader.cpp
src/clp/BufferedFileReader.hpp
src/clp/BufferReader.cpp
Expand Down Expand Up @@ -474,6 +475,7 @@ set(SOURCE_FILES_unitTest
submodules/sqlite3/sqlite3.h
submodules/sqlite3/sqlite3ext.h
tests/LogSuppressor.hpp
tests/test-Array.cpp
tests/test-BufferedFileReader.cpp
tests/test-EncodedVariableInterpreter.cpp
tests/test-encoding_methods.cpp
Expand Down
111 changes: 111 additions & 0 deletions components/core/src/clp/Array.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#ifndef CLP_ARRAY_HPP
#define CLP_ARRAY_HPP

#include <concepts>
#include <cstddef>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <type_traits>

namespace clp {
/**
* Class for a runtime fix-sized array.
* @tparam T The type of elements in the array. The type must be default initializable so that this
* class doesn't need to implement a constructor which takes an initializer list.
*/
template <typename T>
requires(std::is_fundamental_v<T> || std::default_initializable<T>)
class Array {
public:
// Types
using Iterator = T*;
using ConstIterator = T const*;

// Constructors
// NOLINTNEXTLINE(*-avoid-c-arrays)
explicit Array(size_t size) : m_data{std::make_unique<T[]>(size)}, m_size{size} {
if constexpr (std::is_fundamental_v<T>) {
memset(m_data.get(), 0, m_size * sizeof(T));
}
}

// Disable copy constructor and assignment operator
Array(Array const&) = delete;
auto operator=(Array const&) -> Array& = delete;

// Default move constructor and assignment operator
Array(Array&&) = default;
auto operator=(Array&&) -> Array& = default;

// Destructor
~Array() = default;

// Methods
/**
* @return Whether the array is empty.
*/
[[nodiscard]] auto empty() const -> bool { return 0 == size(); }

/**
* @return The size of the array.
*/
[[nodiscard]] auto size() const -> size_t { return m_size; }

/**
* @return A pointer to the underlying data buffer.
*/
[[nodiscard]] auto data() -> T* { return m_data.get(); }

/**
* @return A pointer to the underlying data buffer.
*/
[[nodiscard]] auto data() const -> T const* { return m_data.get(); }

/**
* @param idx
* @return The element at the given index.
* @throw `OperationFailed` if the given index is out of bound.
*/
[[nodiscard]] auto at(size_t idx) -> T& {
assert_is_in_range(idx);
return m_data[idx];
}

/**
* @param idx
* @return The element at the given index.
* @throw `OperationFailed` if the given index is out of bound.
*/
[[nodiscard]] auto at(size_t idx) const -> T const& {
assert_is_in_range(idx);
return m_data[idx];
}

[[nodiscard]] auto begin() -> Iterator { return m_data.get(); }

[[nodiscard]] auto end() -> Iterator { return m_data.get() + m_size; }

[[nodiscard]] auto begin() const -> ConstIterator { return m_data.get(); }

[[nodiscard]] auto end() const -> ConstIterator { return m_data.get() + m_size; }

private:
/**
* @param idx
* @throw `std::out_of_range` if the given index is out of bound.
*/
auto assert_is_in_range(size_t idx) -> void {
if (idx >= m_size) {
throw std::out_of_range("clp::Array out-of-range access.");
}
}

// Variables
// NOLINTNEXTLINE(*-avoid-c-arrays)
std::unique_ptr<T[]> m_data;
size_t m_size;
};
} // namespace clp

#endif // CLP_ARRAY_HPP
57 changes: 57 additions & 0 deletions components/core/tests/test-Array.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <algorithm>
#include <cstddef>
#include <string>
#include <vector>

#include <Catch2/single_include/catch2/catch.hpp>

#include "../src/clp/Array.hpp"

using clp::Array;
using std::vector;

// NOLINTNEXTLINE(readability-function-cognitive-complexity)
TEST_CASE("array_fundamental", "[clp::Array]") {
Array<int> clp_array_empty{0};
REQUIRE(clp_array_empty.empty());
// NOLINTNEXTLINE(readability-container-size-empty)
REQUIRE((0 == clp_array_empty.size()));
REQUIRE((clp_array_empty.begin() == clp_array_empty.end()));

constexpr size_t cBufferSize{1024};

vector<int> std_vector;
for (int i{0}; i < cBufferSize; ++i) {
std_vector.push_back(i);
}

Array<int> clp_array{cBufferSize};
auto const& clp_array_const_ref = clp_array;
std::for_each(clp_array_const_ref.begin(), clp_array_const_ref.end(), [](int i) -> void {
REQUIRE((0 == i));
});

std::copy(std_vector.cbegin(), std_vector.cend(), clp_array.begin());

size_t idx{0};
for (auto const val : clp_array) {
REQUIRE((val == clp_array.at(idx)));
REQUIRE((val == std_vector.at(idx)));
++idx;
}
REQUIRE((cBufferSize == idx));
REQUIRE_THROWS(clp_array.at(idx));
}

TEST_CASE("array_default_initializable", "[clp::Array]") {
Array<std::string> clp_array_empty{0};
REQUIRE(clp_array_empty.empty());
// NOLINTNEXTLINE(readability-container-size-empty)
REQUIRE((0 == clp_array_empty.size()));
REQUIRE((clp_array_empty.begin() == clp_array_empty.end()));

vector<std::string> const std_vector{"yscope", "clp", "clp::Array", "default_initializable"};
Array<std::string> clp_array{std_vector.size()};
std::copy(std_vector.cbegin(), std_vector.cend(), clp_array.begin());
REQUIRE(std::equal(std_vector.begin(), std_vector.end(), clp_array.begin(), clp_array.end()));
}

0 comments on commit 86299ca

Please sign in to comment.