aboutsummaryrefslogtreecommitdiff
path: root/absl/strings/str_cat.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/str_cat.cc')
-rw-r--r--absl/strings/str_cat.cc191
1 files changed, 49 insertions, 142 deletions
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 098ab183..c51c1373 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -20,19 +20,18 @@
#include <cstdint>
#include <cstring>
#include <initializer_list>
+#include <limits>
#include <string>
-#include <type_traits>
#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
#include "absl/base/nullability.h"
#include "absl/strings/internal/resize_uninitialized.h"
-#include "absl/strings/numbers.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
-
// ----------------------------------------------------------------------
// StrCat()
// This merges the given strings or integers, with no delimiter. This
@@ -43,7 +42,8 @@ ABSL_NAMESPACE_BEGIN
namespace {
// Append is merely a version of memcpy that returns the address of the byte
// after the area just overwritten.
-absl::Nonnull<char*> Append(absl::Nonnull<char*> out, const AlphaNum& x) {
+inline absl::Nonnull<char*> Append(absl::Nonnull<char*> out,
+ const AlphaNum& x) {
// memcpy is allowed to overwrite arbitrary memory, so doing this after the
// call would force an extra fetch of x.size().
char* after = out + x.size();
@@ -53,12 +53,23 @@ absl::Nonnull<char*> Append(absl::Nonnull<char*> out, const AlphaNum& x) {
return after;
}
+inline void STLStringAppendUninitializedAmortized(std::string* dest,
+ size_t to_append) {
+ strings_internal::AppendUninitializedTraits<std::string>::Append(dest,
+ to_append);
+}
} // namespace
std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
std::string result;
- absl::strings_internal::STLStringResizeUninitialized(&result,
- a.size() + b.size());
+ // Use uint64_t to prevent size_t overflow. We assume it is not possible for
+ // in memory strings to overflow a uint64_t.
+ constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
+ const uint64_t result_size =
+ static_cast<uint64_t>(a.size()) + static_cast<uint64_t>(b.size());
+ ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
+ absl::strings_internal::STLStringResizeUninitialized(
+ &result, static_cast<size_t>(result_size));
char* const begin = &result[0];
char* out = begin;
out = Append(out, a);
@@ -69,8 +80,15 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
std::string result;
+ // Use uint64_t to prevent size_t overflow. We assume it is not possible for
+ // in memory strings to overflow a uint64_t.
+ constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
+ const uint64_t result_size = static_cast<uint64_t>(a.size()) +
+ static_cast<uint64_t>(b.size()) +
+ static_cast<uint64_t>(c.size());
+ ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
strings_internal::STLStringResizeUninitialized(
- &result, a.size() + b.size() + c.size());
+ &result, static_cast<size_t>(result_size));
char* const begin = &result[0];
char* out = begin;
out = Append(out, a);
@@ -83,8 +101,16 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
const AlphaNum& d) {
std::string result;
+ // Use uint64_t to prevent size_t overflow. We assume it is not possible for
+ // in memory strings to overflow a uint64_t.
+ constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
+ const uint64_t result_size = static_cast<uint64_t>(a.size()) +
+ static_cast<uint64_t>(b.size()) +
+ static_cast<uint64_t>(c.size()) +
+ static_cast<uint64_t>(d.size());
+ ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
strings_internal::STLStringResizeUninitialized(
- &result, a.size() + b.size() + c.size() + d.size());
+ &result, static_cast<size_t>(result_size));
char* const begin = &result[0];
char* out = begin;
out = Append(out, a);
@@ -98,135 +124,18 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
namespace strings_internal {
// Do not call directly - these are not part of the public API.
-void STLStringAppendUninitializedAmortized(std::string* dest,
- size_t to_append) {
- strings_internal::AppendUninitializedTraits<std::string>::Append(dest,
- to_append);
-}
-
-template <typename Integer>
-std::enable_if_t<std::is_integral<Integer>::value, std::string> IntegerToString(
- Integer i) {
- std::string str;
- const auto /* either bool or std::false_type */ is_negative =
- absl::numbers_internal::IsNegative(i);
- const uint32_t digits = absl::numbers_internal::Base10Digits(
- absl::numbers_internal::UnsignedAbsoluteValue(i));
- absl::strings_internal::STLStringResizeUninitialized(
- &str, digits + static_cast<uint32_t>(is_negative));
- absl::numbers_internal::FastIntToBufferBackward(i, &str[str.size()], digits);
- return str;
-}
-
-template <>
-std::string IntegerToString(long i) { // NOLINT
- if (sizeof(i) <= sizeof(int)) {
- return IntegerToString(static_cast<int>(i));
- } else {
- return IntegerToString(static_cast<long long>(i)); // NOLINT
- }
-}
-
-template <>
-std::string IntegerToString(unsigned long i) { // NOLINT
- if (sizeof(i) <= sizeof(unsigned int)) {
- return IntegerToString(static_cast<unsigned int>(i));
- } else {
- return IntegerToString(static_cast<unsigned long long>(i)); // NOLINT
- }
-}
-
-template <typename Float>
-std::enable_if_t<std::is_floating_point<Float>::value, std::string>
-FloatToString(Float f) {
- std::string result;
- strings_internal::STLStringResizeUninitialized(
- &result, numbers_internal::kSixDigitsToBufferSize);
- char* start = &result[0];
- result.erase(numbers_internal::SixDigitsToBuffer(f, start));
- return result;
-}
-
-std::string SingleArgStrCat(int x) { return IntegerToString(x); }
-std::string SingleArgStrCat(unsigned int x) { return IntegerToString(x); }
-// NOLINTNEXTLINE
-std::string SingleArgStrCat(long x) { return IntegerToString(x); }
-// NOLINTNEXTLINE
-std::string SingleArgStrCat(unsigned long x) { return IntegerToString(x); }
-// NOLINTNEXTLINE
-std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
-// NOLINTNEXTLINE
-std::string SingleArgStrCat(unsigned long long x) { return IntegerToString(x); }
-std::string SingleArgStrCat(float x) { return FloatToString(x); }
-std::string SingleArgStrCat(double x) { return FloatToString(x); }
-
-template <class Integer>
-std::enable_if_t<std::is_integral<Integer>::value, void> AppendIntegerToString(
- std::string& str, Integer i) {
- const auto /* either bool or std::false_type */ is_negative =
- absl::numbers_internal::IsNegative(i);
- const uint32_t digits = absl::numbers_internal::Base10Digits(
- absl::numbers_internal::UnsignedAbsoluteValue(i));
- absl::strings_internal::STLStringAppendUninitializedAmortized(
- &str, digits + static_cast<uint32_t>(is_negative));
- absl::numbers_internal::FastIntToBufferBackward(i, &str[str.size()], digits);
-}
-
-template <>
-void AppendIntegerToString(std::string& str, long i) { // NOLINT
- if (sizeof(i) <= sizeof(int)) {
- return AppendIntegerToString(str, static_cast<int>(i));
- } else {
- return AppendIntegerToString(str, static_cast<long long>(i)); // NOLINT
- }
-}
-
-template <>
-void AppendIntegerToString(std::string& str,
- unsigned long i) { // NOLINT
- if (sizeof(i) <= sizeof(unsigned int)) {
- return AppendIntegerToString(str, static_cast<unsigned int>(i));
- } else {
- return AppendIntegerToString(str,
- static_cast<unsigned long long>(i)); // NOLINT
- }
-}
-
-// `SingleArgStrAppend` overloads are defined here for the same reasons as with
-// `SingleArgStrCat` above.
-void SingleArgStrAppend(std::string& str, int x) {
- return AppendIntegerToString(str, x);
-}
-
-void SingleArgStrAppend(std::string& str, unsigned int x) {
- return AppendIntegerToString(str, x);
-}
-
-// NOLINTNEXTLINE
-void SingleArgStrAppend(std::string& str, long x) {
- return AppendIntegerToString(str, x);
-}
-
-// NOLINTNEXTLINE
-void SingleArgStrAppend(std::string& str, unsigned long x) {
- return AppendIntegerToString(str, x);
-}
-
-// NOLINTNEXTLINE
-void SingleArgStrAppend(std::string& str, long long x) {
- return AppendIntegerToString(str, x);
-}
-
-// NOLINTNEXTLINE
-void SingleArgStrAppend(std::string& str, unsigned long long x) {
- return AppendIntegerToString(str, x);
-}
-
std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
std::string result;
- size_t total_size = 0;
- for (absl::string_view piece : pieces) total_size += piece.size();
- strings_internal::STLStringResizeUninitialized(&result, total_size);
+ // Use uint64_t to prevent size_t overflow. We assume it is not possible for
+ // in memory strings to overflow a uint64_t.
+ constexpr uint64_t kMaxSize = uint64_t{std::numeric_limits<size_t>::max()};
+ uint64_t total_size = 0;
+ for (absl::string_view piece : pieces) {
+ total_size += piece.size();
+ }
+ ABSL_INTERNAL_CHECK(total_size <= kMaxSize, "size_t overflow");
+ strings_internal::STLStringResizeUninitialized(
+ &result, static_cast<size_t>(total_size));
char* const begin = &result[0];
char* out = begin;
@@ -258,7 +167,7 @@ void AppendPieces(absl::Nonnull<std::string*> dest,
ASSERT_NO_OVERLAP(*dest, piece);
to_append += piece.size();
}
- strings_internal::STLStringAppendUninitializedAmortized(dest, to_append);
+ STLStringAppendUninitializedAmortized(dest, to_append);
char* const begin = &(*dest)[0];
char* out = begin + old_size;
@@ -277,7 +186,7 @@ void AppendPieces(absl::Nonnull<std::string*> dest,
void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a) {
ASSERT_NO_OVERLAP(*dest, a);
std::string::size_type old_size = dest->size();
- strings_internal::STLStringAppendUninitializedAmortized(dest, a.size());
+ STLStringAppendUninitializedAmortized(dest, a.size());
char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
@@ -289,8 +198,7 @@ void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
ASSERT_NO_OVERLAP(*dest, a);
ASSERT_NO_OVERLAP(*dest, b);
std::string::size_type old_size = dest->size();
- strings_internal::STLStringAppendUninitializedAmortized(dest,
- a.size() + b.size());
+ STLStringAppendUninitializedAmortized(dest, a.size() + b.size());
char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
@@ -304,8 +212,7 @@ void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
ASSERT_NO_OVERLAP(*dest, b);
ASSERT_NO_OVERLAP(*dest, c);
std::string::size_type old_size = dest->size();
- strings_internal::STLStringAppendUninitializedAmortized(
- dest, a.size() + b.size() + c.size());
+ STLStringAppendUninitializedAmortized(dest, a.size() + b.size() + c.size());
char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
@@ -321,7 +228,7 @@ void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
ASSERT_NO_OVERLAP(*dest, c);
ASSERT_NO_OVERLAP(*dest, d);
std::string::size_type old_size = dest->size();
- strings_internal::STLStringAppendUninitializedAmortized(
+ STLStringAppendUninitializedAmortized(
dest, a.size() + b.size() + c.size() + d.size());
char* const begin = &(*dest)[0];
char* out = begin + old_size;