/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/core/SkRemoteGlyphCache.h"

#include <iterator>
#include <memory>
#include <new>
#include <string>
#include <tuple>

#include "src/core/SkDevice.h"
#include "src/core/SkDraw.h"
#include "src/core/SkGlyphRun.h"
#include "src/core/SkStrike.h"
#include "src/core/SkStrikeCache.h"
#include "src/core/SkTLazy.h"
#include "src/core/SkTraceEvent.h"
#include "src/core/SkTypeface_remote.h"

#if SK_SUPPORT_GPU
#include "src/gpu/GrDrawOpAtlas.h"
#include "src/gpu/text/GrTextContext.h"
#endif

static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc,
                                               SkFontID font_id,
                                               SkAutoDescriptor* ad) {
    ad->reset(source_desc->getLength());
    auto* desc = ad->getDesc();
    desc->init();

    // Rec.
    {
        uint32_t size;
        auto ptr = source_desc->findEntry(kRec_SkDescriptorTag, &size);
        SkScalerContextRec rec;
        std::memcpy(&rec, ptr, size);
        rec.fFontID = font_id;
        desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
    }

    // Effects.
    {
        uint32_t size;
        auto ptr = source_desc->findEntry(kEffects_SkDescriptorTag, &size);
        if (ptr) { desc->addEntry(kEffects_SkDescriptorTag, size, ptr); }
    }

    desc->computeChecksum();
    return desc;
}

static const SkDescriptor* create_descriptor(
        const SkPaint& paint, const SkFont& font, const SkMatrix& m,
        const SkSurfaceProps& props, SkScalerContextFlags flags,
        SkAutoDescriptor* ad, SkScalerContextEffects* effects) {
    SkScalerContextRec rec;
    SkScalerContext::MakeRecAndEffects(font, paint, props, flags, m, &rec, effects);
    return SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, *effects, ad);
}

// -- Serializer -----------------------------------------------------------------------------------
size_t pad(size_t size, size_t alignment) { return (size + (alignment - 1)) & ~(alignment - 1); }

// Alignment between x86 and x64 differs for some types, in particular
// int64_t and doubles have 4 and 8-byte alignment, respectively.
// Be consistent even when writing and reading across different architectures.
template<typename T>
size_t serialization_alignment() {
  return sizeof(T) == 8 ? 8 : alignof(T);
}

class Serializer {
public:
    Serializer(std::vector<uint8_t>* buffer) : fBuffer{buffer} { }

    template <typename T, typename... Args>
    T* emplace(Args&&... args) {
        auto result = allocate(sizeof(T), serialization_alignment<T>());
        return new (result) T{std::forward<Args>(args)...};
    }

    template <typename T>
    void write(const T& data) {
        T* result = (T*)allocate(sizeof(T), serialization_alignment<T>());
        memcpy(result, &data, sizeof(T));
    }

    template <typename T>
    T* allocate() {
        T* result = (T*)allocate(sizeof(T), serialization_alignment<T>());
        return result;
    }

    void writeDescriptor(const SkDescriptor& desc) {
        write(desc.getLength());
        auto result = allocate(desc.getLength(), alignof(SkDescriptor));
        memcpy(result, &desc, desc.getLength());
    }

    void* allocate(size_t size, size_t alignment) {
        size_t aligned = pad(fBuffer->size(), alignment);
        fBuffer->resize(aligned + size);
        return &(*fBuffer)[aligned];
    }

private:
    std::vector<uint8_t>* fBuffer;
};

// -- Deserializer -------------------------------------------------------------------------------
// Note that the Deserializer is reading untrusted data, we need to guard against invalid data.
class Deserializer {
public:
    Deserializer(const volatile char* memory, size_t memorySize)
            : fMemory(memory), fMemorySize(memorySize) {}

    template <typename T>
    bool read(T* val) {
        auto* result = this->ensureAtLeast(sizeof(T), serialization_alignment<T>());
        if (!result) return false;

        memcpy(val, const_cast<const char*>(result), sizeof(T));
        return true;
    }

    bool readDescriptor(SkAutoDescriptor* ad) {
        uint32_t descLength = 0u;
        if (!read<uint32_t>(&descLength)) return false;
        if (descLength < sizeof(SkDescriptor)) return false;
        if (descLength != SkAlign4(descLength)) return false;

        auto* result = this->ensureAtLeast(descLength, alignof(SkDescriptor));
        if (!result) return false;

        ad->reset(descLength);
        memcpy(ad->getDesc(), const_cast<const char*>(result), descLength);

        if (ad->getDesc()->getLength() > descLength) return false;
        return ad->getDesc()->isValid();
    }

