aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl/array.ccm
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.ucl/array.ccm')
-rw-r--r--nihil.ucl/array.ccm342
1 files changed, 342 insertions, 0 deletions
diff --git a/nihil.ucl/array.ccm b/nihil.ucl/array.ccm
new file mode 100644
index 0000000..26cd9b9
--- /dev/null
+++ b/nihil.ucl/array.ccm
@@ -0,0 +1,342 @@
+/*
+ * This source code is released into the public domain.
+ */
+
+module;
+
+#include <cassert>
+#include <cstdint>
+#include <cstdlib>
+#include <string>
+
+#include <ucl.h>
+
+export module nihil.ucl:array;
+
+import :object;
+
+namespace nihil::ucl {
+
+export template<datatype T>
+struct array;
+
+template<datatype T>
+struct array_iterator {
+ using difference_type = std::ptrdiff_t;
+ using value_type = T;
+ using reference = T&;
+ using pointer = T*;
+
+ array_iterator() = default;
+
+ auto operator* (this array_iterator const &self) -> T
+ {
+ auto uobj = ::ucl_array_find_index(self._array, self._idx);
+ if (uobj == nullptr)
+ throw error("failed to fetch UCL array index");
+
+ return T(::ucl_object_ref(uobj));
+ }
+
+ auto operator[] (this array_iterator const &self,
+ difference_type idx)
+ -> T
+ {
+ return *(self + idx);
+ }
+
+ auto operator++ (this array_iterator &self) -> array_iterator&
+ {
+ ++self._idx;
+ return self;
+ }
+
+ auto operator++ (this array_iterator &self, int) -> array_iterator
+ {
+ auto copy = self;
+ ++self;
+ return copy;
+ }
+
+ auto operator-- (this array_iterator &self) -> array_iterator&
+ {
+ if (self._idx == 0)
+ throw std::out_of_range("attempt to iterate before "
+ "start of UCL array");
+ --self._idx;
+ return self;
+ }
+
+ auto operator-- (this array_iterator &self, int) -> array_iterator
+ {
+ auto copy = self;
+ --self;
+ return copy;
+ }
+
+ auto operator== (this array_iterator const &lhs,
+ array_iterator const &rhs)
+ -> bool
+ {
+ return lhs._idx == rhs._idx;
+ }
+
+ auto operator<=> (this array_iterator const &lhs,
+ array_iterator const &rhs)
+ {
+ return lhs._idx <=> rhs._idx;
+ }
+
+ auto operator+= (this array_iterator &lhs,
+ difference_type rhs)
+ -> array_iterator &
+ {
+ lhs._idx += rhs;
+ return lhs;
+ }
+
+ auto operator-= (this array_iterator &lhs,
+ difference_type rhs)
+ -> array_iterator &
+ {
+ lhs._idx -= rhs;
+ return lhs;
+ }
+
+ auto operator- (this array_iterator const &lhs,
+ array_iterator const &rhs)
+ -> difference_type
+ {
+ return lhs._idx - rhs._idx;
+ }
+
+private:
+ friend struct array<T>;
+
+ ::ucl_object_t const *_array{};
+ std::size_t _idx{};
+
+ array_iterator(::ucl_object_t const *array, std::size_t idx)
+ : _array(array)
+ , _idx(idx)
+ {}
+};
+
+export template<datatype T>
+auto operator+(array_iterator<T> const &lhs,
+ typename array_iterator<T>::difference_type rhs)
+ -> array_iterator<T>
+{
+ auto copy = lhs;
+ copy += rhs;
+ return copy;
+}
+
+export template<datatype T>
+auto operator+(typename array_iterator<T>::difference_type lhs,
+ array_iterator<T> const &rhs)
+ -> array_iterator<T>
+{
+ return rhs - lhs;
+}
+
+export template<datatype T>
+auto operator-(array_iterator<T> const &lhs,
+ typename array_iterator<T>::difference_type rhs)
+ -> array_iterator<T>
+{
+ auto copy = lhs;
+ copy -= rhs;
+ return copy;
+}
+
+static_assert(std::random_access_iterator<array_iterator<object>>);
+
+export template<datatype T>
+struct array final : object {
+ inline static constexpr object_type ucl_type = object_type::array;
+
+ using value_type = T;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+
+ array() : object(::ucl_object_typed_new(UCL_ARRAY))
+ {
+ if (_object == nullptr)
+ throw error("failed to create UCL object");
+ }
+
+ explicit array(::ucl_object_t *uobj) : object(uobj)
+ {
+ assert(type() == object_type::array);
+ }
+
+ /*
+ * Create an array from an iterator pair.
+ */
+ template<std::input_iterator Iterator>
+ requires(std::convertible_to<std::iter_value_t<Iterator>, T>)
+ array(Iterator first, Iterator last)
+ : object(::ucl_object_typed_new(UCL_ARRAY))
+ {
+ if (_object == nullptr)
+ throw error("failed to create UCL object");
+
+ // This is exception safe, because if we throw here the
+ // base class destructor will free the array.
+ while (first != last) {
+ push_back(*first);
+ ++first;
+ }
+ }
+
+ /*
+ * Create an array from a range.
+ */
+ template<std::ranges::range Range>
+ requires(std::convertible_to<std::ranges::range_value_t<Range>, T>)
+ array(std::from_range_t, Range &&range)
+ : array(std::ranges::begin(range),
+ std::ranges::end(range))
+ {
+ }
+
+ /*
+ * Create an array from an initializer_list.
+ */
+ array(std::initializer_list<T> const &list)
+ : array(std::ranges::begin(list),
+ std::ranges::end(list))
+ {
+ }
+
+ /*
+ * Array iterator access.
+ */
+
+ auto begin(this array const &self) -> array_iterator<T>
+ {
+ return {self.get_ucl_object(), 0};
+ }
+
+ auto end(this array const &self) -> array_iterator<T>
+ {
+ return {self.get_ucl_object(), self.size()};
+ }
+
+ /*
+ * Return the size of this array.
+ */
+ auto size(this array const &self) -> size_type
+ {
+ return ::ucl_array_size(self.get_ucl_object());
+ }
+
+ /*
+ * Test if this array is empty.
+ */
+ auto empty(this array const &self) -> bool
+ {
+ return self.size() == 0;
+ }
+
+ /*
+ * Reserve space for future insertions.
+ */
+ auto reserve(this array &self, size_type nelems) -> void
+ {
+ ::ucl_object_reserve(self.get_ucl_object(), nelems);
+ }
+
+ /*
+ * Append an element to the array.
+ */
+ auto push_back(this array &self, value_type &&v) -> void
+ {
+ // There's no real benefit to moving the object here, but
+ // move it anyway to preserve the expected semantics.
+ auto copy = std::move(v);
+ self.push_back(copy);
+ }
+
+ auto push_back(this array &self, value_type const &v) -> void
+ {
+ auto uobj = ::ucl_object_ref(v.get_ucl_object());
+ ::ucl_array_append(self.get_ucl_object(), uobj);
+ }
+
+ /*
+ * Prepend an element to the array.
+ */
+ auto push_front(this array &self, value_type &&v) -> void
+ {
+ // There's no real benefit to moving the object here, but
+ // move it anyway to preserve the expected semantics.
+ auto copy = std::move(v);
+ self.push_front(copy);
+ }
+
+ auto push_front(this array &self, value_type const &v) -> void
+ {
+ auto uobj = ::ucl_object_ref(v.get_ucl_object());
+ ::ucl_array_prepend(self.get_ucl_object(), uobj);
+ }
+
+ /*
+ * Access an array element by index.
+ */
+ auto at(this array const &self, size_type idx) -> T
+ {
+ if (idx >= self.size())
+ throw std::out_of_range("UCL array index out of range");
+
+ auto uobj = ::ucl_array_find_index(self.get_ucl_object(), idx);
+ if (uobj == nullptr)
+ throw error("failed to fetch UCL array index");
+
+ return T(::ucl_object_ref(uobj));
+ }
+
+ auto operator[] (this array const &self, size_type idx) -> T
+ {
+ return self.at(idx);
+ }
+
+ /*
+ * Return the first element.
+ */
+ auto front(this array const &self) -> T
+ {
+ return self.at(0);
+ }
+
+ /*
+ * Return the last element.
+ */
+ auto back(this array const &self) -> T
+ {
+ if (self.empty())
+ throw std::out_of_range("attempt to access back() on "
+ "empty UCL array");
+ return self.at(self.size() - 1);
+ }
+};
+
+/*
+ * Comparison operators.
+ */
+
+export template<datatype T>
+auto operator==(array<T> const &a, array<T> const &b) -> bool
+{
+ if (a.size() != b.size())
+ return false;
+
+ for (typename array<T>::size_type i = 0; i < a.size(); ++i)
+ if (a.at(i) != b.at(i))
+ return false;
+
+ return true;
+}
+
+} // namespace nihil::ucl