// Copyright 2025 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: resize_and_overwrite.h
// -----------------------------------------------------------------------------
//
// This file contains a polyfill for C++23's
// std::basic_string<CharT,Traits,Allocator>::resize_and_overwrite
//
// The polyfill takes the form of a free function:

// template<typename T, typename Op>
// void StringResizeAndOverwrite(T& str, typename T::size_type count, Op op);
//
// This avoids the cost of initializing a suitably-sized std::string when it is
// intended to be used as a char array, for example, to be populated by a
// C-style API.
//
// Example usage:
//
// std::string IntToString(int n) {
//   std::string result;
//   constexpr size_t kMaxIntChars = 10;
//   absl::StringResizeAndOverwrite(
//       result, kMaxIntChars, [n](char* buffer, size_t buffer_size) {
//         return snprintf(buffer, buffer_size, "%d", n);
//       });
//   return result;
//  }
//
// https://en.cppreference.com/w/cpp/string/basic_string/resize_and_overwrite.html
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1072r10.html

#ifndef ABSL_STRINGS_RESIZE_AND_OVERWRITE_H_
#define ABSL_STRINGS_RESIZE_AND_OVERWRITE_H_

#include <cstddef>
#include <string>  // IWYU pragma: keep
#include <type_traits>
#include <utility>

#include "absl/base/config.h"
#include "absl/base/dynamic_annotations.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h"

#if defined(__cpp_lib_string_resize_and_overwrite) && \
    __cpp_lib_string_resize_and_overwrite >= 202110L
#define ABSL_INTERNAL_HAS_RESIZE_AND_OVERWRITE 1
#endif

namespace absl {
ABSL_NAMESPACE_BEGIN

namespace strings_internal {

#ifndef ABSL_INTERNAL_HAS_RESIZE_AND_OVERWRITE

inline size_t ProbeResizeAndOverwriteOp(char*, size_t) { return 0; }

// Prior to C++23, Google's libc++ backports resize_and_overwrite as
// __google_nonstandard_backport_resize_and_overwrite
template <typename T, typename = void>
struct has__google_nonstandard_backport_resize_and_overwrite : std::false_type {
};

template <typename T>
struct has__google_nonstandard_backport_resize_and_overwrite<
    T,
    std::void_t<
        decltype(std::declval<T&>()
                     .__google_nonstandard_backport_resize_and_overwrite(
                         std::declval<size_t>(), ProbeResizeAndOverwriteOp))>>
    : std::true_type {};

// Prior to C++23, the version of libstdc++ that shipped with GCC >= 14
// has __resize_and_overwrite.
template <typename T, typename = void>
struct has__resize_and_overwrite : std::false_type {};

template <typename T>
struct has__resize_and_overwrite<
    T, std::void_t<decltype(std::declval<T&>().__resize_and_overwrite(
           std::declval<size_t>(), ProbeResizeAndOverwriteOp))>>
    : std::true_type {};

// libc++ used  __resize_default_init to achieve uninitialized string resizes
// before removing it September 2025, in favor of resize_and_overwrite.
// https://github.com/llvm/llvm-project/commit/92f5d8df361bb1bb6dea88f86faeedfd295ab970
template <typename T, typename = void>
struct has__resize_default_init : std::false_type {};

template <typename T>
struct has__resize_default_init<
    T, std::void_t<decltype(std::declval<T&>().__resize_default_init(42))>>
    : std::true_type {};

// Prior to C++23, some versions of MSVC have _Resize_and_overwrite.
template <typename T, typename = void>
struct has_Resize_and_overwrite : std::false_type {};

template <typename T>
struct has_Resize_and_overwrite<
    T, std::void_t<decltype(std::declval<T&>()._Resize_and_overwrite(
           std::declval<size_t>(), ProbeResizeAndOverwriteOp))>>
    : std::true_type {};

#endif  // ifndef ABSL_INTERNAL_HAS_RESIZE_AND_OVERWRITE

// A less-efficient fallback implementation that uses resize().
template <typename T, typename Op>
void StringResizeAndOverwriteFallback(T& str, typename T::size_type n, Op op) {
  if (ABSL_PREDICT_FALSE(n > str.max_size())) {
    absl::base_internal::ThrowStdLengthError("absl::StringResizeAndOverwrite");
  }
#ifdef ABSL_HAVE_MEMORY_SANITIZER
  auto old_size = str.size();
#endif
  str.resize(n);
#ifdef ABSL_HAVE_MEMORY_SANITIZER
  if (old_size < n) {
    ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(str.data() + old_size, n - old_size);
  }
#endif
  auto new_size = std::move(op)(str.data(), n);
  ABSL_HARDENING_ASSERT(new_size >= 0 && new_size <= n);
  ABSL_HARDENING_ASSERT(str.data()[n] == typename T::value_type{});
  str.erase(static_cast<typename T::size_type>(new_size));
}

template <typename T, typename Op>
void StringResizeAndOverwriteImpl(T& str, typename T::size_type n, Op op) {
#ifdef ABSL_INTERNAL_HAS_RESIZE_AND_OVERWRITE
  str.resize_and_overwrite(n, std::move(op));
#else
  if constexpr (strings_internal::
                    has__google_nonstandard_backport_resize_and_overwrite<
                        T>::value) {
    str.__google_nonstandard_backport_resize_and_overwrite(n, std::move(op));
  } else if constexpr (strings_internal::has__resize_and_overwrite<T>::value) {
    str.__resize_and_overwrite(n, std::move(op));
  } else if constexpr (strings_internal::has__resize_default_init<T>::value) {
    str.__resize_default_init(n);
    str.__resize_default_init(
        static_cast<typename T::size_type>(std::move(op)(str.data(), n)));
  } else if constexpr (strings_internal::has_Resize_and_overwrite<T>::value) {
    str._Resize_and_overwrite(n, std::move(op));
  } else {
    strings_internal::StringResizeAndOverwriteFallback(str, n, std::move(op));
  }
#endif
}

}  // namespace strings_internal

// Resizes `str` to contain at most `n` characters, using the user-provided
// operation `op` to modify the possibly indeterminate contents. `op` must
// return the finalized length of `str`.
//
// Invalidates all iterators, pointers, and references into `str`, regardless
// of whether reallocation occurs.
//
// `op(value_type* buf, size_t buf_size)` is allowed to write `value_type{}` to
// `buf[buf_size]`, which facilitiates interoperation with functions that write
// a trailing NUL. Please note that this requirement is more strict than
// `basic_string::resize_and_overwrite()`, which allows writing an abitrary
// value to `buf[buf_size]`.
template <typename T, typename Op>
void StringResizeAndOverwrite(T& str, typename T::size_type n, Op op) {
  strings_internal::StringResizeAndOverwriteImpl(str, n, std::move(op));
#if defined(ABSL_HAVE_MEMORY_SANITIZER)
  __msan_check_mem_is_initialized(str.data(), str.size());
#endif
}

ABSL_NAMESPACE_END
}  // namespace absl

#undef ABSL_INTERNAL_HAS_RESIZE_AND_OVERWRITE

#endif  // ABSL_STRINGS_RESIZE_AND_OVERWRITE_H_
