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