From 11933c038df81f57c0e7d15f5a8795b74e874843 Mon Sep 17 00:00:00 2001 From: Gil Date: Mon, 21 May 2018 13:17:29 -0700 Subject: Add a C++ native StringFormat (#1289) * Add StringFormat * Use StringFormat --- .../src/firebase/firestore/util/CMakeLists.txt | 3 + .../core/src/firebase/firestore/util/status.cc | 4 +- .../src/firebase/firestore/util/string_format.cc | 94 +++++++++++++ .../src/firebase/firestore/util/string_format.h | 149 +++++++++++++++++++++ 4 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/util/string_format.cc create mode 100644 Firestore/core/src/firebase/firestore/util/string_format.h (limited to 'Firestore/core/src') diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index b2b015b..2c12fa4 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -22,10 +22,13 @@ include(CheckIncludeFiles) cc_library( firebase_firestore_util_base SOURCES + string_format.cc + string_format.h string_printf.cc string_printf.h DEPENDS absl_base + absl_strings ) ## assert and log diff --git a/Firestore/core/src/firebase/firestore/util/status.cc b/Firestore/core/src/firebase/firestore/util/status.cc index 662fa5d..e918846 100644 --- a/Firestore/core/src/firebase/firestore/util/status.cc +++ b/Firestore/core/src/firebase/firestore/util/status.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/util/status.h" -#include "Firestore/core/src/firebase/firestore/util/string_printf.h" +#include "Firestore/core/src/firebase/firestore/util/string_format.h" namespace firebase { namespace firestore { @@ -103,7 +103,7 @@ std::string Status::ToString() const { result = "Data loss"; break; default: - result = StringPrintf("Unknown code(%d)", static_cast(code())); + result = StringFormat("Unknown code(%s)", code()); break; } result += ": "; diff --git a/Firestore/core/src/firebase/firestore/util/string_format.cc b/Firestore/core/src/firebase/firestore/util/string_format.cc new file mode 100644 index 0000000..bafdac2 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/string_format.cc @@ -0,0 +1,94 @@ +/* + * Copyright 2018 Google + * + * 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 + * + * http://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. + */ + +#include "Firestore/core/src/firebase/firestore/util/string_format.h" + +namespace firebase { +namespace firestore { +namespace util { +namespace internal { + +static const char* kMissing = ""; +static const char* kInvalid = ""; + +std::string StringFormatPieces( + const char* format, std::initializer_list pieces) { + std::string result; + + const char* format_iter = format; + const char* format_end = format + strlen(format); + + auto pieces_iter = pieces.begin(); + auto pieces_end = pieces.end(); + auto append_next_piece = [&](std::string* dest) { + if (pieces_iter == pieces_end) { + dest->append(kMissing); + } else { + // Pass a piece through + dest->append(pieces_iter->data(), pieces_iter->size()); + ++pieces_iter; + } + }; + + auto append_specifier = [&](char spec) { + switch (spec) { + case '%': + // Pass through literal %. + result.push_back('%'); + break; + + case 's': { + append_next_piece(&result); + break; + } + + default: + result.append(kInvalid); + break; + } + }; + + // Iterate through the format string, advancing `format_iter` as we go. + while (true) { + const char* percent_ptr = std::find(format_iter, format_end, '%'); + + // percent either points to the next format specifier or the end of the + // format string. Either is safe to append here: + result.append(format_iter, percent_ptr); + if (percent_ptr == format_end) { + // No further pieces to format + break; + } + + // Examine the specifier: + const char* spec_ptr = percent_ptr + 1; + if (spec_ptr == format_end) { + // Incomplete specifier, treat as a literal "%" and be done. + append_specifier('%'); + break; + } + append_specifier(*spec_ptr); + + format_iter = spec_ptr + 1; + } + + return result; +} + +} // namespace internal +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/string_format.h b/Firestore/core/src/firebase/firestore/util/string_format.h new file mode 100644 index 0000000..3d7a1dc --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/string_format.h @@ -0,0 +1,149 @@ +/* + * Copyright 2018 Google + * + * 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 + * + * http://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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_FORMAT_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_FORMAT_H_ + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace internal { + +std::string StringFormatPieces(const char* format, + std::initializer_list pieces); + +/** + * Explicit ranking for formatting choices. Only useful as an implementation + * detail of `FormatArg`. + */ +template +struct FormatChoice : FormatChoice {}; + +template <> +struct FormatChoice<4> {}; + +} // namespace internal + +/** + * Acts as the main value parameter to StringFormat and related functions. + * + * Chooses a conversion to a text form in this order: + * * If the value is exactly of `bool` type (without implicit conversions) + * the text will the "true" or "false". + * * If the value is of type `const char*`, the text will be the value + * interpreted as a C string. To show the address of a single char or to + * show the `const char*` as an address, cast to `void*`. + * * If the value is any other pointer type, the text will be the hexidecimal + * formatting of the value as an unsigned integer. + * * Otherwise the value is interpreted as anything absl::AlphaNum accepts. + */ +class FormatArg : public absl::AlphaNum { + public: + template + FormatArg(T&& value) // NOLINT(runtime/explicit) + : FormatArg{std::forward(value), internal::FormatChoice<0>{}} { + } + + private: + /** + * Creates a FormatArg from a boolean value, representing the string + * "true" or "false". + * + * This overload only applies if the type of the argument is exactly `bool`. + * No implicit conversions to bool are accepted. + */ + template {}>::type> + FormatArg(T bool_value, internal::FormatChoice<0>) + : AlphaNum{bool_value ? "true" : "false"} { + } + + /** + * Creates a FormatArg from a character string literal. This is + * handled specially to avoid ambiguity with generic pointers, which are + * handled differently. + */ + FormatArg(std::nullptr_t, internal::FormatChoice<1>) : AlphaNum{"null"} { + } + + /** + * Creates a FormatArg from a character string literal. This is + * handled specially to avoid ambiguity with generic pointers, which are + * handled differently. + */ + FormatArg(const char* string_value, internal::FormatChoice<2>) + : AlphaNum{string_value == nullptr ? "null" : string_value} { + } + + /** + * Creates a FormatArg from an arbitrary pointer, represented as a + * hexidecimal integer literal. + */ + template + FormatArg(T* pointer_value, internal::FormatChoice<3>) + : AlphaNum{absl::Hex{reinterpret_cast(pointer_value)}} { + } + + /** + * As a final fallback, creates a FormatArg from any type of value that + * absl::AlphaNum accepts. + */ + template + FormatArg(T&& value, internal::FormatChoice<4>) + : AlphaNum{std::forward(value)} { + } +}; + +/** + * Formats a string using a simplified printf-like formatting mechanism that + * does not rely on C varargs. + * + * The following format specifiers are recognized: + * * "%%" - A literal "%" + * * "%s" - The next parameter is copied through + * + * Note: + * * If you pass fewer arguments than the format requires, StringFormat will + * insert "". + * * If you pass more arguments than the format requires, any excess arguments + * are ignored. + * * If you use an invalid format specifier, StringFormat will insert + * . + */ +template +std::string StringFormat(const char* format, const FA&... args) { + return internal::StringFormatPieces( + format, {static_cast(args).Piece()...}); +} + +inline std::string StringFormat() { + return {}; +} + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_FORMAT_H_ -- cgit v1.2.3