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