text: /*
   * Copyright 2014 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #ifndef SkCachedData_DEFINED
  #define SkCachedData_DEFINED
  
  #include "include/core/SkTypes.h"
  #include "include/private/SkMutex.h"
  #include "include/private/SkNoncopyable.h"
  
  class SkDiscardableMemory;
  
  class SkCachedData : ::SkNoncopyable {
   public:
    SkCachedData(void* mallocData, size_t size);
    SkCachedData(size_t size, SkDiscardableMemory*);
    virtual ~SkCachedData();
  
    size_t size() const { return fSize; }
    const void* data() const { return fData; }
  
    void* writable_data() { return fData; }
  
    void ref() const { this->internalRef(false); }
    void unref() const { this->internalUnref(false); }
  
    int testing_only_getRefCnt() const { return fRefCnt; }
    bool testing_only_isLocked() const { return fIsLocked; }
    bool testing_only_isInCache() const { return fInCache; }
  
    SkDiscardableMemory* diagnostic_only_getDiscardable() const {
      return kDiscardableMemory_StorageType == fStorageType ? fStorage.fDM : nullptr;
    }
  
   protected:
    // called when fData changes. could be nullptr.
    virtual void onDataChange(void* oldData, void* newData) {}
  
   private:
    SkMutex fMutex;  // could use a pool of these...
  
    enum StorageType { kDiscardableMemory_StorageType, kMalloc_StorageType };
  
    union {
      SkDiscardableMemory* fDM;
      void* fMalloc;
    } fStorage;
    void* fData;
    size_t fSize;
    int fRefCnt;  // low-bit means we're owned by the cache
    StorageType fStorageType;
    bool fInCache;
    bool fIsLocked;
  
    void internalRef(bool fromCache) const;
    void internalUnref(bool fromCache) const;
  
    void inMutexRef(bool fromCache);
    bool inMutexUnref(bool fromCache);  // returns true if we should delete "this"
    void inMutexLock();
    void inMutexUnlock();
  
    // called whenever our fData might change (lock or unlock)
    void setData(void* newData) {
      if (newData != fData) {
        // notify our subclasses of the change
        this->onDataChange(fData, newData);
        fData = newData;
      }
    }
  
    class AutoMutexWritable;
  
   public:
  #ifdef SK_DEBUG
    void validate() const;
  #else
    void validate() const {}
  #endif
  
    /*
     *  Attaching a data to to a SkResourceCache (only one at a time) enables the data to be
     *  unlocked when the cache is the only owner, thus freeing it to be purged (assuming the
     *  data is backed by a SkDiscardableMemory).
     *
     *  When attached, it also automatically attempts to "lock" the data when the first client
     *  ref's the data (typically from a find(key, visitor) call).
     *
     *  Thus the data will always be "locked" when a non-cache has a ref on it (whether or not
     *  the lock succeeded to recover the memory -- check data() to see if it is nullptr).
     */
  
    /*
     *  Call when adding this instance to a SkResourceCache::Rec subclass
     *  (typically in the Rec's constructor).
     */
    void attachToCacheAndRef() const { this->internalRef(true); }
  
    /*
     *  Call when removing this instance from a SkResourceCache::Rec subclass
     *  (typically in the Rec's destructor).
     */
    void detachFromCacheAndUnref() const { this->internalUnref(true); }
  };
  
  #endif
  