1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SkTLazy_DEFINED
9#define SkTLazy_DEFINED
10
11#include "../private/SkTemplates.h"
12#include "SkTypes.h"
13#include <new>
14#include <utility>
15
16/**
17 *  Efficient way to defer allocating/initializing a class until it is needed
18 *  (if ever).
19 */
20template <typename T> class SkTLazy {
21public:
22    SkTLazy() : fPtr(NULL) {}
23
24    explicit SkTLazy(const T* src) : fPtr(NULL) {
25        if (src) {
26            fPtr = new (fStorage.get()) T(*src);
27        }
28    }
29
30    SkTLazy(const SkTLazy<T>& src) : fPtr(NULL) {
31        if (src.isValid()) {
32            fPtr = new (fStorage.get()) T(*src->get());
33        } else {
34            fPtr = NULL;
35        }
36    }
37
38    ~SkTLazy() {
39        if (this->isValid()) {
40            fPtr->~T();
41        }
42    }
43
44    /**
45     *  Return a pointer to an instance of the class initialized with 'args'.
46     *  If a previous instance had been initialized (either from init() or
47     *  set()) it will first be destroyed, so that a freshly initialized
48     *  instance is always returned.
49     */
50    template <typename... Args> T* init(Args&&... args) {
51        if (this->isValid()) {
52            fPtr->~T();
53        }
54        fPtr = new (SkTCast<T*>(fStorage.get())) T(std::forward<Args>(args)...);
55        return fPtr;
56    }
57
58    /**
59     *  Copy src into this, and return a pointer to a copy of it. Note this
60     *  will always return the same pointer, so if it is called on a lazy that
61     *  has already been initialized, then this will copy over the previous
62     *  contents.
63     */
64    T* set(const T& src) {
65        if (this->isValid()) {
66            *fPtr = src;
67        } else {
68            fPtr = new (SkTCast<T*>(fStorage.get())) T(src);
69        }
70        return fPtr;
71    }
72
73    /**
74     * Destroy the lazy object (if it was created via init() or set())
75     */
76    void reset() {
77        if (this->isValid()) {
78            fPtr->~T();
79            fPtr = NULL;
80        }
81    }
82
83    /**
84     *  Returns true if a valid object has been initialized in the SkTLazy,
85     *  false otherwise.
86     */
87    bool isValid() const { return SkToBool(fPtr); }
88
89    /**
90     * Returns the object. This version should only be called when the caller
91     * knows that the object has been initialized.
92     */
93    T* get() const { SkASSERT(this->isValid()); return fPtr; }
94
95    /**
96     * Like above but doesn't assert if object isn't initialized (in which case
97     * NULL is returned).
98     */
99    T* getMaybeNull() const { return fPtr; }
100
101private:
102    T* fPtr; // NULL or fStorage
103    SkAlignedSTStorage<1, T> fStorage;
104};
105
106/**
107 * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized
108 * with a const pointer but provides a non-const pointer accessor. The first time the
109 * accessor is called (if ever) the object is cloned.
110 *
111 * In the following example at most one copy of constThing is made:
112 *
113 * SkTCopyOnFirstWrite<Thing> thing(&constThing);
114 * ...
115 * function_that_takes_a_const_thing_ptr(thing); // constThing is passed
116 * ...
117 * if (need_to_modify_thing()) {
118 *    thing.writable()->modifyMe(); // makes a copy of constThing
119 * }
120 * ...
121 * x = thing->readSomething();
122 * ...
123 * if (need_to_modify_thing_now()) {
124 *    thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe()
125 * }
126 *
127 * consume_a_thing(thing); // could be constThing or a modified copy.
128 */
129template <typename T>
130class SkTCopyOnFirstWrite {
131public:
132    SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
133
134    SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {}
135
136    // Constructor for delayed initialization.
137    SkTCopyOnFirstWrite() : fObj(NULL) {}
138
139    // Should only be called once, and only if the default constructor was used.
140    void init(const T& initial) {
141        SkASSERT(NULL == fObj);
142        SkASSERT(!fLazy.isValid());
143        fObj = &initial;
144    }
145
146    /**
147     * Returns a writable T*. The first time this is called the initial object is cloned.
148     */
149    T* writable() {
150        SkASSERT(fObj);
151        if (!fLazy.isValid()) {
152            fLazy.set(*fObj);
153            fObj = fLazy.get();
154        }
155        return const_cast<T*>(fObj);
156    }
157
158    /**
159     * Operators for treating this as though it were a const pointer.
160     */
161
162    const T *operator->() const { return fObj; }
163
164    operator const T*() const { return fObj; }
165
166    const T& operator *() const { return *fObj; }
167
168private:
169    const T*    fObj;
170    SkTLazy<T>  fLazy;
171};
172
173#endif
174