    const volatile void* read(size_t size, size_t alignment) {
      return this->ensureAtLeast(size, alignment);
    }

    size_t bytesRead() const { return fBytesRead; }

private:
    const volatile char* ensureAtLeast(size_t size, size_t alignment) {
        size_t padded = pad(fBytesRead, alignment);

        // Not enough data.
        if (padded > fMemorySize) return nullptr;
        if (size > fMemorySize - padded) return nullptr;

        auto* result = fMemory + padded;
        fBytesRead = padded + size;
        return result;
    }

    // Note that we read each piece of memory only once to guard against TOCTOU violations.
    const volatile char* fMemory;
    size_t fMemorySize;
    size_t fBytesRead = 0u;
};

// Paths use a SkWriter32 which requires 4 byte alignment.
static const size_t kPathAlignment  = 4u;

size_t SkDescriptorMapOperators::operator()(const SkDescriptor* key) const {
    return key->getChecksum();
}

bool SkDescriptorMapOperators::operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const {
    return *lhs == *rhs;
}

// -- StrikeSpec -----------------------------------------------------------------------------------
struct StrikeSpec {
    StrikeSpec() = default;
    StrikeSpec(SkFontID typefaceID_, SkDiscardableHandleId discardableHandleId_)
            : typefaceID{typefaceID_}, discardableHandleId(discardableHandleId_) {}
    SkFontID typefaceID = 0u;
    SkDiscardableHandleId discardableHandleId = 0u;
    /* desc */
    /* n X (glyphs ids) */
};

// -- SkGlyphCacheState ----------------------------------------------------------------------------
class SkStrikeServer::SkGlyphCacheState : public SkStrikeInterface {
public:
    // N.B. SkGlyphCacheState is not valid until ensureScalerContext is called.
    SkGlyphCacheState(const SkDescriptor& descriptor,
                      std::unique_ptr<SkScalerContext> context,
                      SkDiscardableHandleId discardableHandleId);
    ~SkGlyphCacheState() override;

    void addGlyph(SkPackedGlyphID, bool asPath);
    void writePendingGlyphs(Serializer* serializer);
    SkDiscardableHandleId discardableHandleId() const { return fDiscardableHandleId; }

    bool isSubpixel() const { return fIsSubpixel; }

    const SkDescriptor& getDescriptor() const override {
        return *fDescriptor.getDesc();
    }

    void setTypefaceAndEffects(const SkTypeface* typeface, SkScalerContextEffects effects);

    SkVector rounding() const override;

    SkIPoint subpixelMask() const override {
        return SkIPoint::Make((!fIsSubpixel || fAxisAlignment == kY_SkAxisAlignment) ? 0 : ~0u,
                              (!fIsSubpixel || fAxisAlignment == kX_SkAxisAlignment) ? 0 : ~0u);
    }

    SkSpan<const SkGlyphPos>
    prepareForDrawingRemoveEmpty(
            const SkPackedGlyphID packedGlyphIDs[],
            const SkPoint positions[], size_t n,
            int maxDimension, PreparationDetail detail,
            SkGlyphPos results[]) override;

    void onAboutToExitScope() override {}

private:
    bool hasPendingGlyphs() const {
        return !fPendingGlyphImages.empty() || !fPendingGlyphPaths.empty();
    }
    void writeGlyphPath(const SkPackedGlyphID& glyphID, Serializer* serializer) const;

    void ensureScalerContext();
    void resetScalerContext();

    // The set of glyphs cached on the remote client.
    SkTHashSet<SkPackedGlyphID> fCachedGlyphImages;
    SkTHashSet<SkPackedGlyphID> fCachedGlyphPaths;

    // The set of glyphs which has not yet been serialized and sent to the
    // remote client.
    std::vector<SkPackedGlyphID> fPendingGlyphImages;
    std::vector<SkPackedGlyphID> fPendingGlyphPaths;

    const SkAutoDescriptor fDescriptor;

    const SkDiscardableHandleId fDiscardableHandleId;

    // Values saved from the initial context.
    const bool fIsSubpixel;
    const SkAxisAlignment fAxisAlignment;

