• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2011 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 
11 #ifndef SkTLazy_DEFINED
12 #define SkTLazy_DEFINED
13 
14 #include "SkTypes.h"
15 #include <new>
16 
17 /**
18  *  Efficient way to defer allocating/initializing a class until it is needed
19  *  (if ever).
20  */
21 template <typename T> class SkTLazy {
22 public:
SkTLazy()23     SkTLazy() : fPtr(NULL) {}
24 
SkTLazy(const T * src)25     explicit SkTLazy(const T* src) : fPtr(NULL) {
26         if (src) {
27             fPtr = new (fStorage) T(*src);
28         }
29     }
30 
SkTLazy(const SkTLazy<T> & src)31     SkTLazy(const SkTLazy<T>& src) : fPtr(NULL) {
32         if (src.isValid()) {
33             fPtr = new (fStorage) T(*src->get());
34         } else {
35             fPtr = NULL;
36         }
37     }
38 
~SkTLazy()39     ~SkTLazy() {
40         if (this->isValid()) {
41             fPtr->~T();
42         }
43     }
44 
45     /**
46      *  Return a pointer to a default-initialized instance of the class. If a
47      *  previous instance had been initialzied (either from init() or set()) it
48      *  will first be destroyed, so that a freshly initialized instance is
49      *  always returned.
50      */
init()51     T* init() {
52         if (this->isValid()) {
53             fPtr->~T();
54         }
55         fPtr = new (SkTCast<T*>(fStorage)) T;
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      */
set(const T & src)65     T* set(const T& src) {
66         if (this->isValid()) {
67             *fPtr = src;
68         } else {
69             fPtr = new (SkTCast<T*>(fStorage)) T(src);
70         }
71         return fPtr;
72     }
73 
74     /**
75      *  Returns true if a valid object has been initialized in the SkTLazy,
76      *  false otherwise.
77      */
isValid()78     bool isValid() const { return NULL != fPtr; }
79 
80     /**
81      *  Returns either NULL, or a copy of the object that was passed to
82      *  set() or the constructor.
83      */
get()84     T* get() const { SkASSERT(this->isValid()); return fPtr; }
85 
86 private:
87     T*   fPtr; // NULL or fStorage
88     char fStorage[sizeof(T)];
89 };
90 
91 /**
92  * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized
93  * with a const pointer but provides a non-const pointer accessor. The first time the
94  * accessor is called (if ever) the object is cloned.
95  *
96  * In the following example at most one copy of constThing is made:
97  *
98  * SkTCopyOnFirstWrite<Thing> thing(&constThing);
99  * ...
100  * function_that_takes_a_const_thing_ptr(thing); // constThing is passed
101  * ...
102  * if (need_to_modify_thing()) {
103  *    thing.writable()->modifyMe(); // makes a copy of constThing
104  * }
105  * ...
106  * x = thing->readSomething();
107  * ...
108  * if (need_to_modify_thing_now()) {
109  *    thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe()
110  * }
111  *
112  * consume_a_thing(thing); // could be constThing or a modified copy.
113  */
114 template <typename T>
115 class SkTCopyOnFirstWrite {
116 public:
SkTCopyOnFirstWrite(const T & initial)117     SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
118 
119     // Constructor for delayed initialization.
SkTCopyOnFirstWrite()120     SkTCopyOnFirstWrite() : fObj(NULL) {}
121 
122     // Should only be called once, and only if the default constructor was used.
init(const T & initial)123     void init(const T& initial) {
124         SkASSERT(NULL == fObj);
125         SkASSERT(!fLazy.isValid());
126         fObj = &initial;
127     }
128 
129     /**
130      * Returns a writable T*. The first time this is called the initial object is cloned.
131      */
writable()132     T* writable() {
133         SkASSERT(NULL != fObj);
134         if (!fLazy.isValid()) {
135             fLazy.set(*fObj);
136             fObj = fLazy.get();
137         }
138         return const_cast<T*>(fObj);
139     }
140 
141     /**
142      * Operators for treating this as though it were a const pointer.
143      */
144 
145     const T *operator->() const { return fObj; }
146 
147     operator const T*() const { return fObj; }
148 
149     const T& operator *() const { return *fObj; }
150 
151 private:
152     const T*    fObj;
153     SkTLazy<T>  fLazy;
154 };
155 
156 #endif
157