tvl-depot/absl/strings/internal/str_format/extension.h
Abseil Team d95d156716 Export of internal Abseil changes
--
832be2d52d7695cf72fd70248909791fb8ad1003 by Gennadiy Rozental <rogeeff@google.com>:

Migrate some FlagImpl fields to bit fields.

To save padding space we are migrating some fields representing bool and enums into bit fields. Eventually we'll use remaining padding space for call_once control flag.

No other semantic changes made in this CL.

PiperOrigin-RevId: 293878165

--
09162bba5fd8eddacfd732d46fcfeb33074a259f by Samuel Benzaquen <sbenza@google.com>:

Correctly initialize the `length_mod` member.
Now that it is a raw enum, the default initialization is not enough.

PiperOrigin-RevId: 293827817

--
842b7b805d75c5ab670c52ccd7368cdeba11853d by Matthew Brown <matthewbr@google.com>:

Move str_format_internal::LengthMod from extension.h to parser.h; change to enum

PiperOrigin-RevId: 293697274
GitOrigin-RevId: 832be2d52d7695cf72fd70248909791fb8ad1003
Change-Id: I90899519e9480543e22638616fdf31a41e7f75c0
2020-02-07 16:58:49 -05:00

361 lines
9.2 KiB
C++

//
// 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
//
// 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_STR_FORMAT_EXTENSION_H_
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
#include <limits.h>
#include <cstddef>
#include <cstring>
#include <ostream>
#include "absl/base/config.h"
#include "absl/base/port.h"
#include "absl/strings/internal/str_format/output.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
class FormatRawSinkImpl {
public:
// Implicitly convert from any type that provides the hook function as
// described above.
template <typename T, decltype(str_format_internal::InvokeFlush(
std::declval<T*>(), string_view()))* = nullptr>
FormatRawSinkImpl(T* raw) // NOLINT
: sink_(raw), write_(&FormatRawSinkImpl::Flush<T>) {}
void Write(string_view s) { write_(sink_, s); }
template <typename T>
static FormatRawSinkImpl Extract(T s) {
return s.sink_;
}
private:
template <typename T>
static void Flush(void* r, string_view s) {
str_format_internal::InvokeFlush(static_cast<T*>(r), s);
}
void* sink_;
void (*write_)(void*, string_view);
};
// An abstraction to which conversions write their 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 <typename T>
T Wrap() {
return T(this);
}
template <typename T>
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();
}
};
// 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 ABSL_DLL 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_; }
ConversionChar 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, "");
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_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:
ConversionChar conv_;
Flags flags_;
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,
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<uint64_t>(a) | static_cast<uint64_t>(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<uint64_t>(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<uint64_t>(set) & static_cast<uint64_t>(c)) ==
static_cast<uint64_t>(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 <Conv C>
struct ConvertResult {
static constexpr Conv kConv = C;
bool value;
};
template <Conv C>
constexpr Conv ConvertResult<C>::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
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_