910e62b5创建于 1月15日历史提交
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef PDF_PDFIUM_PDFIUM_API_STRING_BUFFER_ADAPTER_H_
#define PDF_PDFIUM_PDFIUM_API_STRING_BUFFER_ADAPTER_H_

#include <stddef.h>

#include <optional>
#include <string>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/safe_math.h"

namespace chrome_pdf {

namespace internal {

// Helper to deal with the fact that many PDFium APIs write the null-terminator
// into string buffers that are passed to them, but the PDF code likes to use
// std::strings / std::u16strings, where one should not count on the internal
// string buffers to be null-terminated.
template <class StringType>
class PDFiumAPIStringBufferAdapter {
 public:
  // `str` is the string to write into.
  // `expected_size` is the number of characters the PDFium API will write,
  // including the null-terminator. It should be at least 1.
  // `check_expected_size` whether to check the actual number of characters
  // written into `str` against `expected_size` when calling Close().
  PDFiumAPIStringBufferAdapter(StringType* str,
                               size_t expected_size,
                               bool check_expected_size);
  PDFiumAPIStringBufferAdapter(const PDFiumAPIStringBufferAdapter&) = delete;
  PDFiumAPIStringBufferAdapter& operator=(const PDFiumAPIStringBufferAdapter&) =
      delete;
  ~PDFiumAPIStringBufferAdapter();

  // Returns a pointer to `str_`'s buffer. The buffer's size is large enough to
  // hold `expected_size_` + 1 characters, so the PDFium API that uses the
  // pointer has space to write a null-terminator.
  void* GetData();

  // Resizes `str_` to `actual_size` - 1 characters, thereby removing the extra
  // null-terminator. This must be called prior to the adapter's destruction.
  // The pointer returned by GetData() should be considered invalid.
  void Close(size_t actual_size);

  template <typename IntType>
  void Close(IntType actual_size) {
    Close(base::checked_cast<size_t>(actual_size));
  }

 private:
  const raw_ptr<StringType> str_;
  const raw_ptr<void> data_;
  const size_t expected_size_;
  const bool check_expected_size_;
  bool is_closed_;
};

// Helper to deal with the fact that many PDFium APIs write the null-terminator
// into string buffers that are passed to them, but the PDF code likes to use
// std::strings / std::u16strings, where one should not count on the internal
// string buffers to be null-terminated. This version is suitable for APIs that
// work in terms of number of bytes instead of the number of characters. Though
// for std::strings, PDFiumAPIStringBufferAdapter is equivalent.
class PDFiumAPIStringBufferSizeInBytesAdapter {
 public:
  // `str` is the string to write into.
  // `expected_size` is the number of bytes the PDFium API will write,
  // including the null-terminator. It should be at least the size of a
  // character in bytes.
  // `check_expected_size` whether to check the actual number of bytes
  // written into `str` against `expected_size` when calling Close().
  PDFiumAPIStringBufferSizeInBytesAdapter(std::u16string* str,
                                          size_t expected_size,
                                          bool check_expected_size);
  ~PDFiumAPIStringBufferSizeInBytesAdapter();

  // Returns a pointer to `str_`'s buffer. The buffer's size is large enough to
  // hold `expected_size_` + sizeof(char16_t) bytes, so the PDFium API that
  // uses the pointer has space to write a null-terminator.
  void* GetData();

  // Resizes `str_` to `actual_size` - sizeof(char16_t) bytes, thereby
  // removing the extra null-terminator. This must be called prior to the
  // adapter's destruction. The pointer returned by GetData() should be
  // considered invalid.
  void Close(size_t actual_size);

  template <typename IntType>
  void Close(IntType actual_size) {
    Close(base::checked_cast<size_t>(actual_size));
  }