    // The context built using fDescriptor
    std::unique_ptr<SkScalerContext> fContext;

    // These fields are set every time getOrCreateCache. This allows the code to maintain the
    // fContext as lazy as possible.
    const SkTypeface* fTypeface{nullptr};
    SkScalerContextEffects fEffects;

    class GlyphMapHashTraits {
    public:
        static SkPackedGlyphID GetKey(const SkGlyph* glyph) {
            return glyph->getPackedID();
        }
        static uint32_t Hash(SkPackedGlyphID glyphId) {
            return glyphId.hash();
        }
    };

    // FallbackTextHelper cases require glyph metrics when analyzing a glyph run, in which case
    // we cache them here.
    SkTHashTable<SkGlyph*, SkPackedGlyphID, GlyphMapHashTraits> fGlyphMap;

    SkArenaAlloc fAlloc{256};
};

SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState(
        const SkDescriptor& descriptor,
        std::unique_ptr<SkScalerContext> context,
        uint32_t discardableHandleId)
        : fDescriptor{descriptor}
        , fDiscardableHandleId(discardableHandleId)
        , fIsSubpixel{context->isSubpixel()}
        , fAxisAlignment{context->computeAxisAlignmentForHText()}
        // N.B. context must come last because it is used above.
        , fContext{std::move(context)} {
    SkASSERT(fDescriptor.getDesc() != nullptr);
    SkASSERT(fContext != nullptr);
}

SkStrikeServer::SkGlyphCacheState::~SkGlyphCacheState() = default;

void SkStrikeServer::SkGlyphCacheState::addGlyph(SkPackedGlyphID glyph, bool asPath) {
    auto* cache = asPath ? &fCachedGlyphPaths : &fCachedGlyphImages;
    auto* pending = asPath ? &fPendingGlyphPaths : &fPendingGlyphImages;

    // Already cached.
    if (cache->contains(glyph)) {
        return;
    }

    // A glyph is going to be sent. Make sure we have a scaler context to send it.
    this->ensureScalerContext();

    // Serialize and cache. Also create the scalar context to use when serializing
    // this glyph.
    cache->add(glyph);
    pending->push_back(glyph);
}


// -- TrackLayerDevice -----------------------------------------------------------------------------
class SkTextBlobCacheDiffCanvas::TrackLayerDevice final : public SkNoPixelsDevice {
public:
    TrackLayerDevice(
            const SkIRect& bounds, const SkSurfaceProps& props, SkStrikeServer* server,
            sk_sp<SkColorSpace> colorSpace, bool DFTSupport)
            : SkNoPixelsDevice(bounds, props, std::move(colorSpace))
            , fStrikeServer(server)
            , fDFTSupport(DFTSupport)
            , fPainter{props, kUnknown_SkColorType, imageInfo().colorSpace(), fStrikeServer} {
        SkASSERT(fStrikeServer != nullptr);
    }

    SkBaseDevice* onCreateDevice(const CreateInfo& cinfo, const SkPaint*) override {
        const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry);
        return new TrackLayerDevice(this->getGlobalBounds(), surfaceProps, fStrikeServer,
                                    cinfo.fInfo.refColorSpace(), fDFTSupport);
    }

protected:
    void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override {
#if SK_SUPPORT_GPU
        GrTextContext::Options options;
        GrTextContext::SanitizeOptions(&options);

        fPainter.processGlyphRunList(glyphRunList,
                                     this->ctm(),
                                     this->surfaceProps(),
                                     fDFTSupport,
                                     options,
                                     nullptr);
#endif  // SK_SUPPORT_GPU
    }

private:
    SkStrikeServer* const fStrikeServer;
    const bool fDFTSupport{false};
    SkGlyphRunListPainter fPainter;
};

// -- SkTextBlobCacheDiffCanvas -------------------------------------------------------------------
// DEPRECATED
// TODO(herb): remove uses in Chrome
SkTextBlobCacheDiffCanvas::Settings::Settings() = default;

SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas(int width, int height,
                                                     const SkSurfaceProps& props,
                                                     SkStrikeServer* strikeServer,
                                                     bool DFTSupport)
    : SkTextBlobCacheDiffCanvas{width, height, props, strikeServer, nullptr, DFTSupport} { }

SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas(int width, int height,
                                                     const SkSurfaceProps& props,
                                                     SkStrikeServer* strikeServer,
                                                     sk_sp<SkColorSpace> colorSpace,
                                                     Settings settings)
    : SkTextBlobCacheDiffCanvas{width, height, props, strikeServer, std::move(colorSpace),
                                settings.fContextSupportsDistanceFieldText} { }

SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas(int width, int height,
                                                     const SkSurfaceProps& props,
                                                     SkStrikeServer* strikeServer,
                                                     sk_sp<SkColorSpace> colorSpace,
                                                     bool DFTSupport)
     : SkNoDrawCanvas{sk_make_sp<TrackLayerDevice>(SkIRect::MakeWH(width, height),
                                                   props,
                                                   strikeServer,
                                                   std::move(colorSpace),
                                                   DFTSupport)} { }

SkTextBlobCacheDiffCanvas::~SkTextBlobCacheDiffCanvas() = default;

SkCanvas::SaveLayerStrategy SkTextBlobCacheDiffCanvas::getSaveLayerStrategy(
        const SaveLayerRec& rec) {
    return kFullLayer_SaveLayerStrategy;
}

bool SkTextBlobCacheDiffCanvas::onDoSaveBehind(const SkRect*) {
    return false;
}

void SkTextBlobCacheDiffCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                               const SkPaint& paint) {
    SkCanvas::onDrawTextBlob(blob, x, y, paint);
}

// -- WireTypeface ---------------------------------------------------------------------------------
struct WireTypeface {
    WireTypeface() = default;
    WireTypeface(SkFontID typeface_id, int glyph_count, SkFontStyle style, bool is_fixed)
            : typefaceID(typeface_id), glyphCount(glyph_count), style(style), isFixed(is_fixed) {}

    SkFontID        typefaceID{0};
    int             glyphCount{0};
    SkFontStyle     style;
    bool            isFixed{false};
};

// SkStrikeServer ----------------------------------------------------------------------------------
SkStrikeServer::SkStrikeServer(DiscardableHandleManager* discardableHandleManager)
        : fDiscardableHandleManager(discardableHandleManager) {
    SkASSERT(fDiscardableHandleManager);
}

SkStrikeServer::~SkStrikeServer() = default;

sk_sp<SkData> SkStrikeServer::serializeTypeface(SkTypeface* tf) {
    auto* data = fSerializedTypefaces.find(SkTypeface::UniqueID(tf));
    if (data) {
        return *data;
    }

    WireTypeface wire(SkTypeface::UniqueID(tf), tf->countGlyphs(), tf->fontStyle(),
                      tf->isFixedPitch());
    data = fSerializedTypefaces.set(SkTypeface::UniqueID(tf),
                                    SkData::MakeWithCopy(&wire, sizeof(wire)));
    return *data;
}

void SkStrikeServer::writeStrikeData(std::vector<uint8_t>* memory) {
    if (fLockedDescs.empty() && fTypefacesToSend.empty()) {
        return;
    }

    Serializer serializer(memory);
    serializer.emplace<uint64_t>(fTypefacesToSend.size());
    for (const auto& tf : fTypefacesToSend) serializer.write<WireTypeface>(tf);
    fTypefacesToSend.clear();

    serializer.emplace<uint64_t>(fLockedDescs.size());
    for (const auto* desc : fLockedDescs) {
        auto it = fRemoteGlyphStateMap.find(desc);
        SkASSERT(it != fRemoteGlyphStateMap.end());
        it->second->writePendingGlyphs(&serializer);
    }
    fLockedDescs.clear();
}

SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache(
        const SkPaint& paint,
        const SkFont& font,
        const SkSurfaceProps& props,
        const SkMatrix& matrix,
        SkScalerContextFlags flags,
        SkScalerContextEffects* effects) {
    SkAutoDescriptor descStorage;
    auto desc = create_descriptor(paint, font, matrix, props, flags, &descStorage, effects);

    return this->getOrCreateCache(*desc, *font.getTypefaceOrDefault(), *effects);

}

SkScopedStrike SkStrikeServer::findOrCreateScopedStrike(const SkDescriptor& desc,
                                                        const SkScalerContextEffects& effects,
                                                        const SkTypeface& typeface) {
    return SkScopedStrike{this->getOrCreateCache(desc, typeface, effects)};
}

void SkStrikeServer::AddGlyphForTesting(
        SkGlyphCacheState* cache, SkPackedGlyphID glyphID, bool asPath) {
    cache->addGlyph(glyphID, asPath);
}

