diff --git a/src/hb-array.hh b/src/hb-array.hh index b4619ee9a..37ca63d35 100644 --- a/src/hb-array.hh +++ b/src/hb-array.hh @@ -80,6 +80,8 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> length -= n; } unsigned __len__ () const { return length; } + bool operator != (const hb_array_t& o) const + { return arrayZ != o.arrayZ || length != o.length; } /* Extra operators. */ @@ -224,6 +226,10 @@ struct hb_sorted_array_t : hb_sorted_array_t& operator = (const hb_array_t &o) { hb_array_t (*this) = o; return *this; } + /* Iterator implementation. */ + bool operator != (const hb_sorted_array_t& o) const + { return this->arrayZ != o.arrayZ || this->length != o.length; } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); } hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const diff --git a/src/hb-iter.hh b/src/hb-iter.hh index 6fe984fc6..3240fc37a 100644 --- a/src/hb-iter.hh +++ b/src/hb-iter.hh @@ -42,6 +42,17 @@ * copied by value. If the collection / object being iterated on * is writable, then the iterator returns lvalues, otherwise it * returns rvalues. + * + * TODO Document more. + * + * If iterator implementation implements operator!=, then can be + * used in range-based for loop. That comes free if the iterator + * is random-access. Otherwise, the range-based for loop incurs + * one traversal to find end(), which can be avoided if written + * as a while-style for loop, or if iterator implements a faster + * __end__() method. + * TODO When opting in for C++17, address this by changing return + * type of .end()? */ @@ -72,10 +83,13 @@ struct hb_iter_t /* Operators. */ iter_t iter () const { return *thiz(); } iter_t operator + () const { return *thiz(); } + iter_t begin () const { return *thiz(); } + iter_t end () const { return thiz()->__end__ (); } explicit operator bool () const { return thiz()->__more__ (); } unsigned len () const { return thiz()->__len__ (); } /* The following can only be enabled if item_t is reference type. Otherwise - * it will be returning pointer to temporary rvalue. */ + * it will be returning pointer to temporary rvalue. + * TODO Use a wrapper return type to fix for non-reference type. */ template hb_remove_reference* operator -> () const { return hb_addressof (**thiz()); } @@ -107,6 +121,8 @@ struct hb_iter_t #define HB_ITER_USING(Name) \ using item_t = typename Name::item_t; \ + using Name::begin; \ + using Name::end; \ using Name::item_size; \ using Name::is_iterator; \ using Name::iter; \ @@ -150,7 +166,6 @@ struct } HB_FUNCOBJ (hb_iter); - /* Mixin to fill in what the subclass doesn't provide. */ template struct hb_iter_fallback_mixin_t @@ -178,6 +193,18 @@ struct hb_iter_fallback_mixin_t void __prev__ () { *thiz() -= 1; } void __rewind__ (unsigned n) { while (n--) --*thiz(); } + /* Range-based for: Implement __end__() if can be done faster, + * and operator!=. */ + iter_t __end__ () const + { + if (thiz()->is_random_access_iterator) + return *thiz() + thiz()->len (); + /* Above expression loops twice. Following loops once. */ + auto it = *thiz(); + while (it) ++it; + return it; + } + protected: hb_iter_fallback_mixin_t () {} hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) {} @@ -250,6 +277,31 @@ struct hb_is_iterator_of { enum { hb_is_sorted_iterator_of (Iter, typename Iter::item_t) +/* Range-based 'for' for iterables. */ + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +/* begin()/end() are NOT looked up non-ADL. So each namespace must declare them. + * Do it for namespace OT. */ +namespace OT { + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +} + + /* * Adaptors, combiners, etc. */ @@ -279,6 +331,8 @@ struct hb_map_iter_t : void __forward__ (unsigned n) { it += n; } void __prev__ () { --it; } void __rewind__ (unsigned n) { it -= n; } + bool operator != (const hb_map_iter_t& o) const + { return it != o.it || f != o.f; } private: Iter it; @@ -322,6 +376,8 @@ struct hb_filter_iter_t : bool __more__ () const { return bool (it); } void __next__ () { do ++it; while (it && !p (f (*it))); } void __prev__ () { --it; } + bool operator != (const hb_filter_iter_t& o) const + { return it != o.it || p != o.p || f != o.f; } private: Iter it; @@ -407,6 +463,9 @@ struct hb_zip_iter_t : void __forward__ (unsigned n) { a += n; b += n; } void __prev__ () { --a; --b; } void __rewind__ (unsigned n) { a -= n; b -= n; } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); } + bool operator != (const hb_zip_iter_t& o) const + { return a != o.a || b != o.b; } private: A a; @@ -442,6 +501,17 @@ struct hb_enumerate_iter_t : void __forward__ (unsigned n) { i += n; it += n; } void __prev__ () { --i; --it; } void __rewind__ (unsigned n) { i -= n; it -= n; } + hb_enumerate_iter_t __end__ () const + { + if (is_random_access_iterator) + return *this + this->len (); + /* Above expression loops twice. Following loops once. */ + auto it = *this; + while (it) ++it; + return it; + } + bool operator != (const hb_enumerate_iter_t& o) const + { return i != o.i || it != o.it; } private: unsigned i; diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 09e7711c0..a527b3966 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -835,6 +835,8 @@ struct CoverageFormat1 bool more () const { return i < c->glyphArray.len; } void next () { i++; } hb_codepoint_t get_glyph () const { return c->glyphArray[i]; } + bool operator != (const iter_t& o) const + { return i != o.i || c != o.c; } private: const struct CoverageFormat1 *c; @@ -987,6 +989,8 @@ struct CoverageFormat2 j++; } hb_codepoint_t get_glyph () const { return j; } + bool operator != (const iter_t& o) const + { return i != o.i || j != o.j || c != o.c; } private: const struct CoverageFormat2 *c; @@ -1136,6 +1140,16 @@ struct Coverage default:return 0; } } + bool operator != (const iter_t& o) const + { + if (format != o.format) return true; + switch (format) + { + case 1: return u.format1 != o.u.format1; + case 2: return u.format2 != o.u.format2; + default:return false; + } + } private: unsigned int format; diff --git a/src/hb-set.hh b/src/hb-set.hh index 76100f6bc..332e07bd0 100644 --- a/src/hb-set.hh +++ b/src/hb-set.hh @@ -701,6 +701,9 @@ struct hb_set_t void __next__ () { s->next (&v); if (l) l--; } void __prev__ () { s->previous (&v); } unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } protected: const hb_set_t *s; diff --git a/src/test-iter.cc b/src/test-iter.cc index 01ec93a7e..8f2fc809f 100644 --- a/src/test-iter.cc +++ b/src/test-iter.cc @@ -43,6 +43,7 @@ struct array_iter_t : hb_iter_with_fallback_t, T&> void __forward__ (unsigned n) { arr += n; } void __rewind__ (unsigned n) { arr -= n; } unsigned __len__ () const { return arr.length; } + bool operator != (const array_iter_t& o) { return arr != o.arr; } private: hb_array_t arr; @@ -66,12 +67,8 @@ struct some_array_t template static void -test_iterator (Iter it) +test_iterator_non_default_constructable (Iter it) { - Iter default_constructed; - - assert (!default_constructed); - /* Iterate over a copy of it. */ for (auto c = it.iter (); c; c++) *c; @@ -80,6 +77,10 @@ test_iterator (Iter it) for (auto c = +it; c; c++) *c; + /* Range-based for over a copy. */ + for (auto _ : +it) + (void) _; + it += it.len (); it = it + 10; it = 10 + it; @@ -90,11 +91,25 @@ test_iterator (Iter it) static_assert (true || it.is_sorted_iterator, ""); } +template +static void +test_iterator (Iter it) +{ + Iter default_constructed; + assert (!default_constructed); + + test_iterator_non_default_constructable (it); +} + template static void test_iterable (const Iterable &lst = Null(Iterable)) { + for (auto _ : lst) + (void) _; + // Test that can take iterator from. test_iterator (lst.iter ()); } @@ -141,6 +156,9 @@ main (int argc, char **argv) test_iterable (); test_iterator (hb_zip (st, v)); + test_iterator_non_default_constructable (hb_enumerate (st)); + //test_iterator_non_default_constructable (hb_iter (st) | hb_filter ()); + //test_iterator_non_default_constructable (hb_iter (st) | hb_map (hb_identity)); hb_any (st);