Add a way to iterate over a specific op type

Also split the OpBuffer tests out from CanvasOpTests

Test: this
Change-Id: I0fbc726d108bfa4333c01daf7aedacca8716e6ae
diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h
index 6dc29d9..1237d69 100644
--- a/libs/hwui/canvas/OpBuffer.h
+++ b/libs/hwui/canvas/OpBuffer.h
@@ -144,7 +144,86 @@
 
     ItemHeader* last() const { return isEmpty() ? nullptr : itemAt(mBuffer->endOffset); }
 
+    class sentinal {
+    public:
+        explicit sentinal(const uint8_t* end) : end(end) {}
+    private:
+        const uint8_t* const end;
+    };
+
+    sentinal end() const {
+        return sentinal{end_ptr()};
+    }
+
+    template <ItemTypes T>
+    class filtered_iterator {
+    public:
+        explicit filtered_iterator(uint8_t* start, const uint8_t* end)
+                : mCurrent(start), mEnd(end) {
+            ItemHeader* header = reinterpret_cast<ItemHeader*>(mCurrent);
+            if (header->type != T) {
+                advance();
+            }
+        }
+
+        filtered_iterator& operator++() {
+            advance();
+            return *this;
+        }
+
+        // Although this iterator self-terminates, we need a placeholder to compare against
+        // to make for-each loops happy
+        bool operator!=(const sentinal& other) const {
+            return mCurrent != mEnd;
+        }
+
+        ItemContainer<T>& operator*() {
+            return *reinterpret_cast<ItemContainer<T>*>(mCurrent);
+        }
+    private:
+        void advance() {
+            ItemHeader* header = reinterpret_cast<ItemHeader*>(mCurrent);
+            do {
+                mCurrent += header->size;
+                header = reinterpret_cast<ItemHeader*>(mCurrent);
+            } while (mCurrent != mEnd && header->type != T);
+        }
+        uint8_t* mCurrent;
+        const uint8_t* const mEnd;
+    };
+
+    template <ItemTypes T>
+    class filtered_view {
+    public:
+        explicit filtered_view(uint8_t* start, const uint8_t* end) : mStart(start), mEnd(end) {}
+
+        filtered_iterator<T> begin() const {
+            return filtered_iterator<T>{mStart, mEnd};
+        }
+
+        sentinal end() const {
+            return sentinal{mEnd};
+        }
+    private:
+        uint8_t* mStart;
+        const uint8_t* const mEnd;
+    };
+
+    template <ItemTypes T>
+    filtered_view<T> filter() const {
+        return filtered_view<T>{start_ptr(), end_ptr()};
+    }
+
 private:
+
+    uint8_t* start_ptr() const {
+        return reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->startOffset;
+    }
+
+    const uint8_t* end_ptr() const {
+        return reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->used;
+    }
+
     template <typename F, std::size_t... I>
     void for_each(F&& f, std::index_sequence<I...>) const {
         // Validate we're not empty
@@ -159,8 +238,8 @@
         }...};
 
         // Do the actual iteration of each item
-        uint8_t* current = reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->startOffset;
-        uint8_t* end = reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->used;
+        uint8_t* current = start_ptr();
+        const uint8_t* end = end_ptr();
         while (current != end) {
             auto header = reinterpret_cast<ItemHeader*>(current);
             // `f` could be a destructor, so ensure all accesses to the OP happen prior to invoking