void SkStrikeServer::checkForDeletedEntries() {
    auto it = fRemoteGlyphStateMap.begin();
    while (fRemoteGlyphStateMap.size() > fMaxEntriesInDescriptorMap &&
           it != fRemoteGlyphStateMap.end()) {
        if (fDiscardableHandleManager->isHandleDeleted(it->second->discardableHandleId())) {
            it = fRemoteGlyphStateMap.erase(it);
        } else {
            ++it;
        }
    }
}

SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache(
        const SkDescriptor& desc, const SkTypeface& typeface, SkScalerContextEffects effects) {

    // In cases where tracing is turned off, make sure not to get an unused function warning.
    // Lambdaize the function.
    TRACE_EVENT1("skia", "RecForDesc", "rec",
            TRACE_STR_COPY(
                    [&desc](){
                        auto ptr = desc.findEntry(kRec_SkDescriptorTag, nullptr);
                        SkScalerContextRec rec;
                        std::memcpy(&rec, ptr, sizeof(rec));
                        return rec.dump();
                    }().c_str()
            )
    );

    // Already locked.
    if (fLockedDescs.find(&desc) != fLockedDescs.end()) {
        auto it = fRemoteGlyphStateMap.find(&desc);
        SkASSERT(it != fRemoteGlyphStateMap.end());
        SkGlyphCacheState* cache = it->second.get();
        cache->setTypefaceAndEffects(&typeface, effects);
        return cache;
    }

    // Try to lock.
    auto it = fRemoteGlyphStateMap.find(&desc);
    if (it != fRemoteGlyphStateMap.end()) {
        SkGlyphCacheState* cache = it->second.get();
        bool locked = fDiscardableHandleManager->lockHandle(it->second->discardableHandleId());
        if (locked) {
            fLockedDescs.insert(it->first);
            cache->setTypefaceAndEffects(&typeface, effects);
            return cache;
        }

        // If the lock failed, the entry was deleted on the client. Remove our
        // tracking.
        fRemoteGlyphStateMap.erase(it);
    }

    const SkFontID typefaceId = typeface.uniqueID();
    if (!fCachedTypefaces.contains(typefaceId)) {
        fCachedTypefaces.add(typefaceId);
        fTypefacesToSend.emplace_back(typefaceId, typeface.countGlyphs(),
                                      typeface.fontStyle(),
                                      typeface.isFixedPitch());
    }

    auto context = typeface.createScalerContext(effects, &desc);

    // Create a new cache state and insert it into the map.
    auto newHandle = fDiscardableHandleManager->createHandle();
    auto cacheState = skstd::make_unique<SkGlyphCacheState>(desc, std::move(context), newHandle);

    auto* cacheStatePtr = cacheState.get();

    fLockedDescs.insert(&cacheStatePtr->getDescriptor());
    fRemoteGlyphStateMap[&cacheStatePtr->getDescriptor()] = std::move(cacheState);

    checkForDeletedEntries();

    cacheStatePtr->setTypefaceAndEffects(&typeface, effects);
    return cacheStatePtr;
}

// No need to write fForceBW because it is a flag private to SkScalerContext_DW, which will never
// be called on the GPU side.
static void writeGlyph(SkGlyph* glyph, Serializer* serializer) {
    serializer->write<SkPackedGlyphID>(glyph->getPackedID());
    serializer->write<float>(glyph->advanceX());
    serializer->write<float>(glyph->advanceY());
    serializer->write<uint16_t>(glyph->width());
    serializer->write<uint16_t>(glyph->height());
    serializer->write<int16_t>(glyph->top());
    serializer->write<int16_t>(glyph->left());
    serializer->write<uint8_t>(glyph->maskFormat());
}

