• 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 "../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  */
20 template <typename T> class SkTLazy {
21 public:
SkTLazy()22     SkTLazy() : fPtr(nullptr) {}
23 
SkTLazy(const T * src)24     explicit SkTLazy(const T* src)
25         : fPtr(src ? new (fStorage.get()) T(*src) : nullptr) {}
26 
SkTLazy(const SkTLazy & src)27     SkTLazy(const SkTLazy& src) : fPtr(nullptr) { *this = src; }
28 
~SkTLazy()29     ~SkTLazy() {
30         if (this->isValid()) {
31             fPtr->~T();
32         }
33     }
34 
35     SkTLazy& operator=(const SkTLazy& src) {
36         if (src.isValid()) {
37             this->set(*src.get());
38         } else {
39             this->reset();
40         }
41         return *this;
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      */
init(Args &&...args)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      */
set(const T & src)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      */
reset()76     void reset() {
77         if (this->isValid()) {
78             fPtr->~T();
79             fPtr = nullptr;
80         }
81     }
82 
83     /**
84      *  Returns true if a valid object has been initialized in the SkTLazy,
85      *  false otherwise.
86      */
isValid()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      */
get()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      * nullptr is returned).
98      */
getMaybeNull()99     T* getMaybeNull() const { return fPtr; }
100 
101 private:
102     SkAlignedSTStorage<1, T> fStorage;
103     T*                       fPtr; // nullptr or 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  */
129 template <typename T>
130 class SkTCopyOnFirstWrite {
131 public:
SkTCopyOnFirstWrite(const T & initial)132     SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
133 
SkTCopyOnFirstWrite(const T * initial)134     SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {}
135 
136     // Constructor for delayed initialization.
SkTCopyOnFirstWrite()137     SkTCopyOnFirstWrite() : fObj(nullptr) {}
138 
139     // Should only be called once, and only if the default constructor was used.
init(const T & initial)140     void init(const T& initial) {
141         SkASSERT(nullptr == 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      */
writable()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 
168 private:
169     const T*    fObj;
170     SkTLazy<T>  fLazy;
171 };
172 
173 #endif
174