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 */
20template <typename T> class SkTLazy {
21public:
22 SkTLazy() = default;
23 explicit SkTLazy(const T* src) : fPtr(src ? new (&fStorage) T(*src) : nullptr) {}
24 SkTLazy(const SkTLazy& that) : fPtr(that.fPtr ? new (&fStorage) T(*that.fPtr) : nullptr) {}
25 SkTLazy(SkTLazy&& that) : fPtr(that.fPtr ? new (&fStorage) T(std::move(*that.fPtr)) : nullptr){}
26
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 */
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 */
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
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 */
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 */
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 */
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 */
111 T* getMaybeNull() const { return fPtr; }
112
113private:
114 typename std::aligned_storage<sizeof(T), alignof(T)>::type fStorage;
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 */
141template <typename T>
142class SkTCopyOnFirstWrite {
143public:
144 explicit SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
145
146 explicit SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {}
147
148 // Constructor for delayed initialization.
149 SkTCopyOnFirstWrite() : fObj(nullptr) {}
150
151 SkTCopyOnFirstWrite(const SkTCopyOnFirstWrite& that) { *this = 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.
167 void init(const T& initial) {
168 SkASSERT(nullptr == fObj);
169 SkASSERT(!fLazy.isValid());
170 fObj = &initial;
171 }
172
173 /**
174 * Returns a writable T*. The first time this is called the initial object is cloned.
175 */
176 T* writable() {
177 SkASSERT(fObj);
178 if (!fLazy.isValid()) {
179 fLazy.set(*fObj);
180 fObj = fLazy.get();
181 }
182 return const_cast<T*>(fObj);
183 }
184
185 const T* get() const { return fObj; }
186
187 /**
188 * Operators for treating this as though it were a const pointer.
189 */
190
191 const T *operator->() const { return fObj; }
192
193 operator const T*() const { return fObj; }
194
195 const T& operator *() const { return *fObj; }
196
197private:
198 const T* fObj;
199 SkTLazy<T> fLazy;
200};
201
202#endif
203