void SkStrikeServer::SkGlyphCacheState::writePendingGlyphs(Serializer* serializer) {
    // TODO(khushalsagar): Write a strike only if it has any pending glyphs.
    serializer->emplace<bool>(this->hasPendingGlyphs());
    if (!this->hasPendingGlyphs()) {
        this->resetScalerContext();
        return;
    }

    // Write the desc.
    serializer->emplace<StrikeSpec>(fContext->getTypeface()->uniqueID(), fDiscardableHandleId);
    serializer->writeDescriptor(*fDescriptor.getDesc());

    // Write FontMetrics.
    // TODO(khushalsagar): Do we need to re-send each time?
    SkFontMetrics fontMetrics;
    fContext->getFontMetrics(&fontMetrics);
    serializer->write<SkFontMetrics>(fontMetrics);

    // Write glyphs images.
    serializer->emplace<uint64_t>(fPendingGlyphImages.size());
    for (const auto& glyphID : fPendingGlyphImages) {
        SkGlyph glyph{glyphID};
        fContext->getMetrics(&glyph);
        SkASSERT(SkMask::IsValidFormat(glyph.fMaskFormat));

        writeGlyph(&glyph, serializer);
        auto imageSize = glyph.imageSize();
        if (imageSize == 0u) continue;

        glyph.fImage = serializer->allocate(imageSize, glyph.formatAlignment());
        fContext->getImage(glyph);
        // TODO: Generating the image can change the mask format, do we need to update it in the
        // serialized glyph?
    }
    fPendingGlyphImages.clear();

    // Write glyphs paths.
    serializer->emplace<uint64_t>(fPendingGlyphPaths.size());
    for (const auto& glyphID : fPendingGlyphPaths) {
        SkGlyph glyph{glyphID};
        fContext->getMetrics(&glyph);
        SkASSERT(SkMask::IsValidFormat(glyph.fMaskFormat));

        writeGlyph(&glyph, serializer);
        writeGlyphPath(glyphID, serializer);
    }
    fPendingGlyphPaths.clear();
    this->resetScalerContext();
}

void SkStrikeServer::SkGlyphCacheState::ensureScalerContext() {
    if (fContext == nullptr) {
        fContext = fTypeface->createScalerContext(fEffects, fDescriptor.getDesc());
    }
}

void SkStrikeServer::SkGlyphCacheState::resetScalerContext() {
    fContext.reset();
    fTypeface = nullptr;
}

void SkStrikeServer::SkGlyphCacheState::setTypefaceAndEffects(
        const SkTypeface* typeface, SkScalerContextEffects effects) {
    fTypeface = typeface;
    fEffects = effects;
}

SkVector SkStrikeServer::SkGlyphCacheState::rounding() const {
    return SkStrikeCommon::PixelRounding(fIsSubpixel, fAxisAlignment);
}

void SkStrikeServer::SkGlyphCacheState::writeGlyphPath(const SkPackedGlyphID& glyphID,
                                                       Serializer* serializer) const {
    SkPath path;
    if (!fContext->getPath(glyphID, &path)) {
        serializer->write<uint64_t>(0u);
        return;
    }

    size_t pathSize = path.writeToMemory(nullptr);
    serializer->write<uint64_t>(pathSize);
    path.writeToMemory(serializer->allocate(pathSize, kPathAlignment));
}


// Be sure to read and understand the comment for prepareForDrawingRemoveEmpty in
// SkStrikeInterface.h before working on this code.
SkSpan<const SkGlyphPos>
SkStrikeServer::SkGlyphCacheState::prepareForDrawingRemoveEmpty(
        const SkPackedGlyphID packedGlyphIDs[],
        const SkPoint positions[], size_t n,
        int maxDimension, PreparationDetail detail,
        SkGlyphPos results[]) {
    size_t drawableGlyphCount = 0;
    for (size_t i = 0; i < n; i++) {
        SkPoint glyphPos = positions[i];

        // Check the cache for the glyph.
        SkGlyph* glyphPtr = fGlyphMap.findOrNull(packedGlyphIDs[i]);

        // Has this glyph ever been seen before?
        if (glyphPtr == nullptr) {

            // Never seen before. Make a new glyph.
            glyphPtr = fAlloc.make<SkGlyph>(packedGlyphIDs[i]);
            fGlyphMap.set(glyphPtr);
            this->ensureScalerContext();
            fContext->getMetrics(glyphPtr);

            if (glyphPtr->maxDimension() <= maxDimension) {
                // do nothing
            } else if (!glyphPtr->isColor()) {
                // The glyph is too big for the atlas, but it is not color, so it is handled
                // with a path.
                if (glyphPtr->setPath(&fAlloc, fContext.get())) {
                    // Always send the path data, even if its not available, to make sure empty
                    // paths are not incorrectly assumed to be cache misses.
                    fCachedGlyphPaths.add(glyphPtr->getPackedID());
                    fPendingGlyphPaths.push_back(glyphPtr->getPackedID());
                }
            } else {
                // This will be handled by the fallback strike.
                SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor());
            }

            // Make sure to send the glyph to the GPU because we always send the image for a glyph.
            fCachedGlyphImages.add(packedGlyphIDs[i]);
            fPendingGlyphImages.push_back(packedGlyphIDs[i]);
        }

        // Each non-empty glyph needs to be added as per the contract for
        // prepareForDrawingRemoveEmpty.
        // TODO(herb): Change the code to only send the glyphs for fallback?
        if (!glyphPtr->isEmpty()) {
            results[drawableGlyphCount++] = {i, glyphPtr, glyphPos};
        }
    }
    return SkMakeSpan(results, drawableGlyphCount);
}

