173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#ifndef SkRecordPattern_DEFINED
273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#define SkRecordPattern_DEFINED
373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#include "SkTLogic.h"
573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgnamespace SkRecords {
773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// First, some matchers.  These match a single command in the SkRecord,
973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// and may hang onto some data from it.  If so, you can get the data by calling .get().
1073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
1173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Matches a command of type T, and stores that command.
1273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename T>
1373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgclass Is {
1473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgpublic:
1573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    Is() : fPtr(NULL) {}
1673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
1773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    typedef T type;
1873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    type* get() { return fPtr; }
1973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
20c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    bool operator()(T* ptr) {
2173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        fPtr = ptr;
2273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return true;
2373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
2473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
2573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename U>
26c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    bool operator()(U*) {
2773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        fPtr = NULL;
2873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return false;
2973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
3073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
3173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgprivate:
3273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    type* fPtr;
3373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
3473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
3573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Matches any command that draws, and stores its paint.
3673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgclass IsDraw {
3773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    SK_CREATE_MEMBER_DETECTOR(paint);
3873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgpublic:
3973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    IsDraw() : fPaint(NULL) {}
4073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
4173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    typedef SkPaint type;
4273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    type* get() { return fPaint; }
4373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
4473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
45c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    SK_WHEN(HasMember_paint<T>, bool) operator()(T* draw) {
4673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        fPaint = AsPtr(draw->paint);
4773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return true;
4873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
4973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
5073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
51c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    SK_WHEN(!HasMember_paint<T>, bool) operator()(T*) {
5273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        fPaint = NULL;
5373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return false;
5473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
5573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
561b546462bb91e93cf2f033eb2dab53ec492b64abcommit-bot@chromium.org    // SaveLayer has an SkPaint named paint, but it's not a draw.
57c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    bool operator()(SaveLayer*) {
581b546462bb91e93cf2f033eb2dab53ec492b64abcommit-bot@chromium.org        fPaint = NULL;
591b546462bb91e93cf2f033eb2dab53ec492b64abcommit-bot@chromium.org        return false;
601b546462bb91e93cf2f033eb2dab53ec492b64abcommit-bot@chromium.org    }
611b546462bb91e93cf2f033eb2dab53ec492b64abcommit-bot@chromium.org
6273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgprivate:
6373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // Abstracts away whether the paint is always part of the command or optional.
6473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T> static T* AsPtr(SkRecords::Optional<T>& x) { return x; }
6573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T> static T* AsPtr(T& x) { return &x; }
6673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
6773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    type* fPaint;
6873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
6973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
7073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Matches if Matcher doesn't.  Stores nothing.
7173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename Matcher>
7273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Not {
7373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
74c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    bool operator()(T* ptr) { return !Matcher()(ptr); }
7573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
7673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
7773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Matches if either of A or B does.  Stores nothing.
7873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename A, typename B>
7973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Or {
8073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
81c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org    bool operator()(T* ptr) { return A()(ptr) || B()(ptr); }
8273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
8373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
8473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Matches if any of A, B or C does.  Stores nothing.
8573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename A, typename B, typename C>
8673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Or3 : Or<A, Or<B, C> > {};
8773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
88888e4687d96b6af5b5c0efbcf05fcdc010ea8aa2commit-bot@chromium.org// Star is a special matcher that greedily matches Matcher 0 or more times.  Stores nothing.
8973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename Matcher>
90888e4687d96b6af5b5c0efbcf05fcdc010ea8aa2commit-bot@chromium.orgstruct Star {
9173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
92888e4687d96b6af5b5c0efbcf05fcdc010ea8aa2commit-bot@chromium.org    bool operator()(T* ptr) { return Matcher()(ptr); }
9373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
9473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
9573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Cons builds a list of Matchers.
9673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// It first matches Matcher (something from above), then Pattern (another Cons or Nil).
9773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//
9873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// This is the main entry point to pattern matching, and so provides a couple of extra API bits:
9973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//  - search scans through the record to look for matches;
10073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//  - first, second, and third return the data stored by their respective matchers in the pattern.
10173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org//
10273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// These Cons build lists analogously to Lisp's "cons".  See Pattern# for the "list" equivalent.
10373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename Matcher, typename Pattern>
10473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgclass Cons {
10573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgpublic:
10673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // If this pattern matches the SkRecord starting at i,
10773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // return the index just past the end of the pattern, otherwise return 0.
10873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    SK_ALWAYS_INLINE unsigned match(SkRecord* record, unsigned i) {
10973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        i = this->matchHead(&fHead, record, i);
11073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return i == 0 ? 0 : fTail.match(record, i);
11173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
11273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
11373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // Starting from *end, walk through the SkRecord to find the first span matching this pattern.
11473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // If there is no such span, return false.  If there is, return true and set [*begin, *end).
11573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    SK_ALWAYS_INLINE bool search(SkRecord* record, unsigned* begin, unsigned* end) {
11673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        for (*begin = *end; *begin < record->count(); ++(*begin)) {
11773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            *end = this->match(record, *begin);
11873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            if (*end != 0) {
11973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                return true;
12073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            }
12173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        }
12273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return false;
12373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
12473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
12573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // Once either match or search has succeeded, access the stored data of the first, second,
12673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // or third matcher in this pattern.  Add as needed for longer patterns.
12773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // T is checked statically at compile time; no casting is involved.  It's just an API wart.
12873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T> T* first()  { return fHead.get(); }
12973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T> T* second() { return fTail.fHead.get(); }
13073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T> T* third()  { return fTail.fTail.fHead.get(); }
13173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
13273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgprivate:
13373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // If head isn't a Star, try to match at i once.
13473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
13573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    unsigned matchHead(T*, SkRecord* record, unsigned i) {
13673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        if (i < record->count()) {
137c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org            if (record->mutate<bool>(i, fHead)) {
13873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                return i+1;
13973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            }
14073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        }
14173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return 0;
14273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
14373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
14473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // If head is a Star, walk i until it doesn't match.
14573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename T>
14673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    unsigned matchHead(Star<T>*, SkRecord* record, unsigned i) {
14773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        while (i < record->count()) {
148c71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4commit-bot@chromium.org            if (!record->mutate<bool>(i, fHead)) {
14973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org                return i;
15073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            }
15173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org            i++;
15273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        }
15373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org        return 0;
15473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    }
15573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
15673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    Matcher fHead;
15773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    Pattern fTail;
15873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
15973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // All Cons are friends with each other.  This lets first, second, and third work.
16073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    template <typename, typename> friend class Cons;
16173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
16273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
16373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// Nil is the end of every pattern Cons chain.
16473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Nil {
16573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    // Bottoms out recursion down the fTail chain.  Just return whatever i the front decided on.
16673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org    unsigned match(SkRecord*, unsigned i) { return i; }
16773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org};
16873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
16973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// These Pattern# types are syntax sugar over Cons and Nil, just to help eliminate some of the
17073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// template noise.  Use these if you can.  Feel free to add more for longer patterns.
17173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org// All types A, B, C, ... are Matchers.
17273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename A>
17373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Pattern1 : Cons<A, Nil> {};
17473fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
17573fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename A, typename B>
17673fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Pattern2 : Cons<A, Pattern1<B> > {};
17773fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
17873fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgtemplate <typename A, typename B, typename C>
17973fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.orgstruct Pattern3 : Cons<A, Pattern2<B, C> > {};
18073fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
18173fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org}  // namespace SkRecords
18273fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org
18373fffeb83aab56bc8c2c5ce143ee9d132d64ac37commit-bot@chromium.org#endif//SkRecordPattern_DEFINED
184