1c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein/*
2c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein * Copyright 2015 Google Inc.
3c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein *
4c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein * Use of this source code is governed by a BSD-style license that can be
5c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein * found in the LICENSE file.
6c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein */
7c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein
873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#ifndef SkRecordPattern_DEFINED
973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#define SkRecordPattern_DEFINED
1073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
116b20a559968e1ed0d3aa4ecbd6ddda9e681b4f86Hal Canary#include "SkRecord.h"
1273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#include "SkTLogic.h"
1373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
1473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgnamespace SkRecords {
1573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
1673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// First, some matchers.  These match a single command in the SkRecord,
1773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// and may hang onto some data from it.  If so, you can get the data by calling .get().
1873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
1973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Matches a command of type T, and stores that command.
2073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename T>
2173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgclass Is {
2273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgpublic:
2396fcdcc219d2a0d3579719b84b28bede76efba64halcanary    Is() : fPtr(nullptr) {}
2473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
2573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    typedef T type;
2673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    type* get() { return fPtr; }
2773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
28c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    bool operator()(T* ptr) {
2973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        fPtr = ptr;
3073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return true;
3173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
3273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
3373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename U>
34c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    bool operator()(U*) {
3596fcdcc219d2a0d3579719b84b28bede76efba64halcanary        fPtr = nullptr;
3673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return false;
3773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
3873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
3973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgprivate:
4073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    type* fPtr;
4173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
4273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
4373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Matches any command that draws, and stores its paint.
4473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgclass IsDraw {
4573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgpublic:
4696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    IsDraw() : fPaint(nullptr) {}
4773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
4873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    typedef SkPaint type;
4973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    type* get() { return fPaint; }
5073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
5173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
52449d9b7e2d1b2e20963f18639c6e541ef953f069mtklein    SK_WHEN(T::kTags & kDraw_Tag, bool) operator()(T* draw) {
5373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        fPaint = AsPtr(draw->paint);
5473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return true;
5573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
5673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
57449d9b7e2d1b2e20963f18639c6e541ef953f069mtklein    bool operator()(DrawDrawable*) {
58449d9b7e2d1b2e20963f18639c6e541ef953f069mtklein        static_assert(DrawDrawable::kTags & kDraw_Tag, "");
5996fcdcc219d2a0d3579719b84b28bede76efba64halcanary        fPaint = nullptr;
60449d9b7e2d1b2e20963f18639c6e541ef953f069mtklein        return true;
6173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
6273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
63449d9b7e2d1b2e20963f18639c6e541ef953f069mtklein    template <typename T>
64449d9b7e2d1b2e20963f18639c6e541ef953f069mtklein    SK_WHEN(!(T::kTags & kDraw_Tag), bool) operator()(T* draw) {
6596fcdcc219d2a0d3579719b84b28bede76efba64halcanary        fPaint = nullptr;
661b546462bb91e93cf2f033eb2dab53ec492b64abcommit-bot@chromium.org        return false;
671b546462bb91e93cf2f033eb2dab53ec492b64abcommit-bot@chromium.org    }
681b546462bb91e93cf2f033eb2dab53ec492b64abcommit-bot@chromium.org
6973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgprivate:
7073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // Abstracts away whether the paint is always part of the command or optional.
7173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T> static T* AsPtr(SkRecords::Optional<T>& x) { return x; }
7273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T> static T* AsPtr(T& x) { return &x; }
7373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
7473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    type* fPaint;
7573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
7673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
7773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Matches if Matcher doesn't.  Stores nothing.
7873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename Matcher>
7973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Not {
8073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
81c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    bool operator()(T* ptr) { return !Matcher()(ptr); }
8273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
8373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
8424e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein// Matches if any of First or Rest... does.  Stores nothing.
8524e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleintemplate <typename First, typename... Rest>
8673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Or {
8773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
8824e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    bool operator()(T* ptr) { return First()(ptr) || Or<Rest...>()(ptr); }
8924e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein};
9024e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleintemplate <typename First>
9124e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleinstruct Or<First> {
9224e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    template <typename T>
9324e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    bool operator()(T* ptr) { return First()(ptr); }
9473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
9573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
9699d6a9ee8b3516de892d118c71aa5e6e5c865efdmtklein
9724e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein// Greedy is a special matcher that greedily matches Matcher 0 or more times.  Stores nothing.
9873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename Matcher>
9924e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleinstruct Greedy {
10073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
101888e4687d96b6af5b5c0efbcf05fcdc010ea8aa2commit-bot@chromium.org    bool operator()(T* ptr) { return Matcher()(ptr); }
10273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
10373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
10424e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein// Pattern matches each of its matchers in order.
10573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//
10673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// This is the main entry point to pattern matching, and so provides a couple of extra API bits:
10773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//  - search scans through the record to look for matches;
10824e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein//  - first, second, third, ... return the data stored by their respective matchers in the pattern.
10924e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein
11024e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleintemplate <typename... Matchers> class Pattern;
11124e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein
11224e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleintemplate <> class Pattern<> {
11373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgpublic:
11424e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    // Bottoms out recursion.  Just return whatever i the front decided on.
11524e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    int match(SkRecord*, int i) { return i; }
11624e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein};
11724e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein
11824e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleintemplate <typename First, typename... Rest>
11924e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleinclass Pattern<First, Rest...> {
12024e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtkleinpublic:
12124e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    // If this pattern matches the SkRecord starting from i,
12273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // return the index just past the end of the pattern, otherwise return 0.
123c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    SK_ALWAYS_INLINE int match(SkRecord* record, int i) {
12424e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein        i = this->matchFirst(&fFirst, record, i);
12524e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein        return i > 0 ? fRest.match(record, i) : 0;
12673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
12773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
12873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // Starting from *end, walk through the SkRecord to find the first span matching this pattern.
12973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // If there is no such span, return false.  If there is, return true and set [*begin, *end).
130c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    SK_ALWAYS_INLINE bool search(SkRecord* record, int* begin, int* end) {
13173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        for (*begin = *end; *begin < record->count(); ++(*begin)) {
13273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            *end = this->match(record, *begin);
13373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            if (*end != 0) {
13473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                return true;
13573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            }
13673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        }
13773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return false;
13873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
13973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
14024e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    // TODO: some sort of smart get<i>()
14124e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    template <typename T> T* first()  { return fFirst.get();   }
14224e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    template <typename T> T* second() { return fRest.template first<T>();  }
14324e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    template <typename T> T* third()  { return fRest.template second<T>(); }
14424e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    template <typename T> T* fourth() { return fRest.template third<T>();  }
14573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
14673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgprivate:
14724e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    // If first isn't a Greedy, try to match at i once.
14873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
14924e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    int matchFirst(T* first, SkRecord* record, int i) {
15073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        if (i < record->count()) {
151343a63d082bda969d7e8a4e09ba850e931185269mtklein            if (record->mutate(i, *first)) {
15273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                return i+1;
15373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            }
15473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        }
15573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return 0;
15673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
15773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
15824e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    // If first is a Greedy, walk i until it doesn't match.
15973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
16024e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    int matchFirst(Greedy<T>* first, SkRecord* record, int i) {
16173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        while (i < record->count()) {
162343a63d082bda969d7e8a4e09ba850e931185269mtklein            if (!record->mutate(i, *first)) {
16373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                return i;
16473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            }
16573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            i++;
16673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        }
16773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return 0;
16873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
16973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
17024e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    First            fFirst;
17124e7db8b2ea663f8fe4d7bbfa1d686fd643990bbmtklein    Pattern<Rest...> fRest;
17273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
17373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
17473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org}  // namespace SkRecords
17573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
17673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#endif//SkRecordPattern_DEFINED
177