// SkStrikeClient ----------------------------------------------------------------------------------
class SkStrikeClient::DiscardableStrikePinner : public SkStrikePinner {
public:
    DiscardableStrikePinner(SkDiscardableHandleId discardableHandleId,
                            sk_sp<DiscardableHandleManager> manager)
            : fDiscardableHandleId(discardableHandleId), fManager(std::move(manager)) {}

    ~DiscardableStrikePinner() override = default;
    bool canDelete() override { return fManager->deleteHandle(fDiscardableHandleId); }

private:
    const SkDiscardableHandleId fDiscardableHandleId;
    sk_sp<DiscardableHandleManager> fManager;
};

SkStrikeClient::SkStrikeClient(sk_sp<DiscardableHandleManager> discardableManager,
                               bool isLogging,
                               SkStrikeCache* strikeCache)
        : fDiscardableHandleManager(std::move(discardableManager))
        , fStrikeCache{strikeCache ? strikeCache : SkStrikeCache::GlobalStrikeCache()}
        , fIsLogging{isLogging} {}

SkStrikeClient::~SkStrikeClient() = default;

#define READ_FAILURE                                                     \
    {                                                                    \
        SkDebugf("Bad font data serialization line: %d", __LINE__);      \
        DiscardableHandleManager::ReadFailureData data = {               \
                memorySize,  deserializer.bytesRead(), typefaceSize,     \
                strikeCount, glyphImagesCount,         glyphPathsCount}; \
        fDiscardableHandleManager->notifyReadFailure(data);              \
        return false;                                                    \
    }

// No need to read fForceBW because it is a flag private to SkScalerContext_DW, which will never
// be called on the GPU side.
bool SkStrikeClient::ReadGlyph(SkTLazy<SkGlyph>& glyph, Deserializer* deserializer) {
    SkPackedGlyphID glyphID;
    if (!deserializer->read<SkPackedGlyphID>(&glyphID)) return false;
    glyph.init(glyphID);
    if (!deserializer->read<float>(&glyph->fAdvanceX)) return false;
    if (!deserializer->read<float>(&glyph->fAdvanceY)) return false;
    if (!deserializer->read<uint16_t>(&glyph->fWidth)) return false;
    if (!deserializer->read<uint16_t>(&glyph->fHeight)) return false;
    if (!deserializer->read<int16_t>(&glyph->fTop)) return false;
    if (!deserializer->read<int16_t>(&glyph->fLeft)) return false;
    if (!deserializer->read<uint8_t>(&glyph->fMaskFormat)) return false;
    if (!SkMask::IsValidFormat(glyph->fMaskFormat)) return false;

    return true;
}

