// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/paint/paint_typeface_transfer_cache_entry.h"

namespace cc {
namespace {

size_t kMaxFilenameSize = 1024;
size_t kMaxFamilyNameSize = 128;

class DataWriter {
 public:
  explicit DataWriter(base::span<uint8_t> data) : data_(data) {}

  template <typename T>
  void WriteSimple(const T& val) {
    DCHECK_LE(sizeof(T), data_.size());
    *reinterpret_cast<T*>(data_.data()) = val;
    data_ = data_.subspan(sizeof(T));
  }
  void WriteData(size_t bytes, const void* input) {
    DCHECK_LE(bytes, data_.size());
    memcpy(data_.data(), input, bytes);
    data_ = data_.subspan(bytes);
  }

 private:
  base::span<uint8_t> data_;
};

class SizeCounter {
 public:
  SizeCounter() = default;

  template <typename T>
  void WriteSimple(const T& val) {
    size_ += sizeof(T);
  }
  void WriteData(size_t bytes, const void* input) { size_ += bytes; }
  size_t size() const { return size_; }

 private:
  size_t size_ = 0;
};

}  // namespace

ClientPaintTypefaceTransferCacheEntry::ClientPaintTypefaceTransferCacheEntry(
    const PaintTypeface& typeface)
    : typeface_(typeface) {
  SizeCounter counter;
  SerializeInternal(&counter);
  size_ = counter.size();
}

ClientPaintTypefaceTransferCacheEntry::
    ~ClientPaintTypefaceTransferCacheEntry() = default;

uint32_t ClientPaintTypefaceTransferCacheEntry::Id() const {
  return typeface_.sk_id();
}

size_t ClientPaintTypefaceTransferCacheEntry::SerializedSize() const {
  return size_;
}

bool ClientPaintTypefaceTransferCacheEntry::Serialize(
    base::span<uint8_t> data) const {
  DataWriter writer(data);
  return SerializeInternal(&writer);
}

template <typename Writer>
bool ClientPaintTypefaceTransferCacheEntry::SerializeInternal(
    Writer* writer) const {
  writer->WriteSimple(typeface_.sk_id());
  writer->WriteSimple(static_cast<uint8_t>(typeface_.type()));
  switch (typeface_.type()) {
    case PaintTypeface::Type::kTestTypeface:
      // Nothing to serialize here.
      break;
    case PaintTypeface::Type::kSkTypeface:
      // Nothing to do here. This should never be the case when everything is
      // implemented. This should be a NOTREACHED() eventually.
      break;
    case PaintTypeface::Type::kFontConfigInterfaceIdAndTtcIndex:
      writer->WriteSimple(typeface_.font_config_interface_id());
      writer->WriteSimple(typeface_.ttc_index());
      break;
    case PaintTypeface::Type::kFilenameAndTtcIndex:
      writer->template WriteSimple<uint64_t>(typeface_.filename().size());
      writer->WriteData(typeface_.filename().size(),
                        typeface_.filename().data());
      writer->WriteSimple(typeface_.ttc_index());
      break;
    case PaintTypeface::Type::kFamilyNameAndFontStyle:
      writer->template WriteSimple<uint64_t>(typeface_.family_name().size());
      writer->WriteData(typeface_.family_name().size(),
                        typeface_.family_name().data());
      writer->WriteSimple(typeface_.font_style().weight());
      writer->WriteSimple(typeface_.font_style().width());
      writer->WriteSimple(typeface_.font_style().slant());
      break;
  }
  return true;
}

ServicePaintTypefaceTransferCacheEntry::
    ServicePaintTypefaceTransferCacheEntry() = default;
ServicePaintTypefaceTransferCacheEntry::
    ~ServicePaintTypefaceTransferCacheEntry() = default;

size_t ServicePaintTypefaceTransferCacheEntry::CachedSize() const {
  return size_;
}

bool ServicePaintTypefaceTransferCacheEntry::Deserialize(
    GrContext* context,
    base::span<const uint8_t> data) {
  data_ = data;
  size_t initial_size = data_.size();

  SkFontID id;
  uint8_t type;
  ReadSimple(&id);
  ReadSimple(&type);
  if (!valid_ || type > static_cast<uint8_t>(PaintTypeface::Type::kLastType)) {
    valid_ = false;
    return false;
  }
  switch (static_cast<PaintTypeface::Type>(type)) {
    case PaintTypeface::Type::kTestTypeface:
      typeface_ = PaintTypeface::TestTypeface();
      break;
    case PaintTypeface::Type::kSkTypeface:
      // TODO(vmpstr): This shouldn't ever happen once everything is
      // implemented. So this should be a failure (ie |valid_| = false).
      break;
    case PaintTypeface::Type::kFontConfigInterfaceIdAndTtcIndex: {
      int font_config_interface_id = 0;
      int ttc_index = 0;
      ReadSimple(&font_config_interface_id);
      ReadSimple(&ttc_index);
      if (!valid_)
        return false;
      typeface_ = PaintTypeface::FromFontConfigInterfaceIdAndTtcIndex(
          font_config_interface_id, ttc_index);
      break;
    }
    case PaintTypeface::Type::kFilenameAndTtcIndex: {
      size_t size;
      ReadSize(&size);
      if (!valid_ || size > kMaxFilenameSize) {
        valid_ = false;
        return false;
      }

      std::unique_ptr<char[]> buffer(new char[size]);
      ReadData(size, buffer.get());
      std::string filename(buffer.get(), size);

      int ttc_index = 0;
      ReadSimple(&ttc_index);
      if (!valid_)
        return false;
      typeface_ = PaintTypeface::FromFilenameAndTtcIndex(filename, ttc_index);
      break;
    }
    case PaintTypeface::Type::kFamilyNameAndFontStyle: {
      size_t size;
      ReadSize(&size);
      if (!valid_ || size > kMaxFamilyNameSize) {
        valid_ = false;
        return false;
      }

      std::unique_ptr<char[]> buffer(new char[size]);
      ReadData(size, buffer.get());
      std::string family_name(buffer.get(), size);

      int weight = 0;
      int width = 0;
      SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant;
      ReadSimple(&weight);
      ReadSimple(&width);
      ReadSimple(&slant);
      if (!valid_)
        return false;

      typeface_ = PaintTypeface::FromFamilyNameAndFontStyle(
          family_name, SkFontStyle(weight, width, slant));
      break;
    }
  }
  typeface_.SetSkId(id);

  // Set the size to however much data we read.
  size_ = initial_size - data_.size();
  data_ = base::span<uint8_t>();
  return valid_;
}

template <typename T>
void ServicePaintTypefaceTransferCacheEntry::ReadSimple(T* val) {
  if (data_.size() < sizeof(T))
    valid_ = false;
  if (!valid_)
    return;
  *val = *reinterpret_cast<const T*>(data_.data());
  data_ = data_.subspan(sizeof(T));
}

void ServicePaintTypefaceTransferCacheEntry::ReadSize(size_t* size) {
  uint64_t size64;
  ReadSimple(&size64);
  *size = size64;
}

void ServicePaintTypefaceTransferCacheEntry::ReadData(size_t bytes,
                                                      void* data) {
  if (data_.size() < bytes)
    valid_ = false;
  if (!valid_)
    return;
  memcpy(data, data_.data(), bytes);
  data_ = data_.subspan(bytes);
}

}  // namespace cc
