aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/core/src
diff options
context:
space:
mode:
authorGravatar Gil <mcg@google.com>2018-05-21 13:17:29 -0700
committerGravatar GitHub <noreply@github.com>2018-05-21 13:17:29 -0700
commit11933c038df81f57c0e7d15f5a8795b74e874843 (patch)
tree44aae26f6c3b24d34d5ab7e6dbd84a7161220c50 /Firestore/core/src
parent936659e582cb8303991f0fd742679f4e0953706d (diff)
Add a C++ native StringFormat (#1289)
* Add StringFormat * Use StringFormat
Diffstat (limited to 'Firestore/core/src')
-rw-r--r--Firestore/core/src/firebase/firestore/util/CMakeLists.txt3
-rw-r--r--Firestore/core/src/firebase/firestore/util/status.cc4
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_format.cc94
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_format.h149
4 files changed, 248 insertions, 2 deletions
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<int>(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 = "<missing>";
+static const char* kInvalid = "<invalid>";
+
+std::string StringFormatPieces(
+ const char* format, std::initializer_list<absl::string_view> 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 <initializer_list>
+#include <string>
+#include <utility>
+
+#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<absl::string_view> pieces);
+
+/**
+ * Explicit ranking for formatting choices. Only useful as an implementation
+ * detail of `FormatArg`.
+ */
+template <int I>
+struct FormatChoice : FormatChoice<I + 1> {};
+
+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 <typename T>
+ FormatArg(T&& value) // NOLINT(runtime/explicit)
+ : FormatArg{std::forward<T>(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 <typename T,
+ typename = typename std::enable_if<std::is_same<bool, T>{}>::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 <typename T>
+ FormatArg(T* pointer_value, internal::FormatChoice<3>)
+ : AlphaNum{absl::Hex{reinterpret_cast<uintptr_t>(pointer_value)}} {
+ }
+
+ /**
+ * As a final fallback, creates a FormatArg from any type of value that
+ * absl::AlphaNum accepts.
+ */
+ template <typename T>
+ FormatArg(T&& value, internal::FormatChoice<4>)
+ : AlphaNum{std::forward<T>(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 "<missing>".
+ * * If you pass more arguments than the format requires, any excess arguments
+ * are ignored.
+ * * If you use an invalid format specifier, StringFormat will insert
+ * <invalid>.
+ */
+template <typename... FA>
+std::string StringFormat(const char* format, const FA&... args) {
+ return internal::StringFormatPieces(
+ format, {static_cast<const FormatArg&>(args).Piece()...});
+}
+
+inline std::string StringFormat() {
+ return {};
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_FORMAT_H_