bool SkStrikeClient::readStrikeData(const volatile void* memory, size_t memorySize) {
    SkASSERT(memorySize != 0u);
    Deserializer deserializer(static_cast<const volatile char*>(memory), memorySize);

    uint64_t typefaceSize = 0u;
    uint64_t strikeCount = 0u;
    uint64_t glyphImagesCount = 0u;
    uint64_t glyphPathsCount = 0u;

    if (!deserializer.read<uint64_t>(&typefaceSize)) READ_FAILURE

    for (size_t i = 0; i < typefaceSize; ++i) {
        WireTypeface wire;
        if (!deserializer.read<WireTypeface>(&wire)) READ_FAILURE

        // TODO(khushalsagar): The typeface no longer needs a reference to the
        // SkStrikeClient, since all needed glyphs must have been pushed before
        // raster.
        addTypeface(wire);
    }

    if (!deserializer.read<uint64_t>(&strikeCount)) READ_FAILURE

    for (size_t i = 0; i < strikeCount; ++i) {
        bool has_glyphs = false;
        if (!deserializer.read<bool>(&has_glyphs)) READ_FAILURE

        if (!has_glyphs) continue;

        StrikeSpec spec;
        if (!deserializer.read<StrikeSpec>(&spec)) READ_FAILURE

        SkAutoDescriptor sourceAd;
        if (!deserializer.readDescriptor(&sourceAd)) READ_FAILURE

        SkFontMetrics fontMetrics;
        if (!deserializer.read<SkFontMetrics>(&fontMetrics)) READ_FAILURE

        // Get the local typeface from remote fontID.
        auto* tfPtr = fRemoteFontIdToTypeface.find(spec.typefaceID);
        // Received strikes for a typeface which doesn't exist.
        if (!tfPtr) READ_FAILURE
        auto* tf = tfPtr->get();

        // Replace the ContextRec in the desc from the server to create the client
        // side descriptor.
        // TODO: Can we do this in-place and re-compute checksum? Instead of a complete copy.
        SkAutoDescriptor ad;
        auto* client_desc = auto_descriptor_from_desc(sourceAd.getDesc(), tf->uniqueID(), &ad);

        auto strike = fStrikeCache->findStrikeExclusive(*client_desc);
        if (strike == nullptr) {
            // Note that we don't need to deserialize the effects since we won't be generating any
            // glyphs here anyway, and the desc is still correct since it includes the serialized
            // effects.
            SkScalerContextEffects effects;
            auto scaler = SkStrikeCache::CreateScalerContext(*client_desc, effects, *tf);
            strike = fStrikeCache->createStrikeExclusive(
                    *client_desc, std::move(scaler), &fontMetrics,
                    skstd::make_unique<DiscardableStrikePinner>(spec.discardableHandleId,
                                                                fDiscardableHandleManager));
            auto proxyContext = static_cast<SkScalerContextProxy*>(strike->getScalerContext());
            proxyContext->initCache(strike.get(), fStrikeCache);
        }

        if (!deserializer.read<uint64_t>(&glyphImagesCount)) READ_FAILURE
        for (size_t j = 0; j < glyphImagesCount; j++) {
            SkTLazy<SkGlyph> glyph;
            if (!ReadGlyph(glyph, &deserializer)) READ_FAILURE

            if (!glyph->isEmpty()) {
                const volatile void* image =
                        deserializer.read(glyph->imageSize(), glyph->formatAlignment());
                if (!image) READ_FAILURE
                glyph->fImage = (void*)image;
            }

            strike->mergeGlyphAndImage(glyph->getPackedID(), *glyph);
        }

        if (!deserializer.read<uint64_t>(&glyphPathsCount)) READ_FAILURE
        for (size_t j = 0; j < glyphPathsCount; j++) {
            SkTLazy<SkGlyph> glyph;
            if (!ReadGlyph(glyph, &deserializer)) READ_FAILURE

            SkGlyph* allocatedGlyph = strike->mergeGlyphAndImage(glyph->getPackedID(), *glyph);

            SkPath* pathPtr = nullptr;
            SkPath path;
            uint64_t pathSize = 0u;
            if (!deserializer.read<uint64_t>(&pathSize)) READ_FAILURE

            if (pathSize > 0) {
                auto* pathData = deserializer.read(pathSize, kPathAlignment);
                if (!pathData) READ_FAILURE
                if (!path.readFromMemory(const_cast<const void*>(pathData), pathSize)) READ_FAILURE
                pathPtr = &path;
            }

            strike->preparePath(allocatedGlyph, pathPtr);
        }
    }

    return true;
}

sk_sp<SkTypeface> SkStrikeClient::deserializeTypeface(const void* buf, size_t len) {
    WireTypeface wire;
    if (len != sizeof(wire)) return nullptr;
    memcpy(&wire, buf, sizeof(wire));
    return this->addTypeface(wire);
}

sk_sp<SkTypeface> SkStrikeClient::addTypeface(const WireTypeface& wire) {
    auto* typeface = fRemoteFontIdToTypeface.find(wire.typefaceID);
    if (typeface) return *typeface;

    auto newTypeface = sk_make_sp<SkTypefaceProxy>(
            wire.typefaceID, wire.glyphCount, wire.style, wire.isFixed,
            fDiscardableHandleManager, fIsLogging);
    fRemoteFontIdToTypeface.set(wire.typefaceID, newTypeface);
    return newTypeface;
}