// // Copyright 2017 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 // // 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 ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ #include #include #include #include "absl/base/port.h" #include "absl/strings/internal/str_format/output.h" #include "absl/strings/string_view.h" class Cord; namespace absl { namespace str_format_internal { class FormatRawSinkImpl { public: // Implicitly convert from any type that provides the hook function as // described above. template (), string_view()))* = nullptr> FormatRawSinkImpl(T* raw) // NOLINT : sink_(raw), write_(&FormatRawSinkImpl::Flush) {} void Write(string_view s) { write_(sink_, s); } template static FormatRawSinkImpl Extract(T s) { return s.sink_; } private: template static void Flush(void* r, string_view s) { str_format_internal::InvokeFlush(static_cast(r), s); } void* sink_; void (*write_)(void*, string_view); }; // An abstraction to which conversions write their std::string data. class FormatSinkImpl { public: explicit FormatSinkImpl(FormatRawSinkImpl raw) : raw_(raw) {} ~FormatSinkImpl() { Flush(); } void Flush() { raw_.Write(string_view(buf_, pos_ - buf_)); pos_ = buf_; } void Append(size_t n, char c) { if (n == 0) return; size_ += n; auto raw_append = [&](size_t count) { memset(pos_, c, count); pos_ += count; }; while (n > Avail()) { n -= Avail(); if (Avail() > 0) { raw_append(Avail()); } Flush(); } raw_append(n); } void Append(string_view v) { size_t n = v.size(); if (n == 0) return; size_ += n; if (n >= Avail()) { Flush(); raw_.Write(v); return; } memcpy(pos_, v.data(), n); pos_ += n; } size_t size() const { return size_; } // Put 'v' to 'sink' with specified width, precision, and left flag. bool PutPaddedString(string_view v, int w, int p, bool l); template T Wrap() { return T(this); } template static FormatSinkImpl* Extract(T* s) { return s->sink_; } private: size_t Avail() const { return buf_ + sizeof(buf_) - pos_; } FormatRawSinkImpl raw_; size_t size_ = 0; char* pos_ = buf_; char buf_[1024]; }; struct Flags { bool basic : 1; // fastest conversion: no flags, width, or precision bool left : 1; // "-" bool show_pos : 1; // "+" bool sign_col : 1; // " " bool alt : 1; // "#" bool zero : 1; // "0" std::string ToString() const; friend std::ostream& operator<<(std::ostream& os, const Flags& v) { return os << v.ToString(); } }; struct LengthMod { public: enum Id : uint8_t { h, hh, l, ll, L, j, z, t, q, none }; static const size_t kNumValues = none + 1; LengthMod() : id_(none) {} // Index into the opaque array of LengthMod enums. // Requires: i < kNumValues static LengthMod FromIndex(size_t i) { return LengthMod(kSpecs[i].value); } static LengthMod FromId(Id id) { return LengthMod(id); } // The length modifier std::string associated with a specified LengthMod. string_view name() const { const Spec& spec = kSpecs[id_]; return {spec.name, spec.name_length}; } Id id() const { return id_; } friend bool operator==(const LengthMod& a, const LengthMod& b) { return a.id() == b.id(); } friend bool operator!=(const LengthMod& a, const LengthMod& b) { return !(a == b); } friend std::ostream& operator<<(std::ostream& os, const LengthMod& v) { return os << v.name(); } private: struct Spec { Id value; const char *name; size_t name_length; }; static const Spec kSpecs[]; explicit LengthMod(Id id) : id_(id) {} Id id_; }; // clang-format off #define ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ /* text */ \ X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \ /* ints */ \ X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \ X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \ /* floats */ \ X_VAL(f) X_SEP X_VAL(F) X_SEP X_VAL(e) X_SEP X_VAL(E) X_SEP \ 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 ConversionChar { public: enum Id : 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); } 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, ) #undef X_VAL default: break; } return ConversionChar(out_id); } 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; } } bool upper() const { switch (id()) { case X: case F: case E: case G: case A: return true; default: return false; } } bool is_signed() const { switch (id()) { case d: case 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; } } 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; } } 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; } private: struct Spec { Id value; char name; }; static const Spec kSpecs[]; explicit ConversionChar(Id id) : id_(id) {} Id id_; }; class ConversionSpec { public: Flags flags() const { return flags_; } LengthMod length_mod() const { return length_mod_; } ConversionChar conv() const { return conv_; } // Returns the specified width. If width is unspecfied, it returns a negative // value. int width() const { return width_; } // Returns the specified precision. If precision is unspecfied, it returns a // negative value. int precision() const { return precision_; } void set_flags(Flags f) { flags_ = f; } void set_length_mod(LengthMod lm) { length_mod_ = lm; } void set_conv(ConversionChar 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: Flags flags_; LengthMod length_mod_; ConversionChar conv_; int width_; int precision_; }; constexpr uint64_t ConversionCharToConvValue(char conv) { return #define CONV_SET_CASE(c) \ conv == #c[0] ? (uint64_t{1} << (1 + ConversionChar::Id::c)): ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) #undef CONV_SET_CASE conv == '*' ? 1 : 0; } enum class Conv : uint64_t { #define CONV_SET_CASE(c) c = ConversionCharToConvValue(#c[0]), ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) #undef CONV_SET_CASE // Used for width/precision '*' specification. star = ConversionCharToConvValue('*'), // Some predefined values: integral = d | i | u | o | x | X, floating = a | e | f | g | A | E | F | G, numeric = integral | floating, string = s, // absl:ignore(std::string) pointer = p }; // Type safe OR operator. // We need this for two reasons: // 1. operator| on enums makes them decay to integers and the result is an // integer. We need the result to stay as an enum. // 2. We use "enum class" which would not work even if we accepted the decay. constexpr Conv operator|(Conv a, Conv b) { return Conv(static_cast(a) | static_cast(b)); } // Get a conversion with a single character in it. constexpr Conv ConversionCharToConv(char c) { return Conv(ConversionCharToConvValue(c)); } // Checks whether `c` exists in `set`. constexpr bool Contains(Conv set, char c) { return (static_cast(set) & ConversionCharToConvValue(c)) != 0; } // Checks whether all the characters in `c` are contained in `set` constexpr bool Contains(Conv set, Conv c) { return (static_cast(set) & static_cast(c)) == static_cast(c); } // Return type of the AbslFormatConvert() functions. // The Conv template parameter is used to inform the framework of what // conversion characters are supported by that AbslFormatConvert routine. template struct ConvertResult { static constexpr Conv kConv = C; bool value; }; template constexpr Conv ConvertResult::kConv; // Return capacity - used, clipped to a minimum of 0. inline size_t Excess(size_t used, size_t capacity) { return used < capacity ? capacity - used : 0; } } // namespace str_format_internal } // namespace absl #endif // ABSL_STRINGS_STR_FORMAT_EXTENSION_H_