blob: 29a8b496f0a672e12af17d727c0b999e6d9b6eac [file] [log] [blame]
reed@google.coma076e9b2011-04-06 20:17:29 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * 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.
reed@google.coma076e9b2011-04-06 20:17:29 +00006 */
7
reed@google.coma076e9b2011-04-06 20:17:29 +00008#ifndef SkTLazy_DEFINED
9#define SkTLazy_DEFINED
10
bungemanf3c15b72015-08-19 11:56:48 -070011#include "../private/SkTemplates.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000012#include "SkTypes.h"
bungeman@google.comb81be7a2011-07-28 16:51:20 +000013#include <new>
bungeman221524d2016-01-05 14:59:40 -080014#include <utility>
reed@google.coma076e9b2011-04-06 20:17:29 +000015
16/**
17 * Efficient way to defer allocating/initializing a class until it is needed
18 * (if ever).
19 */
20template <typename T> class SkTLazy {
21public:
Herb Derby811a6722019-01-10 13:58:05 -050022 SkTLazy() = default;
23 explicit SkTLazy(const T* src) : fPtr(src ? new (&fStorage) T(*src) : nullptr) {}
24 SkTLazy(const SkTLazy& that) { *this = that; }
25 SkTLazy(SkTLazy&& that) { *this = std::move(that); }
reed@google.coma076e9b2011-04-06 20:17:29 +000026
Herb Derby811a6722019-01-10 13:58:05 -050027 ~SkTLazy() { this->reset(); }
reed@google.coma076e9b2011-04-06 20:17:29 +000028
Ben Wagnere819e5f2017-11-28 16:44:10 -050029 SkTLazy& operator=(const SkTLazy& that) {
30 if (that.isValid()) {
Herb Derby811a6722019-01-10 13:58:05 -050031 this->set(*that);
Ben Wagnere819e5f2017-11-28 16:44:10 -050032 } else {
33 this->reset();
34 }
35 return *this;
36 }
37
38 SkTLazy& operator=(SkTLazy&& that) {
39 if (that.isValid()) {
Herb Derby811a6722019-01-10 13:58:05 -050040 this->set(std::move(*that));
fmalita0cbe77c2016-08-10 16:30:37 -070041 } else {
42 this->reset();
43 }
44 return *this;
45 }
46
reed@google.coma076e9b2011-04-06 20:17:29 +000047 /**
bungeman72440a32015-08-12 13:37:16 -070048 * 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.
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000052 */
bungeman72440a32015-08-12 13:37:16 -070053 template <typename... Args> T* init(Args&&... args) {
Herb Derby811a6722019-01-10 13:58:05 -050054 this->reset();
55 fPtr = new (&fStorage) T(std::forward<Args>(args)...);
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000056 return fPtr;
57 }
bsalomon@google.com8c2fe992011-09-13 15:27:18 +000058
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000059 /**
reed@google.coma076e9b2011-04-06 20:17:29 +000060 * 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) {
bsalomon@google.com8c2fe992011-09-13 15:27:18 +000066 if (this->isValid()) {
reed@google.coma076e9b2011-04-06 20:17:29 +000067 *fPtr = src;
68 } else {
Herb Derby811a6722019-01-10 13:58:05 -050069 fPtr = new (&fStorage) T(src);
reed@google.coma076e9b2011-04-06 20:17:29 +000070 }
71 return fPtr;
72 }
bsalomon@google.com8c2fe992011-09-13 15:27:18 +000073
Ben Wagnere819e5f2017-11-28 16:44:10 -050074 T* set(T&& src) {
75 if (this->isValid()) {
76 *fPtr = std::move(src);
77 } else {
Herb Derby811a6722019-01-10 13:58:05 -050078 fPtr = new (&fStorage) T(std::move(src));
Ben Wagnere819e5f2017-11-28 16:44:10 -050079 }
80 return fPtr;
81 }
82
bsalomon@google.com8c2fe992011-09-13 15:27:18 +000083 /**
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000084 * Destroy the lazy object (if it was created via init() or set())
85 */
86 void reset() {
87 if (this->isValid()) {
88 fPtr->~T();
fmalita0cbe77c2016-08-10 16:30:37 -070089 fPtr = nullptr;
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000090 }
91 }
92
93 /**
bsalomon@google.com8c2fe992011-09-13 15:27:18 +000094 * Returns true if a valid object has been initialized in the SkTLazy,
95 * false otherwise.
96 */
bsalomon49f085d2014-09-05 13:34:00 -070097 bool isValid() const { return SkToBool(fPtr); }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000098
reed@google.coma076e9b2011-04-06 20:17:29 +000099 /**
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000100 * Returns the object. This version should only be called when the caller
101 * knows that the object has been initialized.
reed@google.coma076e9b2011-04-06 20:17:29 +0000102 */
bsalomon@google.com8c2fe992011-09-13 15:27:18 +0000103 T* get() const { SkASSERT(this->isValid()); return fPtr; }
Herb Derby811a6722019-01-10 13:58:05 -0500104 T* operator->() const { return this->get(); }
105 T& operator*() const { return *this->get(); }
skia.committer@gmail.com05a2ee02013-04-02 07:01:34 +0000106
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000107 /**
108 * Like above but doesn't assert if object isn't initialized (in which case
fmalita0cbe77c2016-08-10 16:30:37 -0700109 * nullptr is returned).
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000110 */
111 T* getMaybeNull() const { return fPtr; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000112
reed@google.coma076e9b2011-04-06 20:17:29 +0000113private:
Herb Derby811a6722019-01-10 13:58:05 -0500114 typename std::aligned_storage<sizeof(T), alignof(T)>::type fStorage;
115 T* fPtr{nullptr}; // nullptr or fStorage
reed@google.coma076e9b2011-04-06 20:17:29 +0000116};
117
bsalomon@google.com5dc26b92012-10-11 19:32:32 +0000118/**
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:
Florin Malita8eaf64a2018-04-11 11:59:18 -0400144 explicit SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
bsalomon@google.com5dc26b92012-10-11 19:32:32 +0000145
Florin Malita8eaf64a2018-04-11 11:59:18 -0400146 explicit SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {}
fmalita32cdc322016-01-12 07:21:11 -0800147
bsalomon@google.com45a15f52012-12-10 19:10:17 +0000148 // Constructor for delayed initialization.
fmalita0cbe77c2016-08-10 16:30:37 -0700149 SkTCopyOnFirstWrite() : fObj(nullptr) {}
bsalomon@google.com45a15f52012-12-10 19:10:17 +0000150
Florin Malita8eaf64a2018-04-11 11:59:18 -0400151 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
bsalomon@google.com45a15f52012-12-10 19:10:17 +0000166 // Should only be called once, and only if the default constructor was used.
167 void init(const T& initial) {
fmalita0cbe77c2016-08-10 16:30:37 -0700168 SkASSERT(nullptr == fObj);
bsalomon@google.com45a15f52012-12-10 19:10:17 +0000169 SkASSERT(!fLazy.isValid());
170 fObj = &initial;
171 }
172
bsalomon@google.com5dc26b92012-10-11 19:32:32 +0000173 /**
174 * Returns a writable T*. The first time this is called the initial object is cloned.
175 */
176 T* writable() {
bsalomon49f085d2014-09-05 13:34:00 -0700177 SkASSERT(fObj);
bsalomon@google.com5dc26b92012-10-11 19:32:32 +0000178 if (!fLazy.isValid()) {
179 fLazy.set(*fObj);
180 fObj = fLazy.get();
181 }
182 return const_cast<T*>(fObj);
183 }
184
Florin Malita8eaf64a2018-04-11 11:59:18 -0400185 const T* get() const { return fObj; }
186
bsalomon@google.com5dc26b92012-10-11 19:32:32 +0000187 /**
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
reed@google.coma076e9b2011-04-06 20:17:29 +0000202#endif