 private:
  PDFiumAPIStringBufferAdapter<std::u16string> adapter_;
};

template <class AdapterType,
          class StringType,
          typename BufferType,
          typename ReturnType>
std::optional<StringType> CallPDFiumStringBufferApiAndReturnOptional(
    base::RepeatingCallback<ReturnType(BufferType*, ReturnType)> api,
    bool check_expected_size) {
  ReturnType expected_size = api.Run(nullptr, 0);
  if (expected_size == 0)
    return std::nullopt;

  StringType str;
  AdapterType api_string_adapter(&str, expected_size, check_expected_size);
  auto* data = reinterpret_cast<BufferType*>(api_string_adapter.GetData());
  api_string_adapter.Close(api.Run(data, expected_size));
  return str;
}

// Variation of helper function for API endpoints where success of the call is
// returned as a boolean and the length of the necessary buffer is returned in
// an argument.
template <class AdapterType,
          class StringType,
          typename BufferType,
          typename ReturnType>
std::optional<StringType> CallPDFiumStringBufferApiAndReturnOptional(
    base::RepeatingCallback<
        ReturnType(BufferType*, unsigned long, unsigned long*)> api,
    bool check_expected_size) {
  unsigned long expected_size;
  ReturnType success = api.Run(nullptr, 0, &expected_size);
  if (!success || expected_size == 0) {
    return std::nullopt;
  }

  StringType str;
  AdapterType api_string_adapter(&str, expected_size, check_expected_size);
  auto* data = reinterpret_cast<BufferType*>(api_string_adapter.GetData());

  unsigned long actual_size = 0;
  success = api.Run(data, expected_size, &actual_size);
  if (!success) {
    return std::nullopt;
  }

  api_string_adapter.Close(actual_size);
  return str;
}

template <class AdapterType,
          class StringType,
          typename BufferType,
          typename ReturnType>
StringType CallPDFiumStringBufferApi(
    base::RepeatingCallback<ReturnType(BufferType*, ReturnType)> api,
    bool check_expected_size) {
  std::optional<StringType> result =
      CallPDFiumStringBufferApiAndReturnOptional<AdapterType, StringType>(
          api, check_expected_size);
  return result.value_or(StringType());
}

}  // namespace internal

// Helper function to call PDFium APIs where the output buffer is expected to
// hold UTF-16 data, and the buffer length is specified in bytes.
template <typename BufferType>
std::u16string CallPDFiumWideStringBufferApi(
    base::RepeatingCallback<unsigned long(BufferType*, unsigned long)> api,
    bool check_expected_size) {
  using adapter_type = internal::PDFiumAPIStringBufferSizeInBytesAdapter;
  return internal::CallPDFiumStringBufferApi<adapter_type, std::u16string>(
      api, check_expected_size);
}

// Variant of CallPDFiumWideStringBufferApi() that distinguishes between API
// call failures and empty string return values.
template <typename BufferType>
std::optional<std::u16string> CallPDFiumWideStringBufferApiAndReturnOptional(
    base::RepeatingCallback<unsigned long(BufferType*, unsigned long)> api,
    bool check_expected_size) {
  using adapter_type = internal::PDFiumAPIStringBufferSizeInBytesAdapter;
  return internal::CallPDFiumStringBufferApiAndReturnOptional<adapter_type,
                                                              std::u16string>(
      api, check_expected_size);
}

// Helper function to call PDFium APIs where the output buffer is expected to
// hold ASCII or UTF-8 data, and the buffer length is specified in bytes.
template <typename BufferType, typename ReturnType>
std::string CallPDFiumStringBufferApi(
    base::RepeatingCallback<ReturnType(BufferType*, ReturnType)> api,
    bool check_expected_size) {
  using adapter_type = internal::PDFiumAPIStringBufferAdapter<std::string>;
  return internal::CallPDFiumStringBufferApi<adapter_type, std::string>(
      api, check_expected_size);
}

// Helper function to call PDFium APIs where the output buffer is expected to
// hold UTF-16 data, the output buffer length is specified in bytes, and the
// success of the call is returned as a boolean value, such as
// FPDF_StructElement_Attr_GetStringValue.
template <typename BufferType, typename ReturnType>
std::optional<std::u16string> CallPDFiumWideStringBufferApiAndReturnOptional(
    base::RepeatingCallback<
        ReturnType(BufferType*, unsigned long, unsigned long*)> api,
    bool check_expected_size) {
  using adapter_type = internal::PDFiumAPIStringBufferSizeInBytesAdapter;
  return internal::CallPDFiumStringBufferApiAndReturnOptional<
      adapter_type, std::u16string, BufferType, ReturnType>(
      api, check_expected_size);
}

// Helper function to call PDFium APIs where the output buffer is expected to
// hold UTF-8 data, the output buffer length is specified in characters, and the
// success of the call is returned as a boolean value, such as
// FPDF_StructElement_Attr_GetName.
template <typename BufferType, typename ReturnType>
std::optional<std::string> CallPDFiumStringBufferApiAndReturnOptional(
    base::RepeatingCallback<
        ReturnType(BufferType*, unsigned long, unsigned long*)> api,
    bool check_expected_size) {
  using adapter_type = internal::PDFiumAPIStringBufferAdapter<std::string>;
  return internal::CallPDFiumStringBufferApiAndReturnOptional<
      adapter_type, std::string, BufferType, ReturnType>(api,
                                                         check_expected_size);
}

// Expose internal::PDFiumAPIStringBufferAdapter for special cases that cannot
// use the CallPDFiumStringBuffer* functions above.
template <class StringType>
using PDFiumAPIStringBufferAdapter =
    internal::PDFiumAPIStringBufferAdapter<StringType>;

}  // namespace chrome_pdf

#endif  // PDF_PDFIUM_PDFIUM_API_STRING_BUFFER_ADAPTER_H_