1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/* 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2011 Google Inc. 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file. 6a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com */ 7a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com 8a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com#ifndef SkTLazy_DEFINED 9a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com#define SkTLazy_DEFINED 10a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com 11f3c15b7cfc4eed2528f7db87ea6c1444b55ee856bungeman#include "../private/SkTemplates.h" 12a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com#include "SkTypes.h" 13b81be7a7809fb0c24ff7d589d537c38b04b772f1bungeman@google.com#include <new> 14221524de3be1fc343ad328c5e99562f32b5cad9cbungeman#include <utility> 15a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com 16a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com/** 17a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com * Efficient way to defer allocating/initializing a class until it is needed 18a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com * (if ever). 19a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com */ 20a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.comtemplate <typename T> class SkTLazy { 21a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.compublic: 220cbe77c383a1c829341b27df1a9219bc33524440fmalita SkTLazy() : fPtr(nullptr) {} 23a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com 240cbe77c383a1c829341b27df1a9219bc33524440fmalita explicit SkTLazy(const T* src) 250cbe77c383a1c829341b27df1a9219bc33524440fmalita : fPtr(src ? new (fStorage.get()) T(*src) : nullptr) {} 26a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com 270cbe77c383a1c829341b27df1a9219bc33524440fmalita SkTLazy(const SkTLazy& src) : fPtr(nullptr) { *this = src; } 28a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com 29a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com ~SkTLazy() { 308c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com if (this->isValid()) { 31a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com fPtr->~T(); 32a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com } 33a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com } 34a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com 350cbe77c383a1c829341b27df1a9219bc33524440fmalita SkTLazy& operator=(const SkTLazy& src) { 360cbe77c383a1c829341b27df1a9219bc33524440fmalita if (src.isValid()) { 370cbe77c383a1c829341b27df1a9219bc33524440fmalita this->set(*src.get()); 380cbe77c383a1c829341b27df1a9219bc33524440fmalita } else { 390cbe77c383a1c829341b27df1a9219bc33524440fmalita this->reset(); 400cbe77c383a1c829341b27df1a9219bc33524440fmalita } 410cbe77c383a1c829341b27df1a9219bc33524440fmalita return *this; 420cbe77c383a1c829341b27df1a9219bc33524440fmalita } 430cbe77c383a1c829341b27df1a9219bc33524440fmalita 44a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com /** 4572440a3785c13b8ec539d7e11bea1124eeddecbdbungeman * Return a pointer to an instance of the class initialized with 'args'. 4672440a3785c13b8ec539d7e11bea1124eeddecbdbungeman * If a previous instance had been initialized (either from init() or 4772440a3785c13b8ec539d7e11bea1124eeddecbdbungeman * set()) it will first be destroyed, so that a freshly initialized 4872440a3785c13b8ec539d7e11bea1124eeddecbdbungeman * instance is always returned. 492c8fc5a7038cdfbb28a8364fd0057f3c21f90bfdmike@reedtribe.org */ 5072440a3785c13b8ec539d7e11bea1124eeddecbdbungeman template <typename... Args> T* init(Args&&... args) { 518c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com if (this->isValid()) { 522c8fc5a7038cdfbb28a8364fd0057f3c21f90bfdmike@reedtribe.org fPtr->~T(); 532c8fc5a7038cdfbb28a8364fd0057f3c21f90bfdmike@reedtribe.org } 54221524de3be1fc343ad328c5e99562f32b5cad9cbungeman fPtr = new (SkTCast<T*>(fStorage.get())) T(std::forward<Args>(args)...); 552c8fc5a7038cdfbb28a8364fd0057f3c21f90bfdmike@reedtribe.org return fPtr; 562c8fc5a7038cdfbb28a8364fd0057f3c21f90bfdmike@reedtribe.org } 578c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com 582c8fc5a7038cdfbb28a8364fd0057f3c21f90bfdmike@reedtribe.org /** 59a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com * Copy src into this, and return a pointer to a copy of it. Note this 60a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com * will always return the same pointer, so if it is called on a lazy that 61a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com * has already been initialized, then this will copy over the previous 62a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com * contents. 63a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com */ 64a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com T* set(const T& src) { 658c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com if (this->isValid()) { 66a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com *fPtr = src; 67a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com } else { 6872440a3785c13b8ec539d7e11bea1124eeddecbdbungeman fPtr = new (SkTCast<T*>(fStorage.get())) T(src); 69a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com } 70a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com return fPtr; 71a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com } 728c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com 738c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com /** 746f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org * Destroy the lazy object (if it was created via init() or set()) 756f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org */ 766f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org void reset() { 776f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org if (this->isValid()) { 786f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org fPtr->~T(); 790cbe77c383a1c829341b27df1a9219bc33524440fmalita fPtr = nullptr; 806f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org } 816f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org } 826f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org 836f954b956fc5c36ebbcac404d93ba9349fb0355fcommit-bot@chromium.org /** 848c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com * Returns true if a valid object has been initialized in the SkTLazy, 858c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com * false otherwise. 868c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com */ 8749f085dddff10473b6ebf832a974288300224e60bsalomon bool isValid() const { return SkToBool(fPtr); } 88fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com 89a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com /** 90bb5c46591c50d05418467cd1c4e927ceb85c2ba9commit-bot@chromium.org * Returns the object. This version should only be called when the caller 91bb5c46591c50d05418467cd1c4e927ceb85c2ba9commit-bot@chromium.org * knows that the object has been initialized. 92a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com */ 938c2fe99ed2c210317786683e8c1f1e86cff0be49bsalomon@google.com T* get() const { SkASSERT(this->isValid()); return fPtr; } 9405a2ee052c9ef4c781b7b590b00b3d2da3b3449askia.committer@gmail.com 95bb5c46591c50d05418467cd1c4e927ceb85c2ba9commit-bot@chromium.org /** 96bb5c46591c50d05418467cd1c4e927ceb85c2ba9commit-bot@chromium.org * Like above but doesn't assert if object isn't initialized (in which case 970cbe77c383a1c829341b27df1a9219bc33524440fmalita * nullptr is returned). 98bb5c46591c50d05418467cd1c4e927ceb85c2ba9commit-bot@chromium.org */ 99bb5c46591c50d05418467cd1c4e927ceb85c2ba9commit-bot@chromium.org T* getMaybeNull() const { return fPtr; } 100fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com 101a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.comprivate: 10272440a3785c13b8ec539d7e11bea1124eeddecbdbungeman SkAlignedSTStorage<1, T> fStorage; 103bf783b335cb4d941ac8848a44f6d21912d6eeda2fmalita T* fPtr; // nullptr or fStorage 104a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com}; 105a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com 1065dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com/** 1075dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized 1085dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * with a const pointer but provides a non-const pointer accessor. The first time the 1095dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * accessor is called (if ever) the object is cloned. 1105dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * 1115dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * In the following example at most one copy of constThing is made: 1125dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * 1135dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * SkTCopyOnFirstWrite<Thing> thing(&constThing); 1145dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * ... 1155dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * function_that_takes_a_const_thing_ptr(thing); // constThing is passed 1165dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * ... 1175dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * if (need_to_modify_thing()) { 1185dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * thing.writable()->modifyMe(); // makes a copy of constThing 1195dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * } 1205dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * ... 1215dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * x = thing->readSomething(); 1225dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * ... 1235dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * if (need_to_modify_thing_now()) { 1245dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe() 1255dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * } 1265dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * 1275dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * consume_a_thing(thing); // could be constThing or a modified copy. 1285dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com */ 1295dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.comtemplate <typename T> 1305dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.comclass SkTCopyOnFirstWrite { 1315dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.compublic: 1325dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {} 1335dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com 13432cdc32522bf39a8236880f57ff4ee5b26bdd363fmalita SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {} 13532cdc32522bf39a8236880f57ff4ee5b26bdd363fmalita 13645a15f551b5b3c6c747d8eaf6466b7d3b76a8faebsalomon@google.com // Constructor for delayed initialization. 1370cbe77c383a1c829341b27df1a9219bc33524440fmalita SkTCopyOnFirstWrite() : fObj(nullptr) {} 13845a15f551b5b3c6c747d8eaf6466b7d3b76a8faebsalomon@google.com 13945a15f551b5b3c6c747d8eaf6466b7d3b76a8faebsalomon@google.com // Should only be called once, and only if the default constructor was used. 14045a15f551b5b3c6c747d8eaf6466b7d3b76a8faebsalomon@google.com void init(const T& initial) { 1410cbe77c383a1c829341b27df1a9219bc33524440fmalita SkASSERT(nullptr == fObj); 14245a15f551b5b3c6c747d8eaf6466b7d3b76a8faebsalomon@google.com SkASSERT(!fLazy.isValid()); 14345a15f551b5b3c6c747d8eaf6466b7d3b76a8faebsalomon@google.com fObj = &initial; 14445a15f551b5b3c6c747d8eaf6466b7d3b76a8faebsalomon@google.com } 14545a15f551b5b3c6c747d8eaf6466b7d3b76a8faebsalomon@google.com 1465dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com /** 1475dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * Returns a writable T*. The first time this is called the initial object is cloned. 1485dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com */ 1495dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com T* writable() { 15049f085dddff10473b6ebf832a974288300224e60bsalomon SkASSERT(fObj); 1515dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com if (!fLazy.isValid()) { 1525dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com fLazy.set(*fObj); 1535dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com fObj = fLazy.get(); 1545dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com } 1555dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com return const_cast<T*>(fObj); 1565dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com } 1575dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com 1585dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com /** 1595dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com * Operators for treating this as though it were a const pointer. 1605dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com */ 1615dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com 1625dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com const T *operator->() const { return fObj; } 1635dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com 1645dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com operator const T*() const { return fObj; } 1655dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com 1665dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com const T& operator *() const { return *fObj; } 1675dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com 1685dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.comprivate: 1695dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com const T* fObj; 1705dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com SkTLazy<T> fLazy; 1715dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com}; 1725dc26b97366934ba0f896cea02a3fec027d5d5c1bsalomon@google.com 173a076e9be17654a60310e72c4f70fcd5337f56dbfreed@google.com#endif 174