text: /*
   * Copyright 2006 The Android Open Source Project
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  
  #ifndef SkDescriptor_DEFINED
  #define SkDescriptor_DEFINED
  
  #include "SkChecksum.h"
  #include "SkTypes.h"
  
  class SkDescriptor : SkNoncopyable {
  public:
      static size_t ComputeOverhead(int entryCount) {
          SkASSERT(entryCount >= 0);
          return sizeof(SkDescriptor) + entryCount * sizeof(Entry);
      }
  
      static SkDescriptor* Alloc(size_t length) {
          SkASSERT(SkAlign4(length) == length);
          SkDescriptor* desc = (SkDescriptor*)sk_malloc_throw(length);
          return desc;
      }
  
      static void Free(SkDescriptor* desc) {
          sk_free(desc);
      }
  
      void init() {
          fLength = sizeof(SkDescriptor);
          fCount  = 0;
      }
  
      uint32_t getLength() const { return fLength; }
  
      void* addEntry(uint32_t tag, size_t length, const void* data = nullptr) {
          SkASSERT(tag);
          SkASSERT(SkAlign4(length) == length);
          SkASSERT(this->findEntry(tag, nullptr) == nullptr);
  
          Entry* entry = (Entry*)((char*)this + fLength);
          entry->fTag = tag;
          entry->fLen = SkToU32(length);
          if (data) {
              memcpy(entry + 1, data, length);
          }
  
          fCount += 1;
          fLength = SkToU32(fLength + sizeof(Entry) + length);
          return (entry + 1); // return its data
      }
  
      void computeChecksum() {
          fChecksum = SkDescriptor::ComputeChecksum(this);
      }
  
  #ifdef SK_DEBUG
      void assertChecksum() const {
          SkASSERT(SkDescriptor::ComputeChecksum(this) == fChecksum);
      }
  #endif
  
      const void* findEntry(uint32_t tag, uint32_t* length) const {
          const Entry* entry = (const Entry*)(this + 1);
          int          count = fCount;
  
          while (--count >= 0) {
              if (entry->fTag == tag) {
                  if (length) {
                      *length = entry->fLen;
                  }
                  return entry + 1;
              }
              entry = (const Entry*)((const char*)(entry + 1) + entry->fLen);
          }
          return nullptr;
      }
  
      SkDescriptor* copy() const {
          SkDescriptor* desc = SkDescriptor::Alloc(fLength);
          memcpy(desc, this, fLength);
          return desc;
      }
  
      bool equals(const SkDescriptor& other) const {
          // probe to see if we have a good checksum algo
  //        SkASSERT(a.fChecksum != b.fChecksum || memcmp(&a, &b, a.fLength) == 0);
  
          // the first value we should look at is the checksum, so this loop
          // should terminate early if they descriptors are different.
          // NOTE: if we wrote a sentinel value at the end of each, we chould
          //       remove the aa < stop test in the loop...
          const uint32_t* aa = (const uint32_t*)this;
          const uint32_t* bb = (const uint32_t*)&other;
          const uint32_t* stop = (const uint32_t*)((const char*)aa + fLength);
          do {
              if (*aa++ != *bb++)
                  return false;
          } while (aa < stop);
          return true;
      }
  
      uint32_t getChecksum() const { return fChecksum; }
  
      struct Entry {
          uint32_t fTag;
          uint32_t fLen;
      };
  
  #ifdef SK_DEBUG
      uint32_t getCount() const { return fCount; }
  #endif
  
  private:
      uint32_t fChecksum;  // must be first
      uint32_t fLength;    // must be second
      uint32_t fCount;
  
      static uint32_t ComputeChecksum(const SkDescriptor* desc) {
          const uint32_t* ptr = (const uint32_t*)desc + 1; // skip the checksum field
          size_t len = desc->fLength - sizeof(uint32_t);
          return SkChecksum::Murmur3(ptr, len);
      }
  
      // private so no one can create one except our factories
      SkDescriptor() {}
  };
  
  #include "SkScalerContext.h"
  
  class SkAutoDescriptor : SkNoncopyable {
  public:
      SkAutoDescriptor() : fDesc(nullptr) {}
      SkAutoDescriptor(size_t size) : fDesc(nullptr) { this->reset(size); }
      SkAutoDescriptor(const SkDescriptor& desc) : fDesc(nullptr) {
          size_t size = desc.getLength();
          this->reset(size);
          memcpy(fDesc, &desc, size);
      }
  
      ~SkAutoDescriptor() { this->free(); }
  
      void reset(size_t size) {
          this->free();
          if (size <= sizeof(fStorage)) {
              fDesc = (SkDescriptor*)(void*)fStorage;
          } else {
              fDesc = SkDescriptor::Alloc(size);
          }
      }
  
      SkDescriptor* getDesc() const { SkASSERT(fDesc); return fDesc; }
  private:
      void free() {
          if (fDesc != (SkDescriptor*)(void*)fStorage) {
              SkDescriptor::Free(fDesc);
          }
      }
  
      enum {
          kStorageSize =  sizeof(SkDescriptor)
                          + sizeof(SkDescriptor::Entry) + sizeof(SkScalerContext::Rec)    // for rec
                          + sizeof(SkDescriptor::Entry) + sizeof(void*)                   // for typeface
                          + 32   // slop for occational small extras
      };
      SkDescriptor*   fDesc;
      uint32_t        fStorage[(kStorageSize + 3) >> 2];
  };
  #define SkAutoDescriptor(...) SK_REQUIRE_LOCAL_VAR(SkAutoDescriptor)
  
  
  #endif
  