From f41970666675f873d7c1075efd192f22df8d17fe Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Sun, 22 Jun 2025 14:46:53 +0100 Subject: add nihil.ucl (incomplete) --- nihil.ucl/array.ccm | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 nihil.ucl/array.ccm (limited to 'nihil.ucl/array.ccm') 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 +#include +#include +#include + +#include + +export module nihil.ucl:array; + +import :object; + +namespace nihil::ucl { + +export template +struct array; + +template +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; + + ::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 +auto operator+(array_iterator const &lhs, + typename array_iterator::difference_type rhs) + -> array_iterator +{ + auto copy = lhs; + copy += rhs; + return copy; +} + +export template +auto operator+(typename array_iterator::difference_type lhs, + array_iterator const &rhs) + -> array_iterator +{ + return rhs - lhs; +} + +export template +auto operator-(array_iterator const &lhs, + typename array_iterator::difference_type rhs) + -> array_iterator +{ + auto copy = lhs; + copy -= rhs; + return copy; +} + +static_assert(std::random_access_iterator>); + +export template +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 + requires(std::convertible_to, 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 + requires(std::convertible_to, 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 const &list) + : array(std::ranges::begin(list), + std::ranges::end(list)) + { + } + + /* + * Array iterator access. + */ + + auto begin(this array const &self) -> array_iterator + { + return {self.get_ucl_object(), 0}; + } + + auto end(this array const &self) -> array_iterator + { + 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 +auto operator==(array const &a, array const &b) -> bool +{ + if (a.size() != b.size()) + return false; + + for (typename array::size_type i = 0; i < a.size(); ++i) + if (a.at(i) != b.at(i)) + return false; + + return true; +} + +} // namespace nihil::ucl -- cgit v1.2.3