@@ -94,6 +94,7 @@ class run_length_sequence
9494public:
9595 static_assert (!std::is_const_v<T>);
9696 static_assert (unsigned_numeric_integral<IndexT> && !std::is_const_v<IndexT>);
97+ static_assert (sizeof (IndexT) >= sizeof (int ), " Small index type has no practical benefit" );
9798 static_assert (!std::is_const_v<RunContainerT>);
9899
99100 using size_type = std::common_type_t <std::size_t , IndexT>;
@@ -103,7 +104,7 @@ class run_length_sequence
103104 {
104105 constexpr auto a = std::numeric_limits<IndexT>::max ();
105106 constexpr auto b = std::numeric_limits<difference_type>::max ();
106- return std::cmp_less (b, a) ? b : a ;
107+ return std::cmp_less (b, a) ? static_cast <size_type>(b) : static_cast <size_type>(a) ;
107108 }
108109
109110private:
@@ -245,7 +246,7 @@ class run_length_sequence
245246
246247 [[nodiscard]] constexpr iterator begin () noexcept { check_range_concepts (); return iterator{std::ranges::begin (runs_), offsets_.begin ()}; }
247248 [[nodiscard]] constexpr const_iterator begin () const noexcept { check_range_concepts (); return const_iterator{std::ranges::begin (runs_), offsets_.begin ()}; }
248- [[nodiscard]] constexpr const_iterator cbegin () const noexcept { check_range_concepts (); return begin (); }
249+ [[nodiscard]] constexpr const_iterator cbegin () const noexcept { return begin (); }
249250
250251 [[nodiscard]] constexpr iterator end () noexcept
251252 {
@@ -269,14 +270,14 @@ class run_length_sequence
269270 return const_iterator{std::ranges::end (runs_), std::prev (offsets_.end ())};
270271 }
271272 }
272- [[nodiscard]] constexpr const_iterator cend () const noexcept { check_range_concepts (); return end (); }
273+ [[nodiscard]] constexpr const_iterator cend () const noexcept { return end (); }
273274
274- [[nodiscard]] constexpr reverse_iterator rbegin () noexcept { check_range_concepts (); return reverse_iterator{end ()}; }
275- [[nodiscard]] constexpr const_reverse_iterator rbegin () const noexcept { check_range_concepts (); return const_reverse_iterator{end ()}; }
276- [[nodiscard]] constexpr const_reverse_iterator crbegin () const noexcept { check_range_concepts (); return rbegin (); }
277- [[nodiscard]] constexpr reverse_iterator rend () noexcept { check_range_concepts (); return reverse_iterator{begin ()}; }
278- [[nodiscard]] constexpr const_reverse_iterator rend () const noexcept { check_range_concepts (); return const_reverse_iterator{begin ()}; }
279- [[nodiscard]] constexpr const_reverse_iterator crend () const noexcept { check_range_concepts (); return rend (); }
275+ [[nodiscard]] constexpr reverse_iterator rbegin () noexcept { return reverse_iterator{end ()}; }
276+ [[nodiscard]] constexpr const_reverse_iterator rbegin () const noexcept { return const_reverse_iterator{end ()}; }
277+ [[nodiscard]] constexpr const_reverse_iterator crbegin () const noexcept { return rbegin (); }
278+ [[nodiscard]] constexpr reverse_iterator rend () noexcept { return reverse_iterator{begin ()}; }
279+ [[nodiscard]] constexpr const_reverse_iterator rend () const noexcept { return const_reverse_iterator{begin ()}; }
280+ [[nodiscard]] constexpr const_reverse_iterator crend () const noexcept { return rend (); }
280281
281282 [[nodiscard]] constexpr bool empty () const noexcept
282283 {
@@ -294,7 +295,7 @@ class run_length_sequence
294295 return static_cast <size_type>(offsets_.back ());
295296 }
296297
297- [[nodiscard]] constexpr size_type segment_count () const noexcept
298+ [[nodiscard]] constexpr size_type run_count () const noexcept
298299 {
299300 static_assert (std::ranges::sized_range<RunContainerT>);
300301 return static_cast <size_type>(std::ranges::size (runs_));
@@ -310,43 +311,56 @@ class run_length_sequence
310311 }
311312
312313 template <class U >
313- requires std::is_constructible_v <T, U>
314+ requires std::constructible_from<T, U> && req::half_equality_comparable <T, U>
314315 constexpr reference emplace_back (U&& value) IRIS_LIFETIMEBOUND
315316 {
316317 check_range_concepts ();
317318 static_assert (std::equality_comparable<T>);
318-
319319 IRIS_ZZ_RUN_LENGTH_SEQUENCE_INVARIANT_GUARD
320320
321321 if (offsets_.empty ()) {
322- [[maybe_unused]] ofs_insertion_guard<true , true > ofs_insertion_guard{this };
323- offsets_.emplace_back (static_cast <IndexT>(0u ));
324- offsets_.emplace_back (static_cast <IndexT>(1u )); // sentinel
322+ return this ->emplace_back_on_empty (std::forward<U>(value));
323+
324+ } else {
325+ assert (!std::ranges::empty (runs_));
326+ assert (offsets_.size () >= 2 );
327+ if (offsets_.back () == max_size ()) {
328+ throwf<std::length_error>(" run_length_sequence capacity exceeded" );
329+ }
330+
331+ if (ranges::back (std::as_const (runs_)) == std::as_const (value)) {
332+ // Equivalent element already exists; no need to insert.
333+ return {offsets_.back ()++, ranges::back (runs_)};
334+ }
335+ // Need to insert new element
336+ auto const new_pos = offsets_.back ();
337+ offsets_.emplace_back (new_pos + static_cast <IndexT>(1u )); // new sentinel
338+ [[maybe_unused]] ofs_insertion_guard<true , false > ofs_insertion_guard{this };
325339 auto & elem = ranges::emplace_back_ref (runs_, std::forward<U>(value));
326340 ofs_insertion_guard.clear ();
327- return {static_cast <IndexT>(0u ), elem};
341+ return {new_pos, elem};
342+ }
343+ }
344+
345+ template <class ... Args>
346+ requires std::constructible_from<T, Args...>
347+ constexpr reference emplace_back (Args&&... args) IRIS_LIFETIMEBOUND
348+ {
349+ check_range_concepts ();
350+ static_assert (std::equality_comparable<T>);
351+ IRIS_ZZ_RUN_LENGTH_SEQUENCE_INVARIANT_GUARD
352+
353+ if (offsets_.empty ()) {
354+ return this ->emplace_back_on_empty (std::forward<Args>(args)...);
328355
329356 } else {
330357 assert (!std::ranges::empty (runs_));
331358 assert (offsets_.size () >= 2 );
332359 if (offsets_.back () == max_size ()) {
333360 throwf<std::length_error>(" run_length_sequence capacity exceeded" );
334361 }
335- if constexpr (req::half_equality_comparable<T, U>) {
336- if (ranges::back (std::as_const (runs_)) == std::as_const (value)) {
337- // Equivalent element already exists; no need to insert.
338- return {offsets_.back ()++, ranges::back (runs_)};
339- }
340- // Need to insert new element
341- auto const new_pos = offsets_.back ();
342- offsets_.emplace_back (new_pos + static_cast <IndexT>(1u )); // new sentinel
343- [[maybe_unused]] ofs_insertion_guard<true , false > ofs_insertion_guard{this };
344- auto & elem = ranges::emplace_back_ref (runs_, std::forward<U>(value));
345- ofs_insertion_guard.clear ();
346- return {new_pos, elem};
347-
348- } else if constexpr (requires { ranges::weak_pop_back (runs_); }) {
349- auto & elem = ranges::emplace_back_ref (runs_, std::forward<U>(value));
362+ if constexpr (requires { ranges::weak_pop_back (runs_); }) {
363+ auto & elem = ranges::emplace_back_ref (runs_, std::forward<Args>(args)...);
350364 [[maybe_unused]] elem_insertion_guard<true > elem_insertion_guard{this };
351365
352366 auto const prev_it = std::ranges::prev (std::ranges::end (runs_), 2 );
@@ -365,7 +379,7 @@ class run_length_sequence
365379 return {new_pos, elem};
366380
367381 } else {
368- T temp (std::forward<U>(value) );
382+ T temp (std::forward<Args>(args)... );
369383 if (ranges::back (std::as_const (runs_)) == std::as_const (temp)) {
370384 // Equivalent element already exists; no need to insert.
371385 return {offsets_.back ()++, ranges::back (runs_)};
@@ -382,7 +396,7 @@ class run_length_sequence
382396 }
383397
384398private:
385- constexpr void check_range_concepts () const noexcept
399+ static constexpr void check_range_concepts () noexcept
386400 {
387401 static_assert (std::ranges::bidirectional_range<RunContainerT>);
388402 static_assert (std::same_as<std::ranges::range_value_t <RunContainerT>, T>);
@@ -395,6 +409,18 @@ class run_length_sequence
395409 });
396410 }
397411
412+ template <class ... Args>
413+ constexpr reference emplace_back_on_empty (Args&&... args) IRIS_LIFETIMEBOUND
414+ {
415+ assert (this ->empty ());
416+ [[maybe_unused]] ofs_insertion_guard<true , true > ofs_insertion_guard{this };
417+ offsets_.emplace_back (static_cast <IndexT>(0u ));
418+ offsets_.emplace_back (static_cast <IndexT>(1u )); // sentinel
419+ auto & elem = ranges::emplace_back_ref (runs_, std::forward<Args>(args)...);
420+ ofs_insertion_guard.clear ();
421+ return {static_cast <IndexT>(0u ), elem};
422+ }
423+
398424 template <bool IsBack, bool WasEmpty>
399425 struct [[nodiscard]] ofs_insertion_guard
400426 {
@@ -464,15 +490,17 @@ class run_length_sequence
464490
465491 constexpr ~check_invariant_guard () noexcept
466492 {
467- if (self_->offsets_ .empty ()) {
468- assert (std::ranges::empty (self_->runs_ ));
469- } else {
470- assert (self_->offsets_ .size () == std::ranges::size (self_->runs_ ) + 1 );
471- assert (self_->offsets_ [0 ] == 0 );
472- for (std::size_t i = 0 ; i < self_->offsets_ .size () - 1 ; ++i) {
473- assert (self_->offsets_ [i] < self_->offsets_ [i + 1 ]);
474- }
493+ auto const & offsets = self_->offsets_ ;
494+ auto const & runs = self_->runs_ ;
495+ if (offsets.empty ()) {
496+ assert (std::ranges::empty (runs));
497+ return ;
475498 }
499+ assert (offsets.size () >= 2 );
500+ assert (offsets.size () == std::ranges::size (runs) + 1 );
501+ assert (offsets.front () == static_cast <IndexT>(0u ));
502+ auto const n = offsets.size ();
503+ assert (offsets[n - 2 ] < offsets[n - 1 ]);
476504 }
477505
478506 private:
0 commit comments