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

#ifndef BASE_CONTAINERS_AUTO_SPANIFICATION_HELPER_H_
#define BASE_CONTAINERS_AUTO_SPANIFICATION_HELPER_H_

#include <array>

#include "base/containers/span.h"
#include "base/numerics/checked_math.h"

namespace base {

// SpanificationArray{Begin,End,CBegin,CEnd} were introduced temporarily in
// order to help the auto spanification tool (//tools/clang/spanify), and not
// meant to be used widely.
template <typename Element, size_t N>
constexpr span<Element> SpanificationArrayBegin(
    Element (&array LIFETIME_BOUND)[N]) {
  return span(array);
}

template <typename Element, size_t N>
constexpr span<Element> SpanificationArrayEnd(
    Element (&array LIFETIME_BOUND)[N]) {
  return span(array).last(0u);
}

template <typename Element, size_t N>
constexpr span<const Element> SpanificationArrayCBegin(
    const Element (&array LIFETIME_BOUND)[N]) {
  return span(array);
}

template <typename Element, size_t N>
constexpr span<const Element> SpanificationArrayCEnd(
    const Element (&array LIFETIME_BOUND)[N]) {
  return span(array).last(0u);
}

template <typename Element, size_t N>
constexpr span<Element> SpanificationArrayBegin(
    std::array<Element, N>& array LIFETIME_BOUND) {
  return span(array);
}

template <typename Element, size_t N>
constexpr span<Element> SpanificationArrayEnd(
    std::array<Element, N>& array LIFETIME_BOUND) {
  return span(array).last(0u);
}

template <typename Element, size_t N>
constexpr span<const Element> SpanificationArrayCBegin(
    const std::array<Element, N>& array LIFETIME_BOUND) {
  return span(array);
}

template <typename Element, size_t N>
constexpr span<const Element> SpanificationArrayCEnd(
    const std::array<Element, N>& array LIFETIME_BOUND) {
  return span(array).last(0u);
}

// SpanificationSizeofForStdArray was introduced temporarily in order to help
// the auto spanification tool (//tools/clang/spanify), and not meant to be
// used widely.
//
// Note that it's *not* guaranteed by the C++ standard that
//     sizeof(arr) == arr.size() * sizeof(arr[0])
// and it's possible that std::array has additional data and/or padding.
template <typename Element, size_t N>
constexpr size_t SpanificationSizeofForStdArray(const std::array<Element, N>&) {
  return sizeof(Element) * N;
}

// Modifies the input span by removing its first element (if not empty)
// and returns the modified span.
// Used to rewrite pre-increment (++ptr).
// WARNING: This helper is intended to be used only by the auto spanification
// tool. Do not use this helper outside of the tool. Usage should usually be
// replaced with `base::span::(const_)iterator`.
template <typename T>
span<T> PreIncrementSpan(span<T>& span_ref) {
  static_assert(
      span<T>::extent == dynamic_extent,
      "PreIncrementSpan requires a dynamic-extent span (base::span<T>)");
  // An iterator that is at the end is expressed as an empty span and it shall
  // not be incremented.
  CHECK(!span_ref.empty());
  span_ref = span_ref.template subspan<1u>();
  return span_ref;
}

// Returns a copy of the input span *before* modification, and then
// modifies the input span by removing its first element (if not empty).
// Used to rewrite post-increment (ptr++).
// WARNING: This helper is intended to be used only by the auto spanification
// tool. Do not use this helper outside of the tool. Usage should usually be
// replaced with `base::span::(const_)iterator`.
template <typename T>
span<T> PostIncrementSpan(span<T>& span_ref) {
  static_assert(
      span<T>::extent == dynamic_extent,
      "PostIncrementSpan requires a dynamic-extent span (base::span<T>)");
  // An iterator that is at the end is expressed as an empty span and it shall
  // not be incremented.
  CHECK(!span_ref.empty());
  span<T> original_span = span_ref;
  span_ref = span_ref.template subspan<1u>();
  return original_span;
}

}  // namespace base

namespace base::spanification_internal {

// ToPointer is a helper function that converts either of a `T&` or a `T*` to a
// pointer `T*`.
//
// Example) Given the following two cases of spanification rewriting,
//     obj.method(arg...)  ==> MACRO(obj, arg...)
//     ptr->method(arg...) ==> MACRO(ptr, arg...)
// MACRO takes either of an optionally-const T& or T* value as the receiver
// object argument. ToPointer(obj) / ToPointer(ptr) converts them to a pointer
// type value. This helps avoiding implementing two versions of the macro.
//
// Note: This helper is intended to be used only in the following macros. Do not
// use this helper outside of them.

template <typename T>
inline const T* ToPointer(const T* value) {
  return value;
}

template <typename T>
inline T* ToPointer(T* value) {
  return value;
}

template <typename T>
inline const T* ToPointer(const T& value) {
  return &value;
}

template <typename T>
inline T* ToPointer(T& value) {
  return &value;
}

// If the value is a smart pointer type value, returns just the value.

template <typename T>
  requires requires(T t) { t.operator->(); }
inline const T& ToPointer(const T& value) {
  return value;
}

template <typename T>
  requires requires(T t) { t.operator->(); }
inline T& ToPointer(T& value) {
  return value;
}

}  // namespace base::spanification_internal

