• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/core/SkTypes.h"
12 #include <new>
13 #include <type_traits>
14 #include <utility>
15 
16 /**
17  *  Efficient way to defer allocating/initializing a class until it is needed
18  *  (if ever).
19  */
20 template <typename T> class SkTLazy {
21 public:
22     SkTLazy() = default;
SkTLazy(const T * src)23     explicit SkTLazy(const T* src) : fPtr(src ? new (&fStorage) T(*src) : nullptr) {}
SkTLazy(const SkTLazy & that)24     SkTLazy(const SkTLazy& that) : fPtr(that.fPtr ? new (&fStorage) T(*that.fPtr) : nullptr) {}
SkTLazy(SkTLazy && that)25     SkTLazy(SkTLazy&& that) : fPtr(that.fPtr ? new (&fStorage) T(std::move(*that.fPtr)) : nullptr){}
26 
~SkTLazy()27     ~SkTLazy() { this->reset(); }
28 
29     SkTLazy& operator=(const SkTLazy& that) {
30         if (that.isValid()) {
31             this->set(*that);
32         } else {
33             this->reset();
34         }
35         return *this;
36     }
37 
38     SkTLazy& operator=(SkTLazy&& that) {
39         if (that.isValid()) {
40             this->set(std::move(*that));
41         } else {
42             this->reset();
43         }
44         return *this;
45     }
46 
47     /**
48      *  Return a pointer to an instance of the class initialized with 'args'.
49      *  If a previous instance had been initialized (either from init() or
50      *  set()) it will first be destroyed, so that a freshly initialized
51      *  instance is always returned.
52      */
init(Args &&...args)53     template <typename... Args> T* init(Args&&... args) {
54         this->reset();
55         fPtr = new (&fStorage) T(std::forward<Args>(args)...);
56         return fPtr;
57     }
58 
59     /**
60      *  Copy src into this, and return a pointer to a copy of it. Note this
61      *  will always return the same pointer, so if it is called on a lazy that
62      *  has already been initialized, then this will copy over the previous
63      *  contents.
64      */
set(const T & src)65     T* set(const T& src) {
66         if (this->isValid()) {
67             *fPtr = src;
68         } else {
69             fPtr = new (&fStorage) T(src);
70         }
71         return fPtr;
72     }
73 
set(T && src)74     T* set(T&& src) {
75         if (this->isValid()) {
76             *fPtr = std::move(src);
77         } else {
78             fPtr = new (&fStorage) T(std::move(src));
79         }
80         return fPtr;
81     }
82 
83     /**
84      * Destroy the lazy object (if it was created via init() or set())
85      */
reset()86     void reset() {
87         if (this->isValid()) {
88             fPtr->~T();
89             fPtr = nullptr;
90         }
91     }
92 
93     /**
94      *  Returns true if a valid object has been initialized in the SkTLazy,
95      *  false otherwise.
96      */
isValid()97     bool isValid() const { return SkToBool(fPtr); }
98 
99     /**
100      * Returns the object. This version should only be called when the caller
101      * knows that the object has been initialized.
102      */
get()103     T* get() const { SkASSERT(this->isValid()); return fPtr; }
104     T* operator->() const { return this->get(); }
105     T& operator*() const { return *this->get(); }
106 
107     /**
108      * Like above but doesn't assert if object isn't initialized (in which case
109      * nullptr is returned).
110      */
getMaybeNull()111     T* getMaybeNull() const { return fPtr; }
112 
113 private:
114     alignas(T) char fStorage[sizeof(T)];
115     T*              fPtr{nullptr}; // nullptr or fStorage
116 };
117 
118 /**
119  * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized
120  * with a const pointer but provides a non-const pointer accessor. The first time the
121  * accessor is called (if ever) the object is cloned.
122  *
123  * In the following example at most one copy of constThing is made:
124  *
125  * SkTCopyOnFirstWrite<Thing> thing(&constThing);
126  * ...
127  * function_that_takes_a_const_thing_ptr(thing); // constThing is passed
128  * ...
129  * if (need_to_modify_thing()) {
130  *    thing.writable()->modifyMe(); // makes a copy of constThing
131  * }
132  * ...
133  * x = thing->readSomething();
134  * ...
135  * if (need_to_modify_thing_now()) {
136  *    thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe()
137  * }
138  *
139  * consume_a_thing(thing); // could be constThing or a modified copy.
140  */
141 template <typename T>
142 class SkTCopyOnFirstWrite {
143 public:
SkTCopyOnFirstWrite(const T & initial)144     explicit SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
145 
SkTCopyOnFirstWrite(const T * initial)146     explicit SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {}
147 
148     // Constructor for delayed initialization.
SkTCopyOnFirstWrite()149     SkTCopyOnFirstWrite() : fObj(nullptr) {}
150 
SkTCopyOnFirstWrite(const SkTCopyOnFirstWrite & that)151     SkTCopyOnFirstWrite(const SkTCopyOnFirstWrite&  that) { *this = that;            }
SkTCopyOnFirstWrite(SkTCopyOnFirstWrite && that)152     SkTCopyOnFirstWrite(      SkTCopyOnFirstWrite&& that) { *this = std::move(that); }
153 
154     SkTCopyOnFirstWrite& operator=(const SkTCopyOnFirstWrite& that) {
155         fLazy = that.fLazy;
156         fObj  = fLazy.isValid() ? fLazy.get() : that.fObj;
157         return *this;
158     }
159 
160     SkTCopyOnFirstWrite& operator=(SkTCopyOnFirstWrite&& that) {
161         fLazy = std::move(that.fLazy);
162         fObj  = fLazy.isValid() ? fLazy.get() : that.fObj;
163         return *this;
164     }
165 
166     // Should only be called once, and only if the default constructor was used.
init(const T & initial)167     void init(const T& initial) {
168         SkASSERT(nullptr == fObj);
169         SkASSERT(!fLazy.isValid());
170         fObj = &initial;
171     }
172 
173     // If not already initialized, in-place instantiates the writable object
174     template <typename... Args>
initIfNeeded(Args &&...args)175     void initIfNeeded(Args&&... args) {
176         if (nullptr == fObj) {
177             SkASSERT(!fLazy.isValid());
178             fObj = fLazy.init(std::forward<Args>(args)...);
179         }
180     }
181 
182     /**
183      * Returns a writable T*. The first time this is called the initial object is cloned.
184      */
writable()185     T* writable() {
186         SkASSERT(fObj);
187         if (!fLazy.isValid()) {
188             fLazy.set(*fObj);
189             fObj = fLazy.get();
190         }
191         return const_cast<T*>(fObj);
192     }
193 
get()194     const T* get() const { return fObj; }
195 
196     /**
197      * Operators for treating this as though it were a const pointer.
198      */
199 
200     const T *operator->() const { return fObj; }
201 
202     operator const T*() const { return fObj; }
203 
204     const T& operator *() const { return *fObj; }
205 
206 private:
207     const T*    fObj;
208     SkTLazy<T>  fLazy;
209 };
210 
211 #endif
212