Line data Source code
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:
22 1259 : SkTLazy() : fPtr(nullptr) {}
23 :
24 : explicit SkTLazy(const T* src)
25 : : fPtr(src ? new (fStorage.get()) T(*src) : nullptr) {}
26 :
27 : SkTLazy(const SkTLazy& src) : fPtr(nullptr) { *this = src; }
28 :
29 1259 : ~SkTLazy() {
30 1259 : if (this->isValid()) {
31 0 : fPtr->~T();
32 : }
33 1259 : }
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 : */
50 0 : template <typename... Args> T* init(Args&&... args) {
51 0 : if (this->isValid()) {
52 0 : fPtr->~T();
53 : }
54 0 : fPtr = new (SkTCast<T*>(fStorage.get())) T(std::forward<Args>(args)...);
55 0 : 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 : */
64 0 : T* set(const T& src) {
65 0 : if (this->isValid()) {
66 0 : *fPtr = src;
67 : } else {
68 0 : fPtr = new (SkTCast<T*>(fStorage.get())) T(src);
69 : }
70 0 : return fPtr;
71 : }
72 :
73 : /**
74 : * Destroy the lazy object (if it was created via init() or set())
75 : */
76 0 : void reset() {
77 0 : if (this->isValid()) {
78 0 : fPtr->~T();
79 0 : fPtr = nullptr;
80 : }
81 0 : }
82 :
83 : /**
84 : * Returns true if a valid object has been initialized in the SkTLazy,
85 : * false otherwise.
86 : */
87 1259 : 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 : */
93 0 : 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 : */
99 0 : 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 516 : class SkTCopyOnFirstWrite {
131 : public:
132 516 : SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
133 :
134 : SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {}
135 :
136 : // Constructor for delayed initialization.
137 : SkTCopyOnFirstWrite() : fObj(nullptr) {}
138 :
139 : // Should only be called once, and only if the default constructor was used.
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 : */
149 0 : T* writable() {
150 0 : SkASSERT(fObj);
151 0 : if (!fLazy.isValid()) {
152 0 : fLazy.set(*fObj);
153 0 : fObj = fLazy.get();
154 : }
155 0 : return const_cast<T*>(fObj);
156 : }
157 :
158 : /**
159 : * Operators for treating this as though it were a const pointer.
160 : */
161 :
162 528 : const T *operator->() const { return fObj; }
163 :
164 : operator const T*() const { return fObj; }
165 :
166 1118 : const T& operator *() const { return *fObj; }
167 :
168 : private:
169 : const T* fObj;
170 : SkTLazy<T> fLazy;
171 : };
172 :
173 : #endif
|