aboutsummaryrefslogtreecommitdiffhomepage
path: root/absl/strings/internal
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/internal')
-rw-r--r--absl/strings/internal/cord_internal.h151
-rw-r--r--absl/strings/internal/str_format/arg.cc37
-rw-r--r--absl/strings/internal/str_format/arg.h7
-rw-r--r--absl/strings/internal/str_format/arg_test.cc2
-rw-r--r--absl/strings/internal/str_format/extension.cc11
-rw-r--r--absl/strings/internal/str_format/extension.h200
-rw-r--r--absl/strings/internal/str_format/float_conversion.cc18
-rw-r--r--absl/strings/internal/str_format/parser.cc6
-rw-r--r--absl/strings/internal/str_format/parser.h10
-rw-r--r--absl/strings/internal/str_format/parser_test.cc14
10 files changed, 305 insertions, 151 deletions
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
new file mode 100644
index 0000000..5b5d108
--- /dev/null
+++ b/absl/strings/internal/cord_internal.h
@@ -0,0 +1,151 @@
+// Copyright 2020 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.
+
+#ifndef ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
+#define ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
+
+#include <atomic>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+#include "absl/meta/type_traits.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// Wraps std::atomic for reference counting.
+class Refcount {
+ public:
+ Refcount() : count_{1} {}
+ ~Refcount() {}
+
+ // Increments the reference count by 1. Imposes no memory ordering.
+ inline void Increment() { count_.fetch_add(1, std::memory_order_relaxed); }
+
+ // Asserts that the current refcount is greater than 0. If the refcount is
+ // greater than 1, decrements the reference count by 1.
+ //
+ // Returns false if there are no references outstanding; true otherwise.
+ // Inserts barriers to ensure that state written before this method returns
+ // false will be visible to a thread that just observed this method returning
+ // false.
+ inline bool Decrement() {
+ int32_t refcount = count_.load(std::memory_order_acquire);
+ assert(refcount > 0);
+ return refcount != 1 && count_.fetch_sub(1, std::memory_order_acq_rel) != 1;
+ }
+
+ // Same as Decrement but expect that refcount is greater than 1.
+ inline bool DecrementExpectHighRefcount() {
+ int32_t refcount = count_.fetch_sub(1, std::memory_order_acq_rel);
+ assert(refcount > 0);
+ return refcount != 1;
+ }
+
+ // Returns the current reference count using acquire semantics.
+ inline int32_t Get() const { return count_.load(std::memory_order_acquire); }
+
+ // Returns whether the atomic integer is 1.
+ // If the reference count is used in the conventional way, a
+ // reference count of 1 implies that the current thread owns the
+ // reference and no other thread shares it.
+ // This call performs the test for a reference count of one, and
+ // performs the memory barrier needed for the owning thread
+ // to act on the object, knowing that it has exclusive access to the
+ // object.
+ inline bool IsOne() { return count_.load(std::memory_order_acquire) == 1; }
+
+ private:
+ std::atomic<int32_t> count_;
+};
+
+// The overhead of a vtable is too much for Cord, so we roll our own subclasses
+// using only a single byte to differentiate classes from each other - the "tag"
+// byte. Define the subclasses first so we can provide downcasting helper
+// functions in the base class.
+
+struct CordRepConcat;
+struct CordRepSubstring;
+struct CordRepExternal;
+
+struct CordRep {
+ // The following three fields have to be less than 32 bytes since
+ // that is the smallest supported flat node size.
+ // We use uint64_t for the length even in 32-bit binaries.
+ uint64_t length;
+ Refcount refcount;
+ // If tag < FLAT, it represents CordRepKind and indicates the type of node.
+ // Otherwise, the node type is CordRepFlat and the tag is the encoded size.
+ uint8_t tag;
+ char data[1]; // Starting point for flat array: MUST BE LAST FIELD of CordRep
+
+ inline CordRepConcat* concat();
+ inline const CordRepConcat* concat() const;
+ inline CordRepSubstring* substring();
+ inline const CordRepSubstring* substring() const;
+ inline CordRepExternal* external();
+ inline const CordRepExternal* external() const;
+};
+
+struct CordRepConcat : public CordRep {
+ CordRep* left;
+ CordRep* right;
+
+ uint8_t depth() const { return static_cast<uint8_t>(data[0]); }
+ void set_depth(uint8_t depth) { data[0] = static_cast<char>(depth); }
+};
+
+struct CordRepSubstring : public CordRep {
+ size_t start; // Starting offset of substring in child
+ CordRep* child;
+};
+
+// TODO(strel): replace the following logic (and related functions in cord.cc)
+// with container_internal::Layout.
+
+// Alignment requirement for CordRepExternal so that the type erased releaser
+// will be stored at a suitably aligned address.
+constexpr size_t ExternalRepAlignment() {
+#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ return __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+ return alignof(max_align_t);
+#endif
+}
+
+// Type for function pointer that will invoke and destroy the type-erased
+// releaser function object. Accepts a pointer to the releaser and the
+// `string_view` that were passed in to `NewExternalRep` below. The return value
+// is the size of the `Releaser` type.
+using ExternalReleaserInvoker = size_t (*)(void*, absl::string_view);
+
+// External CordReps are allocated together with a type erased releaser. The
+// releaser is stored in the memory directly following the CordRepExternal.
+struct alignas(ExternalRepAlignment()) CordRepExternal : public CordRep {
+ const char* base;
+ // Pointer to function that knows how to call and destroy the releaser.
+ ExternalReleaserInvoker releaser_invoker;
+};
+
+// TODO(strel): look into removing, it doesn't seem like anything relies on this
+static_assert(sizeof(CordRepConcat) == sizeof(CordRepSubstring), "");
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+#endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index d390412..4d0604e 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -88,7 +88,7 @@ class ConvertedIntInfo {
template <typename T>
void UnsignedToStringRight(T u, ConversionChar conv) {
char *p = end();
- switch (conv.radix()) {
+ switch (FormatConversionCharRadix(conv)) {
default:
case 10:
for (; u; u /= 10)
@@ -99,7 +99,7 @@ class ConvertedIntInfo {
*--p = static_cast<char>('0' + static_cast<size_t>(u % 8));
break;
case 16: {
- const char *digits = kDigit[conv.upper() ? 1 : 0];
+ const char *digits = kDigit[FormatConversionCharIsUpper(conv) ? 1 : 0];
for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)];
break;
}
@@ -121,21 +121,20 @@ class ConvertedIntInfo {
string_view BaseIndicator(const ConvertedIntInfo &info,
const ConversionSpec conv) {
bool alt = conv.flags().alt;
- int radix = conv.conv().radix();
- if (conv.conv().id() == ConversionChar::p)
- alt = true; // always show 0x for %p.
+ int radix = FormatConversionCharRadix(conv.conv());
+ if (conv.conv() == ConversionChar::p) alt = true; // always show 0x for %p.
// From the POSIX description of '#' flag:
// "For x or X conversion specifiers, a non-zero result shall have
// 0x (or 0X) prefixed to it."
if (alt && radix == 16 && !info.digits().empty()) {
- if (conv.conv().upper()) return "0X";
+ if (FormatConversionCharIsUpper(conv.conv())) return "0X";
return "0x";
}
return {};
}
string_view SignColumn(bool neg, const ConversionSpec conv) {
- if (conv.conv().is_signed()) {
+ if (FormatConversionCharIsSigned(conv.conv())) {
if (neg) return "-";
if (conv.flags().show_pos) return "+";
if (conv.flags().sign_col) return " ";
@@ -175,7 +174,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
if (!precision_specified)
precision = 1;
- if (conv.flags().alt && conv.conv().id() == ConversionChar::o) {
+ if (conv.flags().alt && conv.conv() == ConversionChar::o) {
// From POSIX description of the '#' (alt) flag:
// "For o conversion, it increases the precision (if necessary) to
// force the first digit of the result to be zero."
@@ -211,7 +210,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
template <typename T>
bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
ConvertedIntInfo info(v, conv.conv());
- if (conv.flags().basic && conv.conv().id() != ConversionChar::p) {
+ if (conv.flags().basic && (conv.conv() != ConversionChar::p)) {
if (info.is_neg()) sink->Append(1, '-');
if (info.digits().empty()) {
sink->Append(1, '0');
@@ -225,14 +224,13 @@ bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
template <typename T>
bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
- if (conv.conv().is_float()) {
+ if (FormatConversionCharIsFloat(conv.conv())) {
return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
}
- if (conv.conv().id() == ConversionChar::c)
+ if (conv.conv() == ConversionChar::c)
return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
- if (!conv.conv().is_integral())
- return false;
- if (!conv.conv().is_signed() && IsSigned<T>::value) {
+ if (!FormatConversionCharIsIntegral(conv.conv())) return false;
+ if (!FormatConversionCharIsSigned(conv.conv()) && IsSigned<T>::value) {
using U = typename MakeUnsigned<T>::type;
return FormatConvertImpl(static_cast<U>(v), conv, sink).value;
}
@@ -241,13 +239,13 @@ bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
template <typename T>
bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
- return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink);
+ return FormatConversionCharIsFloat(conv.conv()) &&
+ ConvertFloatImpl(v, conv, sink);
}
inline bool ConvertStringArg(string_view v, const ConversionSpec conv,
FormatSinkImpl *sink) {
- if (conv.conv().id() != ConversionChar::s)
- return false;
+ if (conv.conv() != ConversionChar::s) return false;
if (conv.flags().basic) {
sink->Append(v);
return true;
@@ -274,7 +272,7 @@ ConvertResult<Conv::s> FormatConvertImpl(string_view v,
ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
const ConversionSpec conv,
FormatSinkImpl *sink) {
- if (conv.conv().id() == ConversionChar::p)
+ if (conv.conv() == ConversionChar::p)
return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
size_t len;
if (v == nullptr) {
@@ -291,8 +289,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
// ==================== Raw pointers ====================
ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
FormatSinkImpl *sink) {
- if (conv.conv().id() != ConversionChar::p)
- return {false};
+ if (conv.conv() != ConversionChar::p) return {false};
if (!v.value) {
sink->Append("(nil)");
return {true};
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index b672a22..7a93756 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -70,7 +70,7 @@ template <class AbslCord,
ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value,
ConversionSpec conv,
FormatSinkImpl* sink) {
- if (conv.conv().id() != ConversionChar::s) return {false};
+ if (conv.conv() != ConversionChar::s) return {false};
bool is_left = conv.flags().left;
size_t space_remaining = 0;
@@ -185,8 +185,7 @@ struct FormatCountCaptureHelper {
FormatSinkImpl* sink) {
const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v;
- if (conv.conv().id() != str_format_internal::ConversionChar::n)
- return {false};
+ if (conv.conv() != str_format_internal::ConversionChar::n) return {false};
*v2.p_ = static_cast<int>(sink->size());
return {true};
}
@@ -378,7 +377,7 @@ class FormatArgImpl {
template <typename T>
static bool Dispatch(Data arg, ConversionSpec spec, void* out) {
// A `none` conv indicates that we want the `int` conversion.
- if (ABSL_PREDICT_FALSE(spec.conv().id() == ConversionChar::none)) {
+ if (ABSL_PREDICT_FALSE(spec.conv() == ConversionChar::none)) {
return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(),
std::is_enum<T>());
}
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc
index 96c9cfd..04fa56c 100644
--- a/absl/strings/internal/str_format/arg_test.cc
+++ b/absl/strings/internal/str_format/arg_test.cc
@@ -96,7 +96,7 @@ TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) {
std::string s;
FormatSinkImpl sink(&s);
ConversionSpec conv;
- conv.set_conv(ConversionChar::FromChar('s'));
+ conv.set_conv(ConversionChar::s);
conv.set_flags(Flags());
conv.set_width(-1);
conv.set_precision(-1);
diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc
index 21688e8..2e5bc2c 100644
--- a/absl/strings/internal/str_format/extension.cc
+++ b/absl/strings/internal/str_format/extension.cc
@@ -23,15 +23,6 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
-const ConversionChar::Spec ConversionChar::kSpecs[] = {
-#define X_VAL(id) { ConversionChar::id, #id[0] }
-#define X_SEP ,
- ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP),
- {ConversionChar::none, '\0'},
-#undef X_VAL
-#undef X_SEP
-};
-
std::string Flags::ToString() const {
std::string s;
s.append(left ? "-" : "");
@@ -42,8 +33,6 @@ std::string Flags::ToString() const {
return s;
}
-const size_t ConversionChar::kNumValues;
-
bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) {
size_t space_remaining = 0;
if (w >= 0) space_remaining = w;
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index 4868eac..1a863c2 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -148,117 +148,122 @@ struct Flags {
X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \
/* misc */ \
X_VAL(n) X_SEP X_VAL(p)
-// clang-format on
-struct ABSL_DLL ConversionChar {
- public:
- enum Id : uint8_t {
+enum class FormatConversionChar : uint8_t {
c, C, s, S, // text
d, i, o, u, x, X, // int
f, F, e, E, g, G, a, A, // float
n, p, // misc
- none
- };
- static const size_t kNumValues = none + 1;
-
- ConversionChar() : id_(none) {}
-
- public:
- // Index into the opaque array of ConversionChar enums.
- // Requires: i < kNumValues
- static ConversionChar FromIndex(size_t i) {
- return ConversionChar(kSpecs[i].value);
- }
+ kNone,
+ none = kNone
+};
+// clang-format on
- static ConversionChar FromChar(char c) {
- ConversionChar::Id out_id = ConversionChar::none;
- switch (c) {
-#define X_VAL(id) \
- case #id[0]: \
- out_id = ConversionChar::id; \
- break;
- ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, )
+inline FormatConversionChar FormatConversionCharFromChar(char c) {
+ switch (c) {
+#define X_VAL(id) \
+ case #id[0]: \
+ return FormatConversionChar::id;
+ ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, )
#undef X_VAL
- default:
- break;
- }
- return ConversionChar(out_id);
}
+ return FormatConversionChar::kNone;
+}
- static ConversionChar FromId(Id id) { return ConversionChar(id); }
- Id id() const { return id_; }
-
- int radix() const {
- switch (id()) {
- case x: case X: case a: case A: case p: return 16;
- case o: return 8;
- default: return 10;
- }
+inline int FormatConversionCharRadix(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::x:
+ case FormatConversionChar::X:
+ case FormatConversionChar::a:
+ case FormatConversionChar::A:
+ case FormatConversionChar::p:
+ return 16;
+ case FormatConversionChar::o:
+ return 8;
+ default:
+ return 10;
}
+}
- bool upper() const {
- switch (id()) {
- case X: case F: case E: case G: case A: return true;
- default: return false;
- }
+inline bool FormatConversionCharIsUpper(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::X:
+ case FormatConversionChar::F:
+ case FormatConversionChar::E:
+ case FormatConversionChar::G:
+ case FormatConversionChar::A:
+ return true;
+ default:
+ return false;
}
+}
- bool is_signed() const {
- switch (id()) {
- case d: case i: return true;
- default: return false;
- }
+inline bool FormatConversionCharIsSigned(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::d:
+ case FormatConversionChar::i:
+ return true;
+ default:
+ return false;
}
+}
- bool is_integral() const {
- switch (id()) {
- case d: case i: case u: case o: case x: case X:
- return true;
- default: return false;
- }
+inline bool FormatConversionCharIsIntegral(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::d:
+ case FormatConversionChar::i:
+ case FormatConversionChar::u:
+ case FormatConversionChar::o:
+ case FormatConversionChar::x:
+ case FormatConversionChar::X:
+ return true;
+ default:
+ return false;
}
+}
- bool is_float() const {
- switch (id()) {
- case a: case e: case f: case g: case A: case E: case F: case G:
- return true;
- default: return false;
- }
+inline bool FormatConversionCharIsFloat(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::a:
+ case FormatConversionChar::e:
+ case FormatConversionChar::f:
+ case FormatConversionChar::g:
+ case FormatConversionChar::A:
+ case FormatConversionChar::E:
+ case FormatConversionChar::F:
+ case FormatConversionChar::G:
+ return true;
+ default:
+ return false;
}
+}
- bool IsValid() const { return id() != none; }
-
- // The associated char.
- char Char() const { return kSpecs[id_].name; }
-
- friend bool operator==(const ConversionChar& a, const ConversionChar& b) {
- return a.id() == b.id();
- }
- friend bool operator!=(const ConversionChar& a, const ConversionChar& b) {
- return !(a == b);
- }
- friend std::ostream& operator<<(std::ostream& os, const ConversionChar& v) {
- char c = v.Char();
- if (!c) c = '?';
- return os << c;
+inline char FormatConversionCharToChar(FormatConversionChar c) {
+ switch (c) {
+#define X_VAL(e) \
+ case FormatConversionChar::e: \
+ return #e[0];
+#define X_SEP
+ ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP)
+ case FormatConversionChar::kNone:
+ return '\0';
+#undef X_VAL
+#undef X_SEP
}
+ return '\0';
+}
- private:
- struct Spec {
- Id value;
- char name;
- };
- static const Spec kSpecs[];
-
- explicit ConversionChar(Id id) : id_(id) {}
-
- Id id_;
-};
+// The associated char.
+inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) {
+ char c = FormatConversionCharToChar(v);
+ if (!c) c = '?';
+ return os << c;
+}
class ConversionSpec {
public:
Flags flags() const { return flags_; }
- ConversionChar conv() const {
+ FormatConversionChar conv() const {
// Keep this field first in the struct . It generates better code when
// accessing it when ConversionSpec is passed by value in registers.
static_assert(offsetof(ConversionSpec, conv_) == 0, "");
@@ -273,22 +278,24 @@ class ConversionSpec {
int precision() const { return precision_; }
void set_flags(Flags f) { flags_ = f; }
- void set_conv(ConversionChar c) { conv_ = c; }
+ void set_conv(FormatConversionChar c) { conv_ = c; }
void set_width(int w) { width_ = w; }
void set_precision(int p) { precision_ = p; }
void set_left(bool b) { flags_.left = b; }
private:
- ConversionChar conv_;
+ FormatConversionChar conv_ = FormatConversionChar::kNone;
Flags flags_;
int width_;
int precision_;
};
-constexpr uint64_t ConversionCharToConvValue(char conv) {
+constexpr uint64_t FormatConversionCharToConvValue(char conv) {
return
-#define CONV_SET_CASE(c) \
- conv == #c[0] ? (uint64_t{1} << (1 + ConversionChar::Id::c)):
+#define CONV_SET_CASE(c) \
+ conv == #c[0] \
+ ? (uint64_t{1} << (1 + static_cast<uint8_t>(FormatConversionChar::c))) \
+ :
ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
#undef CONV_SET_CASE
conv == '*'
@@ -297,12 +304,12 @@ constexpr uint64_t ConversionCharToConvValue(char conv) {
}
enum class Conv : uint64_t {
-#define CONV_SET_CASE(c) c = ConversionCharToConvValue(#c[0]),
+#define CONV_SET_CASE(c) c = FormatConversionCharToConvValue(#c[0]),
ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
#undef CONV_SET_CASE
// Used for width/precision '*' specification.
- star = ConversionCharToConvValue('*'),
+ star = FormatConversionCharToConvValue('*'),
// Some predefined values:
integral = d | i | u | o | x | X,
@@ -323,12 +330,12 @@ constexpr Conv operator|(Conv a, Conv b) {
// Get a conversion with a single character in it.
constexpr Conv ConversionCharToConv(char c) {
- return Conv(ConversionCharToConvValue(c));
+ return Conv(FormatConversionCharToConvValue(c));
}
// Checks whether `c` exists in `set`.
constexpr bool Contains(Conv set, char c) {
- return (static_cast<uint64_t>(set) & ConversionCharToConvValue(c)) != 0;
+ return (static_cast<uint64_t>(set) & FormatConversionCharToConvValue(c)) != 0;
}
// Checks whether all the characters in `c` are contained in `set`
@@ -353,6 +360,9 @@ inline size_t Excess(size_t used, size_t capacity) {
return used < capacity ? capacity - used : 0;
}
+// Type alias for use during migration.
+using ConversionChar = FormatConversionChar;
+
} // namespace str_format_internal
ABSL_NAMESPACE_END
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc
index ebe4da5..c98ed4b 100644
--- a/absl/strings/internal/str_format/float_conversion.cc
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -33,7 +33,7 @@ bool FallbackToSnprintf(const Float v, const ConversionSpec &conv,
if (std::is_same<long double, Float>()) {
*fp++ = 'L';
}
- *fp++ = conv.conv().Char();
+ *fp++ = FormatConversionCharToChar(conv.conv());
*fp = 0;
assert(fp < fmt + sizeof(fmt));
}
@@ -100,9 +100,11 @@ bool ConvertNonNumericFloats(char sign_char, Float v,
char text[4], *ptr = text;
if (sign_char) *ptr++ = sign_char;
if (std::isnan(v)) {
- ptr = std::copy_n(conv.conv().upper() ? "NAN" : "nan", 3, ptr);
+ ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan",
+ 3, ptr);
} else if (std::isinf(v)) {
- ptr = std::copy_n(conv.conv().upper() ? "INF" : "inf", 3, ptr);
+ ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf",
+ 3, ptr);
} else {
return false;
}
@@ -399,7 +401,7 @@ bool FloatToSink(const Float v, const ConversionSpec &conv,
Buffer buffer;
- switch (conv.conv().id()) {
+ switch (conv.conv()) {
case ConversionChar::f:
case ConversionChar::F:
if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer,
@@ -416,7 +418,8 @@ bool FloatToSink(const Float v, const ConversionSpec &conv,
return FallbackToSnprintf(v, conv, sink);
}
if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back();
- PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer);
+ PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e',
+ &buffer);
break;
case ConversionChar::g:
@@ -447,7 +450,10 @@ bool FloatToSink(const Float v, const ConversionSpec &conv,
while (buffer.back() == '0') buffer.pop_back();
if (buffer.back() == '.') buffer.pop_back();
}
- if (exp) PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer);
+ if (exp) {
+ PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e',
+ &buffer);
+ }
break;
case ConversionChar::a:
diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc
index c4b527b..aab68db 100644
--- a/absl/strings/internal/str_format/parser.cc
+++ b/absl/strings/internal/str_format/parser.cc
@@ -17,7 +17,7 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
-using CC = ConversionChar::Id;
+using CC = ConversionChar;
using LM = LengthMod;
ABSL_CONST_INIT const ConvTag kTags[256] = {
@@ -322,7 +322,9 @@ bool ParsedFormatBase::MatchesConversions(
if (conv.width.is_from_arg() &&
!add_if_valid_conv(conv.width.get_from_arg(), '*'))
return false;
- if (!add_if_valid_conv(conv.arg_position, conv.conv.Char())) return false;
+ if (!add_if_valid_conv(conv.arg_position,
+ FormatConversionCharToChar(conv.conv)))
+ return false;
}
return used.size() == convs.size() || allow_ignored;
}
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h
index 6cbe257..45c90d1 100644
--- a/absl/strings/internal/str_format/parser.h
+++ b/absl/strings/internal/str_format/parser.h
@@ -67,7 +67,7 @@ struct UnboundConversion {
Flags flags;
LengthMod length_mod = LengthMod::none;
- ConversionChar conv;
+ ConversionChar conv = FormatConversionChar::kNone;
};
// Consume conversion spec prefix (not including '%') of [p, end) if valid.
@@ -79,10 +79,12 @@ const char* ConsumeUnboundConversion(const char* p, const char* end,
UnboundConversion* conv, int* next_arg);
// Helper tag class for the table below.
-// It allows fast `char -> ConversionChar/LengthMod` checking and conversions.
+// It allows fast `char -> ConversionChar/LengthMod` checking and
+// conversions.
class ConvTag {
public:
- constexpr ConvTag(ConversionChar::Id id) : tag_(id) {} // NOLINT
+ constexpr ConvTag(ConversionChar conversion_char) // NOLINT
+ : tag_(static_cast<int8_t>(conversion_char)) {}
// We invert the length modifiers to make them negative so that we can easily
// test for them.
constexpr ConvTag(LengthMod length_mod) // NOLINT
@@ -94,7 +96,7 @@ class ConvTag {
bool is_length() const { return tag_ < 0 && tag_ != -128; }
ConversionChar as_conv() const {
assert(is_conv());
- return ConversionChar::FromId(static_cast<ConversionChar::Id>(tag_));
+ return static_cast<ConversionChar>(tag_);
}
LengthMod as_length() const {
assert(is_length());
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc
index 4a8efd0..1b1ee03 100644
--- a/absl/strings/internal/str_format/parser_test.cc
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -41,7 +41,7 @@ TEST(LengthModTest, Names) {
TEST(ConversionCharTest, Names) {
struct Expectation {
- ConversionChar::Id id;
+ ConversionChar id;
char name;
};
// clang-format off
@@ -55,12 +55,10 @@ TEST(ConversionCharTest, Names) {
{ConversionChar::none, '\0'},
};
// clang-format on
- EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), ConversionChar::kNumValues);
for (auto e : kExpect) {
SCOPED_TRACE(e.name);
- ConversionChar v = ConversionChar::FromId(e.id);
- EXPECT_EQ(e.id, v.id());
- EXPECT_EQ(e.name, v.Char());
+ ConversionChar v = e.id;
+ EXPECT_EQ(e.name, FormatConversionCharToChar(v));
}
}
@@ -119,7 +117,7 @@ TEST_F(ConsumeUnboundConversionTest, BasicConversion) {
EXPECT_FALSE(Run("dd")); // no excess allowed
EXPECT_TRUE(Run("d"));
- EXPECT_EQ('d', o.conv.Char());
+ EXPECT_EQ('d', FormatConversionCharToChar(o.conv));
EXPECT_FALSE(o.width.is_from_arg());
EXPECT_LT(o.width.value(), 0);
EXPECT_FALSE(o.precision.is_from_arg());
@@ -160,7 +158,7 @@ TEST_F(ConsumeUnboundConversionTest, ArgPosition) {
TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) {
EXPECT_TRUE(Run("14d"));
- EXPECT_EQ('d', o.conv.Char());
+ EXPECT_EQ('d', FormatConversionCharToChar(o.conv));
EXPECT_FALSE(o.width.is_from_arg());
EXPECT_EQ(14, o.width.value());
EXPECT_FALSE(o.precision.is_from_arg());
@@ -330,7 +328,7 @@ struct SummarizeConsumer {
if (conv.precision.is_from_arg()) {
*out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
}
- *out += conv.conv.Char();
+ *out += FormatConversionCharToChar(conv.conv);
*out += "}";
return true;
}