// The following helper macros are introduced temporarily in order to help the
// auto spanification tool (//tools/clang/spanify). The macros wrap third-party
// API calls which should return a base::span for safety but actually not. In
// the future, these macro calls should be replaced with new spanified APIs.
//
// The helper macros are macros because this header (in base/) cannot depend on
// non-base (especially third-party) libraries. The call sites must include
// necessary headers on their side.
//
// In the following macro definitions, a temporary lambda expression is used in
// order to not evaluate arguments multiple times. It also introduces a C++ code
// block where we can define temporary variables.

// https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/include/core/SkBitmap.h;drc=f72bd467feb15edd9323e46eab1b74ab6025bc5b;l=936
#define UNSAFE_SKBITMAP_GETADDR32(arg_self, arg_x, arg_y) \
  ([](auto&& self, int x, int y) {                        \
    uint32_t* row = self->getAddr32(x, y);                \
    ::base::CheckedNumeric<size_t> width = self->width(); \
    size_t size = (width - x).ValueOrDie();               \
    return UNSAFE_TODO(base::span<uint32_t>(row, size));  \
  }(::base::spanification_internal::ToPointer(arg_self), arg_x, arg_y))

// https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/include/openssl/pool.h;drc=c76e4f83a8c5786b463c3e55c070a21ac751b96b;l=81
#define UNSAFE_CRYPTO_BUFFER_DATA(arg_buf)                    \
  ([](const CRYPTO_BUFFER* buf) {                             \
    const uint8_t* data = CRYPTO_BUFFER_data(buf);            \
    size_t len = CRYPTO_BUFFER_len(buf);                      \
    return UNSAFE_TODO(base::span<const uint8_t>(data, len)); \
  }(arg_buf))

// https://source.chromium.org/chromium/chromium/src/+/main:third_party/harfbuzz-ng/src/src/hb-buffer.h;drc=ea6a172f84f2cbcfed803b5ae71064c7afb6b5c2;l=647
#define UNSAFE_HB_BUFFER_GET_GLYPH_INFOS(arg_buffer, arg_length)     \
  ([](hb_buffer_t* buffer, unsigned int* length) {                   \
    unsigned int len;                                                \
    hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &len); \
    if (length)                                                      \
      *length = len;                                                 \
    return UNSAFE_TODO(base::span<hb_glyph_info_t>(info, len));      \
  }(arg_buffer, arg_length))

// https://source.chromium.org/chromium/chromium/src/+/main:third_party/harfbuzz-ng/src/src/hb-buffer.h;drc=c76e4f83a8c5786b463c3e55c070a21ac751b96b;l=651
#define UNSAFE_HB_BUFFER_GET_GLYPH_POSITIONS(arg_buffer, arg_length)        \
  ([](hb_buffer_t* buffer, unsigned int* length) {                          \
    unsigned int len;                                                       \
    hb_glyph_position_t* pos = hb_buffer_get_glyph_positions(buffer, &len); \
    if (length)                                                             \
      *length = len;                                                        \
    /* It's not clear whether the length is guaranteed to be 0 when !pos.   \
       Explicitly set the length to 0 just in case. */                      \
    if (!pos)                                                               \
      return UNSAFE_TODO(base::span<hb_glyph_position_t>(pos, 0u));         \
    return UNSAFE_TODO(base::span<hb_glyph_position_t>(pos, len));          \
  }(arg_buffer, arg_length))

// https://source.chromium.org/chromium/chromium/src/+/main:remoting/host/xsession_chooser_linux.cc;drc=fca90714b3949f0f4c27f26ef002fe8d33f3cb73;l=274
// https://web.mit.edu/barnowl/share/gtk-doc/html/glib/glib-Miscellaneous-Utility-Functions.html#g-get-system-data-dirs
#define UNSAFE_G_GET_SYSTEM_DATA_DIRS()                             \
  ([]() {                                                           \
    const gchar* const* dirs = g_get_system_data_dirs();            \
    size_t count = 0;                                               \
    while (UNSAFE_TODO(dirs[count]))                                \
      ++count;                                                      \
    /* It's okay to access the null-terminator at the end. */       \
    size_t size = count + 1;                                        \
    return UNSAFE_TODO(base::span<const gchar* const>(dirs, size)); \
  }())

#endif  // BASE_CONTAINERS_AUTO_SPANIFICATION_HELPER_H_