Commit 4491d606 by Abseil Team Committed by Shaindel Schwartz

Export of internal Abseil changes.

--
70f43a482d7d4ae4a255f17ca02b0106653dd600 by Shaindel Schwartz <shaindel@google.com>:

Internal change

PiperOrigin-RevId: 201571193

--
93e6e9c2e683158be49d9dd1f5cb1a91d0c0f556 by Abseil Team <absl-team@google.com>:

Internal change.

PiperOrigin-RevId: 201567108

--
fbd8ee94fbe9f2448e5adf5e88706f9c8216048f by Juemin Yang <jueminyang@google.com>:

str_format release

PiperOrigin-RevId: 201565129

--
387faa301555a8a888c4429df52734aa806dca46 by Abseil Team <absl-team@google.com>:

Adds a defaulted allocator parameter to the size_type constructor of InlinedVector

PiperOrigin-RevId: 201558711

--
39b15ea2c68d7129d70cbde7e71af900032595ec by Matt Calabrese <calabrese@google.com>:

Update the variant implementation to eliminate unnecessary checking on alternative access when the index is known or required to be correct.

PiperOrigin-RevId: 201529535

--
adab77f1f7bb363aa534297f22aae2b0f08889ea by Abseil Team <absl-team@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 201458388

--
a701dc0ba62e3cadf0de14203415b91df4ee8151 by Greg Falcon <gfalcon@google.com>:

Internal cleanup

PiperOrigin-RevId: 201394836

--
8a7191410b8f440fdfa27f722ff05e451502ab61 by Abseil Team <absl-team@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 201369269
GitOrigin-RevId: 70f43a482d7d4ae4a255f17ca02b0106653dd600
Change-Id: I8ab073b30b4e27405a3b6da2c826bb4f3f0b9af6
parent d89dba27
......@@ -89,7 +89,9 @@ class InlinedVector {
: allocator_and_tag_(alloc) {}
// Create a vector with n copies of value_type().
explicit InlinedVector(size_type n) : allocator_and_tag_(allocator_type()) {
explicit InlinedVector(size_type n,
const allocator_type& alloc = allocator_type())
: allocator_and_tag_(alloc) {
InitAssign(n);
}
......
......@@ -1763,4 +1763,30 @@ TEST(AllocatorSupportTest, ScopedAllocatorWorks) {
EXPECT_EQ(allocated, 0);
}
TEST(AllocatorSupportTest, SizeAllocConstructor) {
constexpr int inlined_size = 4;
using Alloc = CountingAllocator<int>;
using AllocVec = absl::InlinedVector<int, inlined_size, Alloc>;
{
auto len = inlined_size / 2;
int64_t allocated = 0;
auto v = AllocVec(len, Alloc(&allocated));
// Inline storage used; allocator should not be invoked
EXPECT_THAT(allocated, 0);
EXPECT_THAT(v, AllOf(SizeIs(len), Each(0)));
}
{
auto len = inlined_size * 2;
int64_t allocated = 0;
auto v = AllocVec(len, Alloc(&allocated));
// Out of line storage used; allocation of 8 elements expected
EXPECT_THAT(allocated, len * sizeof(int));
EXPECT_THAT(v, AllOf(SizeIs(len), Each(0)));
}
}
} // anonymous namespace
......@@ -492,3 +492,142 @@ cc_test(
"@com_github_google_benchmark//:benchmark_main",
],
)
cc_library(
name = "str_format",
hdrs = [
"str_format.h",
],
copts = ABSL_DEFAULT_COPTS,
deps = [
":str_format_internal",
],
)
cc_library(
name = "str_format_internal",
srcs = [
"internal/str_format/arg.cc",
"internal/str_format/bind.cc",
"internal/str_format/extension.cc",
"internal/str_format/float_conversion.cc",
"internal/str_format/output.cc",
"internal/str_format/parser.cc",
],
hdrs = [
"internal/str_format/arg.h",
"internal/str_format/bind.h",
"internal/str_format/checker.h",
"internal/str_format/extension.h",
"internal/str_format/float_conversion.h",
"internal/str_format/output.h",
"internal/str_format/parser.h",
],
copts = ABSL_DEFAULT_COPTS,
visibility = ["//visibility:private"],
deps = [
":strings",
"//absl/base:core_headers",
"//absl/container:inlined_vector",
"//absl/meta:type_traits",
"//absl/numeric:int128",
"//absl/types:span",
],
)
cc_test(
name = "str_format_test",
srcs = ["str_format_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":str_format",
":strings",
"//absl/base:core_headers",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "str_format_extension_test",
srcs = [
"internal/str_format/extension_test.cc",
],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":str_format",
":str_format_internal",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "str_format_arg_test",
srcs = ["internal/str_format/arg_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":str_format",
":str_format_internal",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "str_format_bind_test",
srcs = ["internal/str_format/bind_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":str_format_internal",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "str_format_checker_test",
srcs = ["internal/str_format/checker_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":str_format",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "str_format_convert_test",
size = "small",
srcs = ["internal/str_format/convert_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":str_format_internal",
"//absl/numeric:int128",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "str_format_output_test",
srcs = ["internal/str_format/output_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":str_format_internal",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "str_format_parser_test",
srcs = ["internal/str_format/parser_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":str_format_internal",
"//absl/base:core_headers",
"@com_google_googletest//:gtest_main",
],
)
......@@ -81,6 +81,58 @@ absl_library(
strings
)
# add str_format library
absl_library(
TARGET
absl_str_format
SOURCES
"str_format.h"
PUBLIC_LIBRARIES
str_format_internal
EXPORT_NAME
str_format
)
# str_format_internal
absl_library(
TARGET
str_format_internal
SOURCES
"internal/str_format/arg.cc"
"internal/str_format/bind.cc"
"internal/str_format/extension.cc"
"internal/str_format/float_conversion.cc"
"internal/str_format/output.cc"
"internal/str_format/parser.cc"
"internal/str_format/arg.h"
"internal/str_format/bind.h"
"internal/str_format/checker.h"
"internal/str_format/extension.h"
"internal/str_format/float_conversion.h"
"internal/str_format/output.h"
"internal/str_format/parser.h"
PUBLIC_LIBRARIES
str_format_extension_internal
absl::strings
absl::base
absl::numeric
absl::container
absl::span
)
# str_format_extension_internal
absl_library(
TARGET
str_format_extension_internal
SOURCES
"internal/str_format/extension.cc"
"internal/str_format/extension.h"
"internal/str_format/output.cc"
"internal/str_format/output.h"
PUBLIC_LIBRARIES
absl::base
absl::strings
)
#
## TESTS
......@@ -347,3 +399,68 @@ absl_test(
PUBLIC_LIBRARIES
${CHARCONV_BIGINT_TEST_PUBLIC_LIBRARIES}
)
# test str_format_test
absl_test(
TARGET
str_format_test
SOURCES
"str_format_test.cc"
PUBLIC_LIBRARIES
absl::base
absl::str_format
absl::strings
)
# test str_format_bind_test
absl_test(
TARGET
str_format_bind_test
SOURCES
"internal/str_format/bind_test.cc"
PUBLIC_LIBRARIES
str_format_internal
)
# test str_format_checker_test
absl_test(
TARGET
str_format_checker_test
SOURCES
"internal/str_format/checker_test.cc"
PUBLIC_LIBRARIES
absl::str_format
)
# test str_format_convert_test
absl_test(
TARGET
str_format_convert_test
SOURCES
"internal/str_format/convert_test.cc"
PUBLIC_LIBRARIES
str_format_internal
absl::numeric
)
# test str_format_output_test
absl_test(
TARGET
str_format_output_test
SOURCES
"internal/str_format/output_test.cc"
PUBLIC_LIBRARIES
str_format_extension_internal
)
# test str_format_parser_test
absl_test(
TARGET
str_format_parser_test
SOURCES
"internal/str_format/parser_test.cc"
PUBLIC_LIBRARIES
str_format_internal
absl::base
)
// 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
//
#include "absl/strings/internal/str_format/arg.h"
#include <ostream>
#include <string>
#include "gtest/gtest.h"
#include "absl/strings/str_format.h"
namespace absl {
namespace str_format_internal {
namespace {
class FormatArgImplTest : public ::testing::Test {
public:
enum Color { kRed, kGreen, kBlue };
static const char *hi() { return "hi"; }
};
TEST_F(FormatArgImplTest, ToInt) {
int out = 0;
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out));
EXPECT_EQ(1, out);
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(-1), &out));
EXPECT_EQ(-1, out);
EXPECT_TRUE(
FormatArgImplFriend::ToInt(FormatArgImpl(static_cast<char>(64)), &out));
EXPECT_EQ(64, out);
EXPECT_TRUE(FormatArgImplFriend::ToInt(
FormatArgImpl(static_cast<unsigned long long>(123456)), &out)); // NOLINT
EXPECT_EQ(123456, out);
EXPECT_TRUE(FormatArgImplFriend::ToInt(
FormatArgImpl(static_cast<unsigned long long>( // NOLINT
std::numeric_limits<int>::max()) +
1),
&out));
EXPECT_EQ(std::numeric_limits<int>::max(), out);
EXPECT_TRUE(FormatArgImplFriend::ToInt(
FormatArgImpl(static_cast<long long>( // NOLINT
std::numeric_limits<int>::min()) -
10),
&out));
EXPECT_EQ(std::numeric_limits<int>::min(), out);
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(false), &out));
EXPECT_EQ(0, out);
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(true), &out));
EXPECT_EQ(1, out);
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(2.2), &out));
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(3.2f), &out));
EXPECT_FALSE(FormatArgImplFriend::ToInt(
FormatArgImpl(static_cast<int *>(nullptr)), &out));
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out));
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out));
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out));
EXPECT_EQ(2, out);
}
extern const char kMyArray[];
TEST_F(FormatArgImplTest, CharArraysDecayToCharPtr) {
const char* a = "";
EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("")));
EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("A")));
EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl("ABC")));
EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyArray)));
}
TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) {
auto expected = FormatArgImplFriend::GetVTablePtrForTest(
FormatArgImpl(static_cast<void *>(nullptr)));
EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(
FormatArgImpl(static_cast<int *>(nullptr))),
expected);
EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(
FormatArgImpl(static_cast<volatile int *>(nullptr))),
expected);
auto p = static_cast<void (*)()>([] {});
EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(p)),
expected);
}
TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) {
std::string s;
FormatSinkImpl sink(&s);
ConversionSpec conv;
conv.set_conv(ConversionChar::FromChar('s'));
conv.set_flags(Flags());
conv.set_width(-1);
conv.set_precision(-1);
EXPECT_TRUE(
FormatArgImplFriend::Convert(FormatArgImpl(kMyArray), conv, &sink));
sink.Flush();
EXPECT_EQ("ABCDE", s);
}
const char kMyArray[] = "ABCDE";
} // namespace
} // namespace str_format_internal
} // namespace absl
#include "absl/strings/internal/str_format/bind.h"
#include <cerrno>
#include <limits>
#include <sstream>
#include <string>
namespace absl {
namespace str_format_internal {
namespace {
inline bool BindFromPosition(int position, int* value,
absl::Span<const FormatArgImpl> pack) {
assert(position > 0);
if (static_cast<size_t>(position) > pack.size()) {
return false;
}
// -1 because positions are 1-based
return FormatArgImplFriend::ToInt(pack[position - 1], value);
}
class ArgContext {
public:
explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {}
// Fill 'bound' with the results of applying the context's argument pack
// to the specified 'props'. We synthesize a BoundConversion by
// lining up a UnboundConversion with a user argument. We also
// resolve any '*' specifiers for width and precision, so after
// this call, 'bound' has all the information it needs to be formatted.
// Returns false on failure.
bool Bind(const UnboundConversion *props, BoundConversion *bound);
private:
absl::Span<const FormatArgImpl> pack_;
};
inline bool ArgContext::Bind(const UnboundConversion* unbound,
BoundConversion* bound) {
const FormatArgImpl* arg = nullptr;
int arg_position = unbound->arg_position;
if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
arg = &pack_[arg_position - 1]; // 1-based
if (!unbound->flags.basic) {
int width = unbound->width.value();
bool force_left = false;
if (unbound->width.is_from_arg()) {
if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_))
return false;
if (width < 0) {
// "A negative field width is taken as a '-' flag followed by a
// positive field width."
force_left = true;
width = -width;
}
}
int precision = unbound->precision.value();
if (unbound->precision.is_from_arg()) {
if (!BindFromPosition(unbound->precision.get_from_arg(), &precision,
pack_))
return false;
}
bound->set_width(width);
bound->set_precision(precision);
bound->set_flags(unbound->flags);
if (force_left)
bound->set_left(true);
} else {
bound->set_flags(unbound->flags);
bound->set_width(-1);
bound->set_precision(-1);
}
bound->set_length_mod(unbound->length_mod);
bound->set_conv(unbound->conv);
bound->set_arg(arg);
return true;
}
template <typename Converter>
class ConverterConsumer {
public:
ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack)
: converter_(converter), arg_context_(pack) {}
bool Append(string_view s) {
converter_.Append(s);
return true;
}
bool ConvertOne(const UnboundConversion& conv, string_view conv_string) {
BoundConversion bound;
if (!arg_context_.Bind(&conv, &bound)) return false;
return converter_.ConvertOne(bound, conv_string);
}
private:
Converter converter_;
ArgContext arg_context_;
};
template <typename Converter>
bool ConvertAll(const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args,
const Converter& converter) {
const ParsedFormatBase* pc = format.parsed_conversion();
if (pc)
return pc->ProcessFormat(ConverterConsumer<Converter>(converter, args));
return ParseFormatString(format.str(),
ConverterConsumer<Converter>(converter, args));
}
class DefaultConverter {
public:
explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {}
void Append(string_view s) const { sink_->Append(s); }
bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_);
}
private:
FormatSinkImpl* sink_;
};
class SummarizingConverter {
public:
explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {}
void Append(string_view s) const { sink_->Append(s); }
bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
UntypedFormatSpecImpl spec("%d");
std::ostringstream ss;
ss << "{" << Streamable(spec, {*bound.arg()}) << ":" << bound.flags();
if (bound.width() >= 0) ss << bound.width();
if (bound.precision() >= 0) ss << "." << bound.precision();
ss << bound.length_mod() << bound.conv() << "}";
Append(ss.str());
return true;
}
private:
FormatSinkImpl* sink_;
};
} // namespace
bool BindWithPack(const UnboundConversion* props,
absl::Span<const FormatArgImpl> pack,
BoundConversion* bound) {
return ArgContext(pack).Bind(props, bound);
}
std::string Summarize(const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args) {
typedef SummarizingConverter Converter;
std::string out;
{
// inner block to destroy sink before returning out. It ensures a last
// flush.
FormatSinkImpl sink(&out);
if (!ConvertAll(format, args, Converter(&sink))) {
sink.Flush();
out.clear();
}
}
return out;
}
bool FormatUntyped(FormatRawSinkImpl raw_sink,
const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args) {
FormatSinkImpl sink(raw_sink);
using Converter = DefaultConverter;
if (!ConvertAll(format, args, Converter(&sink))) {
sink.Flush();
return false;
}
return true;
}
std::ostream& Streamable::Print(std::ostream& os) const {
if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit);
return os;
}
std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args) {
size_t orig = out->size();
if (!FormatUntyped(out, format, args)) out->resize(orig);
return *out;
}
int FprintF(std::FILE* output, const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args) {
FILERawSink sink(output);
if (!FormatUntyped(&sink, format, args)) {
errno = EINVAL;
return -1;
}
if (sink.error()) {
errno = sink.error();
return -1;
}
if (sink.count() > std::numeric_limits<int>::max()) {
errno = EFBIG;
return -1;
}
return static_cast<int>(sink.count());
}
int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args) {
BufferRawSink sink(output, size ? size - 1 : 0);
if (!FormatUntyped(&sink, format, args)) {
errno = EINVAL;
return -1;
}
size_t total = sink.total_written();
if (size) output[std::min(total, size - 1)] = 0;
return static_cast<int>(total);
}
} // namespace str_format_internal
} // namespace absl
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
#include <array>
#include <cstdio>
#include <sstream>
#include <string>
#include "absl/base/port.h"
#include "absl/container/inlined_vector.h"
#include "absl/strings/internal/str_format/arg.h"
#include "absl/strings/internal/str_format/checker.h"
#include "absl/strings/internal/str_format/parser.h"
#include "absl/types/span.h"
namespace absl {
class UntypedFormatSpec;
namespace str_format_internal {
class BoundConversion : public ConversionSpec {
public:
const FormatArgImpl* arg() const { return arg_; }
void set_arg(const FormatArgImpl* a) { arg_ = a; }
private:
const FormatArgImpl* arg_;
};
// This is the type-erased class that the implementation uses.
class UntypedFormatSpecImpl {
public:
UntypedFormatSpecImpl() = delete;
explicit UntypedFormatSpecImpl(string_view s) : str_(s), pc_() {}
explicit UntypedFormatSpecImpl(
const str_format_internal::ParsedFormatBase* pc)
: pc_(pc) {}
string_view str() const { return str_; }
const str_format_internal::ParsedFormatBase* parsed_conversion() const {
return pc_;
}
template <typename T>
static const UntypedFormatSpecImpl& Extract(const T& s) {
return s.spec_;
}
private:
string_view str_;
const str_format_internal::ParsedFormatBase* pc_;
};
template <typename T, typename...>
struct MakeDependent {
using type = T;
};
// Implicitly convertible from `const char*`, `string_view`, and the
// `ExtendedParsedFormat` type. This abstraction allows all format functions to
// operate on any without providing too many overloads.
template <typename... Args>
class FormatSpecTemplate
: public MakeDependent<UntypedFormatSpec, Args...>::type {
using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type;
public:
#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
// Honeypot overload for when the std::string is not constexpr.
// We use the 'unavailable' attribute to give a better compiler error than
// just 'method is deleted'.
FormatSpecTemplate(...) // NOLINT
__attribute__((unavailable("Format std::string is not constexpr.")));
// Honeypot overload for when the format is constexpr and invalid.
// We use the 'unavailable' attribute to give a better compiler error than
// just 'method is deleted'.
// To avoid checking the format twice, we just check that the format is
// constexpr. If is it valid, then the overload below will kick in.
// We add the template here to make this overload have lower priority.
template <typename = void>
FormatSpecTemplate(const char* s) // NOLINT
__attribute__((
enable_if(str_format_internal::EnsureConstexpr(s), "constexpr trap"),
unavailable(
"Format specified does not match the arguments passed.")));
template <typename T = void>
FormatSpecTemplate(string_view s) // NOLINT
__attribute__((enable_if(str_format_internal::EnsureConstexpr(s),
"constexpr trap"))) {
static_assert(sizeof(T*) == 0,
"Format specified does not match the arguments passed.");
}
// Good format overload.
FormatSpecTemplate(const char* s) // NOLINT
__attribute__((enable_if(ValidFormatImpl<ArgumentToConv<Args>()...>(s),
"bad format trap")))
: Base(s) {}
FormatSpecTemplate(string_view s) // NOLINT
__attribute__((enable_if(ValidFormatImpl<ArgumentToConv<Args>()...>(s),
"bad format trap")))
: Base(s) {}
#else // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
FormatSpecTemplate(const char* s) : Base(s) {} // NOLINT
FormatSpecTemplate(string_view s) : Base(s) {} // NOLINT
#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
template <Conv... C, typename = typename std::enable_if<
sizeof...(C) == sizeof...(Args) &&
AllOf(Contains(ArgumentToConv<Args>(),
C)...)>::type>
FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT
: Base(&pc) {}
};
template <typename... Args>
struct FormatSpecDeductionBarrier {
using type = FormatSpecTemplate<Args...>;
};
class Streamable {
public:
Streamable(const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args)
: format_(format), args_(args.begin(), args.end()) {}
std::ostream& Print(std::ostream& os) const;
friend std::ostream& operator<<(std::ostream& os, const Streamable& l) {
return l.Print(os);
}
private:
const UntypedFormatSpecImpl& format_;
absl::InlinedVector<FormatArgImpl, 4> args_;
};
// for testing
std::string Summarize(const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args);
bool BindWithPack(const UnboundConversion* props,
absl::Span<const FormatArgImpl> pack, BoundConversion* bound);
bool FormatUntyped(FormatRawSinkImpl raw_sink,
const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args);
std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args);
inline std::string FormatPack(const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args) {
std::string out;
AppendPack(&out, format, args);
return out;
}
int FprintF(std::FILE* output, const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args);
int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl& format,
absl::Span<const FormatArgImpl> args);
// Returned by Streamed(v). Converts via '%s' to the std::string created
// by std::ostream << v.
template <typename T>
class StreamedWrapper {
public:
explicit StreamedWrapper(const T& v) : v_(v) { }
private:
template <typename S>
friend ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<S>& v,
const ConversionSpec& conv,
FormatSinkImpl* out);
const T& v_;
};
} // namespace str_format_internal
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
#include "absl/strings/internal/str_format/bind.h"
#include <string.h>
#include "gtest/gtest.h"
namespace absl {
namespace str_format_internal {
namespace {
template <typename T, size_t N>
size_t ArraySize(T (&)[N]) {
return N;
}
class FormatBindTest : public ::testing::Test {
public:
bool Extract(const char *s, UnboundConversion *props, int *next) const {
absl::string_view src = s;
return ConsumeUnboundConversion(&src, props, next) && src.empty();
}
};
TEST_F(FormatBindTest, BindSingle) {
struct Expectation {
int line;
const char *fmt;
int ok_phases;
const FormatArgImpl *arg;
int width;
int precision;
int next_arg;
};
const int no = -1;
const int ia[] = { 10, 20, 30, 40};
const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]),
FormatArgImpl(ia[2]), FormatArgImpl(ia[3])};
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
const Expectation kExpect[] = {
{__LINE__, "d", 2, &args[0], no, no, 2},
{__LINE__, "4d", 2, &args[0], 4, no, 2},
{__LINE__, ".5d", 2, &args[0], no, 5, 2},
{__LINE__, "4.5d", 2, &args[0], 4, 5, 2},
{__LINE__, "*d", 2, &args[1], 10, no, 3},
{__LINE__, ".*d", 2, &args[1], no, 10, 3},
{__LINE__, "*.*d", 2, &args[2], 10, 20, 4},
{__LINE__, "1$d", 2, &args[0], no, no, 0},
{__LINE__, "2$d", 2, &args[1], no, no, 0},
{__LINE__, "3$d", 2, &args[2], no, no, 0},
{__LINE__, "4$d", 2, &args[3], no, no, 0},
{__LINE__, "2$*1$d", 2, &args[1], 10, no, 0},
{__LINE__, "2$*2$d", 2, &args[1], 20, no, 0},
{__LINE__, "2$*3$d", 2, &args[1], 30, no, 0},
{__LINE__, "2$.*1$d", 2, &args[1], no, 10, 0},
{__LINE__, "2$.*2$d", 2, &args[1], no, 20, 0},
{__LINE__, "2$.*3$d", 2, &args[1], no, 30, 0},
{__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0},
{__LINE__, "2$*2$.*2$d", 2, &args[1], 20, 20, 0},
{__LINE__, "2$*1$.*3$d", 2, &args[1], 10, 30, 0},
{__LINE__, "2$*3$.*1$d", 2, &args[1], 30, 10, 0},
{__LINE__, "1$*d", 0}, // indexed, then positional
{__LINE__, "*2$d", 0}, // positional, then indexed
{__LINE__, "6$d", 1}, // arg position out of bounds
{__LINE__, "1$6$d", 0}, // width position incorrectly specified
{__LINE__, "1$.6$d", 0}, // precision position incorrectly specified
{__LINE__, "1$*6$d", 1}, // width position out of bounds
{__LINE__, "1$.*6$d", 1}, // precision position out of bounds
};
#pragma GCC diagnostic pop
for (const Expectation &e : kExpect) {
SCOPED_TRACE(e.line);
SCOPED_TRACE(e.fmt);
UnboundConversion props;
BoundConversion bound;
int ok_phases = 0;
int next = 0;
if (Extract(e.fmt, &props, &next)) {
++ok_phases;
if (BindWithPack(&props, args, &bound)) {
++ok_phases;
}
}
EXPECT_EQ(e.ok_phases, ok_phases);
if (e.ok_phases < 2) continue;
if (e.arg != nullptr) {
EXPECT_EQ(e.arg, bound.arg());
}
EXPECT_EQ(e.width, bound.width());
EXPECT_EQ(e.precision, bound.precision());
}
}
TEST_F(FormatBindTest, FormatPack) {
struct Expectation {
int line;
const char *fmt;
const char *summary;
};
const int ia[] = { 10, 20, 30, 40, -10 };
const FormatArgImpl args[] = {FormatArgImpl(ia[0]), FormatArgImpl(ia[1]),
FormatArgImpl(ia[2]), FormatArgImpl(ia[3]),
FormatArgImpl(ia[4])};
const Expectation kExpect[] = {
{__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"},
{__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"},
{__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"},
{__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"},
{__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"},
{__LINE__, "a%.*fb", "a{20:.10f}b"},
{__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"},
{__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"},
{__LINE__, "a%04ldb", "a{10:04ld}b"},
{__LINE__, "a%-#04lldb", "a{10:-#04lld}b"},
{__LINE__, "a%1$*5$db", "a{10:-10d}b"},
{__LINE__, "a%1$.*5$db", "a{10:d}b"},
};
for (const Expectation &e : kExpect) {
absl::string_view fmt = e.fmt;
SCOPED_TRACE(e.line);
SCOPED_TRACE(e.fmt);
UntypedFormatSpecImpl format(fmt);
EXPECT_EQ(e.summary,
str_format_internal::Summarize(format, absl::MakeSpan(args)))
<< "line:" << e.line;
}
}
} // namespace
} // namespace str_format_internal
} // namespace absl
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/str_format.h"
namespace absl {
namespace str_format_internal {
namespace {
std::string ConvToString(Conv conv) {
std::string out;
#define CONV_SET_CASE(c) \
if (Contains(conv, Conv::c)) out += #c;
ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
#undef CONV_SET_CASE
if (Contains(conv, Conv::star)) out += "*";
return out;
}
TEST(StrFormatChecker, ArgumentToConv) {
Conv conv = ArgumentToConv<std::string>();
EXPECT_EQ(ConvToString(conv), "s");
conv = ArgumentToConv<const char*>();
EXPECT_EQ(ConvToString(conv), "sp");
conv = ArgumentToConv<double>();
EXPECT_EQ(ConvToString(conv), "fFeEgGaA");
conv = ArgumentToConv<int>();
EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaA*");
conv = ArgumentToConv<std::string*>();
EXPECT_EQ(ConvToString(conv), "p");
}
#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
struct Case {
bool result;
const char* format;
};
template <typename... Args>
constexpr Case ValidFormat(const char* format) {
return {ValidFormatImpl<ArgumentToConv<Args>()...>(format), format};
}
TEST(StrFormatChecker, ValidFormat) {
// We want to make sure these expressions are constexpr and they have the
// expected value.
// If they are not constexpr the attribute will just ignore them and not give
// a compile time error.
enum e {};
enum class e2 {};
constexpr Case trues[] = {
ValidFormat<>("abc"), //
ValidFormat<e>("%d"), //
ValidFormat<e2>("%d"), //
ValidFormat<int>("%% %d"), //
ValidFormat<int>("%ld"), //
ValidFormat<int>("%lld"), //
ValidFormat<std::string>("%s"), //
ValidFormat<std::string>("%10s"), //
ValidFormat<int>("%.10x"), //
ValidFormat<int, int>("%*.3x"), //
ValidFormat<int>("%1.d"), //
ValidFormat<int>("%.d"), //
ValidFormat<int, double>("%d %g"), //
ValidFormat<int, std::string>("%*s"), //
ValidFormat<int, double>("%.*f"), //
ValidFormat<void (*)(), volatile int*>("%p %p"), //
ValidFormat<string_view, const char*, double, void*>(
"string_view=%s const char*=%s double=%f void*=%p)"),
ValidFormat<int>("%% %1$d"), //
ValidFormat<int>("%1$ld"), //
ValidFormat<int>("%1$lld"), //
ValidFormat<std::string>("%1$s"), //
ValidFormat<std::string>("%1$10s"), //
ValidFormat<int>("%1$.10x"), //
ValidFormat<int>("%1$*1$.*1$d"), //
ValidFormat<int, int>("%1$*2$.3x"), //
ValidFormat<int>("%1$1.d"), //
ValidFormat<int>("%1$.d"), //
ValidFormat<double, int>("%2$d %1$g"), //
ValidFormat<int, std::string>("%2$*1$s"), //
ValidFormat<int, double>("%2$.*1$f"), //
ValidFormat<void*, string_view, const char*, double>(
"string_view=%2$s const char*=%3$s double=%4$f void*=%1$p "
"repeat=%3$s)")};
for (Case c : trues) {
EXPECT_TRUE(c.result) << c.format;
}
constexpr Case falses[] = {
ValidFormat<int>(""), //
ValidFormat<e>("%s"), //
ValidFormat<e2>("%s"), //
ValidFormat<>("%s"), //
ValidFormat<>("%r"), //
ValidFormat<int>("%s"), //
ValidFormat<int>("%.1.d"), //
ValidFormat<int>("%*1d"), //
ValidFormat<int>("%1-d"), //
ValidFormat<std::string, int>("%*s"), //
ValidFormat<int>("%*d"), //
ValidFormat<std::string>("%p"), //
ValidFormat<int (*)(int)>("%d"), //
ValidFormat<>("%3$d"), //
ValidFormat<>("%1$r"), //
ValidFormat<int>("%1$s"), //
ValidFormat<int>("%1$.1.d"), //
ValidFormat<int>("%1$*2$1d"), //
ValidFormat<int>("%1$1-d"), //
ValidFormat<std::string, int>("%2$*1$s"), //
ValidFormat<std::string>("%1$p"),
ValidFormat<int, int>("%d %2$d"), //
};
for (Case c : falses) {
EXPECT_FALSE(c.result) << c.format;
}
}
TEST(StrFormatChecker, LongFormat) {
#define CHARS_X_40 "1234567890123456789012345678901234567890"
#define CHARS_X_400 \
CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 CHARS_X_40 \
CHARS_X_40 CHARS_X_40 CHARS_X_40
#define CHARS_X_4000 \
CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400 \
CHARS_X_400 CHARS_X_400 CHARS_X_400 CHARS_X_400
constexpr char long_format[] =
CHARS_X_4000 "%d" CHARS_X_4000 "%s" CHARS_X_4000;
constexpr bool is_valid = ValidFormat<int, std::string>(long_format).result;
EXPECT_TRUE(is_valid);
}
#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
} // namespace
} // namespace str_format_internal
} // namespace absl
//
// 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.
#include "absl/strings/internal/str_format/extension.h"
#include <errno.h>
#include <algorithm>
#include <string>
namespace absl {
namespace str_format_internal {
namespace {
// clang-format off
#define ABSL_LENGTH_MODS_EXPAND_ \
X_VAL(h) X_SEP \
X_VAL(hh) X_SEP \
X_VAL(l) X_SEP \
X_VAL(ll) X_SEP \
X_VAL(L) X_SEP \
X_VAL(j) X_SEP \
X_VAL(z) X_SEP \
X_VAL(t) X_SEP \
X_VAL(q)
// clang-format on
} // namespace
const LengthMod::Spec LengthMod::kSpecs[] = {
#define X_VAL(id) { LengthMod::id, #id, strlen(#id) }
#define X_SEP ,
ABSL_LENGTH_MODS_EXPAND_, {LengthMod::none, "", 0}
#undef X_VAL
#undef X_SEP
};
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 ? "-" : "");
s.append(show_pos ? "+" : "");
s.append(sign_col ? " " : "");
s.append(alt ? "#" : "");
s.append(zero ? "0" : "");
return s;
}
const size_t LengthMod::kNumValues;
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;
size_t n = v.size();
if (p >= 0) n = std::min(n, static_cast<size_t>(p));
string_view shown(v.data(), n);
space_remaining = Excess(shown.size(), space_remaining);
if (!l) Append(space_remaining, ' ');
Append(shown);
if (l) Append(space_remaining, ' ');
return true;
}
} // namespace str_format_internal
} // namespace absl
//
// 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.
//
#include "absl/strings/internal/str_format/extension.h"
#include <random>
#include <string>
#include "absl/strings/str_format.h"
#include "gtest/gtest.h"
namespace {
std::string MakeRandomString(size_t len) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis('a', 'z');
std::string s(len, '0');
for (char& c : s) {
c = dis(gen);
}
return s;
}
TEST(FormatExtensionTest, SinkAppendSubstring) {
for (size_t chunk_size : {1, 10, 100, 1000, 10000}) {
std::string expected, actual;
absl::str_format_internal::FormatSinkImpl sink(&actual);
for (size_t chunks = 0; chunks < 10; ++chunks) {
std::string rand = MakeRandomString(chunk_size);
expected += rand;
sink.Append(rand);
}
sink.Flush();
EXPECT_EQ(actual, expected);
}
}
TEST(FormatExtensionTest, SinkAppendChars) {
for (size_t chunk_size : {1, 10, 100, 1000, 10000}) {
std::string expected, actual;
absl::str_format_internal::FormatSinkImpl sink(&actual);
for (size_t chunks = 0; chunks < 10; ++chunks) {
std::string rand = MakeRandomString(1);
expected.append(chunk_size, rand[0]);
sink.Append(chunk_size, rand[0]);
}
sink.Flush();
EXPECT_EQ(actual, expected);
}
}
} // namespace
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
#include "absl/strings/internal/str_format/extension.h"
namespace absl {
namespace str_format_internal {
bool ConvertFloatImpl(float v, const ConversionSpec &conv,
FormatSinkImpl *sink);
bool ConvertFloatImpl(double v, const ConversionSpec &conv,
FormatSinkImpl *sink);
bool ConvertFloatImpl(long double v, const ConversionSpec &conv,
FormatSinkImpl *sink);
} // namespace str_format_internal
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
// 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.
#include "absl/strings/internal/str_format/output.h"
#include <errno.h>
#include <cstring>
namespace absl {
namespace str_format_internal {
void BufferRawSink::Write(string_view v) {
size_t to_write = std::min(v.size(), size_);
std::memcpy(buffer_, v.data(), to_write);
buffer_ += to_write;
size_ -= to_write;
total_written_ += v.size();
}
void FILERawSink::Write(string_view v) {
while (!v.empty() && !error_) {
if (size_t result = std::fwrite(v.data(), 1, v.size(), output_)) {
// Some progress was made.
count_ += result;
v.remove_prefix(result);
} else {
// Some error occurred.
if (errno != EINTR) {
error_ = errno;
}
}
}
}
} // namespace str_format_internal
} // namespace absl
// 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.
//
// Output extension hooks for the Format library.
// `internal::InvokeFlush` calls the appropriate flush function for the
// specified output argument.
// `BufferRawSink` is a simple output sink for a char buffer. Used by SnprintF.
// `FILERawSink` is a std::FILE* based sink. Used by PrintF and FprintF.
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
#include <cstdio>
#include <ostream>
#include <string>
#include "absl/base/port.h"
#include "absl/strings/string_view.h"
class Cord;
namespace absl {
namespace str_format_internal {
// RawSink implementation that writes into a char* buffer.
// It will not overflow the buffer, but will keep the total count of chars
// that would have been written.
class BufferRawSink {
public:
BufferRawSink(char* buffer, size_t size) : buffer_(buffer), size_(size) {}
size_t total_written() const { return total_written_; }
void Write(string_view v);
private:
char* buffer_;
size_t size_;
size_t total_written_ = 0;
};
// RawSink implementation that writes into a FILE*.
// It keeps track of the total number of bytes written and any error encountered
// during the writes.
class FILERawSink {
public:
explicit FILERawSink(std::FILE* output) : output_(output) {}
void Write(string_view v);
size_t count() const { return count_; }
int error() const { return error_; }
private:
std::FILE* output_;
int error_ = 0;
size_t count_ = 0;
};
// Provide RawSink integration with common types from the STL.
inline void AbslFormatFlush(std::string* out, string_view s) {
out->append(s.begin(), s.size());
}
inline void AbslFormatFlush(std::ostream* out, string_view s) {
out->write(s.begin(), s.size());
}
template <class AbslCord, typename = typename std::enable_if<
std::is_same<AbslCord, ::Cord>::value>::type>
inline void AbslFormatFlush(AbslCord* out, string_view s) {
out->Append(s);
}
inline void AbslFormatFlush(FILERawSink* sink, string_view v) {
sink->Write(v);
}
inline void AbslFormatFlush(BufferRawSink* sink, string_view v) {
sink->Write(v);
}
template <typename T>
auto InvokeFlush(T* out, string_view s)
-> decltype(str_format_internal::AbslFormatFlush(out, s)) {
str_format_internal::AbslFormatFlush(out, s);
}
} // namespace str_format_internal
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
// 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.
#include "absl/strings/internal/str_format/output.h"
#include <sstream>
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace absl {
namespace {
TEST(InvokeFlush, String) {
std::string str = "ABC";
str_format_internal::InvokeFlush(&str, "DEF");
EXPECT_EQ(str, "ABCDEF");
#if UTIL_FORMAT_HAS_GLOBAL_STRING
std::string str2 = "ABC";
str_format_internal::InvokeFlush(&str2, "DEF");
EXPECT_EQ(str2, "ABCDEF");
#endif // UTIL_FORMAT_HAS_GLOBAL_STRING
}
TEST(InvokeFlush, Stream) {
std::stringstream str;
str << "ABC";
str_format_internal::InvokeFlush(&str, "DEF");
EXPECT_EQ(str.str(), "ABCDEF");
}
TEST(BufferRawSink, Limits) {
char buf[16];
{
std::fill(std::begin(buf), std::end(buf), 'x');
str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
str_format_internal::InvokeFlush(&bufsink, "Hello World237");
EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx");
}
{
std::fill(std::begin(buf), std::end(buf), 'x');
str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
str_format_internal::InvokeFlush(&bufsink, "Hello World237237");
EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x");
}
{
std::fill(std::begin(buf), std::end(buf), 'x');
str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
str_format_internal::InvokeFlush(&bufsink, "Hello World");
str_format_internal::InvokeFlush(&bufsink, "237");
EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World237xx");
}
{
std::fill(std::begin(buf), std::end(buf), 'x');
str_format_internal::BufferRawSink bufsink(buf, sizeof(buf) - 1);
str_format_internal::InvokeFlush(&bufsink, "Hello World");
str_format_internal::InvokeFlush(&bufsink, "237237");
EXPECT_EQ(std::string(buf, sizeof(buf)), "Hello World2372x");
}
}
} // namespace
} // namespace absl
......@@ -34,15 +34,13 @@ namespace {
const char kInfiniteFutureStr[] = "infinite-future";
const char kInfinitePastStr[] = "infinite-past";
using cctz_sec = cctz::time_point<cctz::sys_seconds>;
using cctz_fem = cctz::detail::femtoseconds;
struct cctz_parts {
cctz_sec sec;
cctz_fem fem;
cctz::time_point<cctz::seconds> sec;
cctz::detail::femtoseconds fem;
};
inline cctz_sec unix_epoch() {
return std::chrono::time_point_cast<cctz::sys_seconds>(
inline cctz::time_point<cctz::seconds> unix_epoch() {
return std::chrono::time_point_cast<cctz::seconds>(
std::chrono::system_clock::from_time_t(0));
}
......@@ -53,8 +51,8 @@ cctz_parts Split(absl::Time t) {
const auto d = time_internal::ToUnixDuration(t);
const int64_t rep_hi = time_internal::GetRepHi(d);
const int64_t rep_lo = time_internal::GetRepLo(d);
const auto sec = unix_epoch() + cctz::sys_seconds(rep_hi);
const auto fem = cctz_fem(rep_lo * (1000 * 1000 / 4));
const auto sec = unix_epoch() + cctz::seconds(rep_hi);
const auto fem = cctz::detail::femtoseconds(rep_lo * (1000 * 1000 / 4));
return {sec, fem};
}
......
......@@ -34,23 +34,24 @@ namespace cctz {
// Convenience aliases. Not intended as public API points.
template <typename D>
using time_point = std::chrono::time_point<std::chrono::system_clock, D>;
using sys_seconds = std::chrono::duration<std::int_fast64_t>;
using seconds = std::chrono::duration<std::int_fast64_t>;
using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead.
namespace detail {
template <typename D>
inline std::pair<time_point<sys_seconds>, D>
inline std::pair<time_point<seconds>, D>
split_seconds(const time_point<D>& tp) {
auto sec = std::chrono::time_point_cast<sys_seconds>(tp);
auto sec = std::chrono::time_point_cast<seconds>(tp);
auto sub = tp - sec;
if (sub.count() < 0) {
sec -= sys_seconds(1);
sub += sys_seconds(1);
sec -= seconds(1);
sub += seconds(1);
}
return {sec, std::chrono::duration_cast<D>(sub)};
}
inline std::pair<time_point<sys_seconds>, sys_seconds>
split_seconds(const time_point<sys_seconds>& tp) {
return {tp, sys_seconds(0)};
inline std::pair<time_point<seconds>, seconds>
split_seconds(const time_point<seconds>& tp) {
return {tp, seconds::zero()};
}
} // namespace detail
......@@ -99,7 +100,7 @@ class time_zone {
bool is_dst; // is offset non-standard?
const char* abbr; // time-zone abbreviation (e.g., "PST")
};
absolute_lookup lookup(const time_point<sys_seconds>& tp) const;
absolute_lookup lookup(const time_point<seconds>& tp) const;
template <typename D>
absolute_lookup lookup(const time_point<D>& tp) const {
return lookup(detail::split_seconds(tp).first);
......@@ -120,7 +121,7 @@ class time_zone {
// offset, the transition point itself, and the post-transition offset,
// respectively (all three times are equal if kind == UNIQUE). If any
// of these three absolute times is outside the representable range of a
// time_point<sys_seconds> the field is set to its maximum/minimum value.
// time_point<seconds> the field is set to its maximum/minimum value.
//
// Example:
// cctz::time_zone lax;
......@@ -152,9 +153,9 @@ class time_zone {
SKIPPED, // the civil time did not exist (pre >= trans > post)
REPEATED, // the civil time was ambiguous (pre < trans <= post)
} kind;
time_point<sys_seconds> pre; // uses the pre-transition offset
time_point<sys_seconds> trans; // instant of civil-offset change
time_point<sys_seconds> post; // uses the post-transition offset
time_point<seconds> pre; // uses the pre-transition offset
time_point<seconds> trans; // instant of civil-offset change
time_point<seconds> post; // uses the post-transition offset
};
civil_lookup lookup(const civil_second& cs) const;
......@@ -180,7 +181,7 @@ time_zone utc_time_zone();
// Returns a time zone that is a fixed offset (seconds east) from UTC.
// Note: If the absolute value of the offset is greater than 24 hours
// you'll get UTC (i.e., zero offset) instead.
time_zone fixed_time_zone(const sys_seconds& offset);
time_zone fixed_time_zone(const seconds& offset);
// Returns a time zone representing the local time zone. Falls back to UTC.
time_zone local_time_zone();
......@@ -199,7 +200,7 @@ inline civil_second convert(const time_point<D>& tp, const time_zone& tz) {
// it was either repeated or non-existent), then the returned time_point is
// the best estimate that preserves relative order. That is, this function
// guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz).
inline time_point<sys_seconds> convert(const civil_second& cs,
inline time_point<seconds> convert(const civil_second& cs,
const time_zone& tz) {
const time_zone::civil_lookup cl = tz.lookup(cs);
if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans;
......@@ -208,10 +209,10 @@ inline time_point<sys_seconds> convert(const civil_second& cs,
namespace detail {
using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>;
std::string format(const std::string&, const time_point<sys_seconds>&,
std::string format(const std::string&, const time_point<seconds>&,
const femtoseconds&, const time_zone&);
bool parse(const std::string&, const std::string&, const time_zone&,
time_point<sys_seconds>*, femtoseconds*, std::string* err = nullptr);
time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
} // namespace detail
// Formats the given time_point in the given cctz::time_zone according to
......@@ -298,7 +299,7 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp,
template <typename D>
inline bool parse(const std::string& fmt, const std::string& input,
const time_zone& tz, time_point<D>* tpp) {
time_point<sys_seconds> sec;
time_point<seconds> sec;
detail::femtoseconds fs;
const bool b = detail::parse(fmt, input, tz, &sec, &fs);
if (b) {
......
......@@ -42,9 +42,9 @@ int Parse02d(const char* p) {
} // namespace
bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
bool FixedOffsetFromName(const std::string& name, seconds* offset) {
if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
*offset = sys_seconds::zero();
*offset = seconds::zero();
return true;
}
......@@ -69,12 +69,12 @@ bool FixedOffsetFromName(const std::string& name, sys_seconds* offset) {
secs += ((hours * 60) + mins) * 60;
if (secs > 24 * 60 * 60) return false; // outside supported offset range
*offset = sys_seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
*offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
return true;
}
std::string FixedOffsetToName(const sys_seconds& offset) {
if (offset == sys_seconds::zero()) return "UTC";
std::string FixedOffsetToName(const seconds& offset) {
if (offset == seconds::zero()) return "UTC";
if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
// We don't support fixed-offset zones more than 24 hours
// away from UTC to avoid complications in rendering such
......@@ -101,7 +101,7 @@ std::string FixedOffsetToName(const sys_seconds& offset) {
return buf;
}
std::string FixedOffsetToAbbr(const sys_seconds& offset) {
std::string FixedOffsetToAbbr(const seconds& offset) {
std::string abbr = FixedOffsetToName(offset);
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1;
if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
......
......@@ -38,9 +38,9 @@ namespace cctz {
// Note: FixedOffsetFromName() fails on syntax errors or when the parsed
// offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr()
// both produce "UTC" when the argument offset exceeds 24 hours.
bool FixedOffsetFromName(const std::string& name, sys_seconds* offset);
std::string FixedOffsetToName(const sys_seconds& offset);
std::string FixedOffsetToAbbr(const sys_seconds& offset);
bool FixedOffsetFromName(const std::string& name, seconds* offset);
std::string FixedOffsetToName(const seconds& offset);
std::string FixedOffsetToAbbr(const seconds& offset);
} // namespace cctz
} // namespace time_internal
......
......@@ -277,7 +277,7 @@ const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
// not support the tm_gmtoff and tm_zone extensions to std::tm.
//
// Requires that zero() <= fs < seconds(1).
std::string format(const std::string& format, const time_point<sys_seconds>& tp,
std::string format(const std::string& format, const time_point<seconds>& tp,
const detail::femtoseconds& fs, const time_zone& tz) {
std::string result;
result.reserve(format.size()); // A reasonable guess for the result size.
......@@ -555,7 +555,7 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
// We also handle the %z specifier to accommodate platforms that do not
// support the tm_gmtoff extension to std::tm. %Z is parsed but ignored.
bool parse(const std::string& format, const std::string& input,
const time_zone& tz, time_point<sys_seconds>* sec,
const time_zone& tz, time_point<seconds>* sec,
detail::femtoseconds* fs, std::string* err) {
// The unparsed input.
const char* data = input.c_str(); // NUL terminated
......@@ -822,15 +822,15 @@ bool parse(const std::string& format, const std::string& input,
const auto tp = ptz.lookup(cs).pre;
// Checks for overflow/underflow and returns an error as necessary.
if (tp == time_point<sys_seconds>::max()) {
const auto al = ptz.lookup(time_point<sys_seconds>::max());
if (tp == time_point<seconds>::max()) {
const auto al = ptz.lookup(time_point<seconds>::max());
if (cs > al.cs) {
if (err != nullptr) *err = "Out-of-range field";
return false;
}
}
if (tp == time_point<sys_seconds>::min()) {
const auto al = ptz.lookup(time_point<sys_seconds>::min());
if (tp == time_point<seconds>::min()) {
const auto al = ptz.lookup(time_point<seconds>::min());
if (cs < al.cs) {
if (err != nullptr) *err = "Out-of-range field";
return false;
......
......@@ -37,30 +37,28 @@ class TimeZoneIf {
virtual ~TimeZoneIf();
virtual time_zone::absolute_lookup BreakTime(
const time_point<sys_seconds>& tp) const = 0;
const time_point<seconds>& tp) const = 0;
virtual time_zone::civil_lookup MakeTime(
const civil_second& cs) const = 0;
virtual std::string Description() const = 0;
virtual bool NextTransition(time_point<sys_seconds>* tp) const = 0;
virtual bool PrevTransition(time_point<sys_seconds>* tp) const = 0;
virtual bool NextTransition(time_point<seconds>* tp) const = 0;
virtual bool PrevTransition(time_point<seconds>* tp) const = 0;
protected:
TimeZoneIf() {}
};
// Convert between time_point<sys_seconds> and a count of seconds since
// the Unix epoch. We assume that the std::chrono::system_clock and the
// Convert between time_point<seconds> and a count of seconds since the
// Unix epoch. We assume that the std::chrono::system_clock and the
// Unix clock are second aligned, but not that they share an epoch.
inline std::int_fast64_t ToUnixSeconds(const time_point<sys_seconds>& tp) {
return (tp - std::chrono::time_point_cast<sys_seconds>(
std::chrono::system_clock::from_time_t(0)))
.count();
inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
return (tp - std::chrono::time_point_cast<seconds>(
std::chrono::system_clock::from_time_t(0))).count();
}
inline time_point<sys_seconds> FromUnixSeconds(std::int_fast64_t t) {
return std::chrono::time_point_cast<sys_seconds>(
std::chrono::system_clock::from_time_t(0)) +
sys_seconds(t);
inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) {
return std::chrono::time_point_cast<seconds>(
std::chrono::system_clock::from_time_t(0)) + seconds(t);
}
} // namespace cctz
......
......@@ -45,8 +45,8 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
const time_zone::Impl* const utc_impl = UTCImpl();
// First check for UTC (which is never a key in time_zone_map).
auto offset = sys_seconds::zero();
if (FixedOffsetFromName(name, &offset) && offset == sys_seconds::zero()) {
auto offset = seconds::zero();
if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
*tz = time_zone(utc_impl);
return true;
}
......
......@@ -48,8 +48,7 @@ class time_zone::Impl {
const std::string& name() const { return name_; }
// Breaks a time_point down to civil-time components in this time zone.
time_zone::absolute_lookup BreakTime(
const time_point<sys_seconds>& tp) const {
time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const {
return zone_->BreakTime(tp);
}
......@@ -75,10 +74,10 @@ class time_zone::Impl {
// to NextTransition()/PrevTransition() will eventually return false,
// but it is unspecified exactly when NextTransition(&tp) jumps to false,
// or what time is set by PrevTransition(&tp) for a very distant tp.
bool NextTransition(time_point<sys_seconds>* tp) const {
bool NextTransition(time_point<seconds>* tp) const {
return zone_->NextTransition(tp);
}
bool PrevTransition(time_point<sys_seconds>* tp) const {
bool PrevTransition(time_point<seconds>* tp) const {
return zone_->PrevTransition(tp);
}
......
......@@ -140,7 +140,7 @@ std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday,
return (days * kSecsPerDay) + pt.time.offset;
}
inline time_zone::civil_lookup MakeUnique(const time_point<sys_seconds>& tp) {
inline time_zone::civil_lookup MakeUnique(const time_point<seconds>& tp) {
time_zone::civil_lookup cl;
cl.kind = time_zone::civil_lookup::UNIQUE;
cl.pre = cl.trans = cl.post = tp;
......@@ -179,7 +179,7 @@ inline civil_second YearShift(const civil_second& cs, year_t shift) {
} // namespace
// What (no leap-seconds) UTC+seconds zoneinfo would look like.
bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
transition_types_.resize(1);
TransitionType& tt(transition_types_.back());
tt.utc_offset = static_cast<std::int_least32_t>(offset.count());
......@@ -218,8 +218,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const sys_seconds& offset) {
future_spec_.clear(); // never needed for a fixed-offset zone
extended_ = false;
tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs;
tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs;
tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
transitions_.shrink_to_fit();
return true;
......@@ -565,10 +565,10 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
}
// Compute the maximum/minimum civil times that can be converted to a
// time_point<sys_seconds> for each of the zone's transition types.
// time_point<seconds> for each of the zone's transition types.
for (auto& tt : transition_types_) {
tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs;
tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs;
tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
}
transitions_.shrink_to_fit();
......@@ -713,7 +713,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
// zone never fails because the simple, fixed-offset state can be
// internally generated. Note that this depends on our choice to not
// accept leap-second encoded ("right") zoneinfo.
auto offset = sys_seconds::zero();
auto offset = seconds::zero();
if (FixedOffsetFromName(name, &offset)) {
return ResetToBuiltinUTC(offset);
}
......@@ -755,14 +755,14 @@ time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
year_t c4_shift) const {
assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_);
time_zone::civil_lookup cl = MakeTime(cs);
if (c4_shift > sys_seconds::max().count() / kSecsPer400Years) {
cl.pre = cl.trans = cl.post = time_point<sys_seconds>::max();
if (c4_shift > seconds::max().count() / kSecsPer400Years) {
cl.pre = cl.trans = cl.post = time_point<seconds>::max();
} else {
const auto offset = sys_seconds(c4_shift * kSecsPer400Years);
const auto limit = time_point<sys_seconds>::max() - offset;
const auto offset = seconds(c4_shift * kSecsPer400Years);
const auto limit = time_point<seconds>::max() - offset;
for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) {
if (*tp > limit) {
*tp = time_point<sys_seconds>::max();
*tp = time_point<seconds>::max();
} else {
*tp += offset;
}
......@@ -772,7 +772,7 @@ time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
}
time_zone::absolute_lookup TimeZoneInfo::BreakTime(
const time_point<sys_seconds>& tp) const {
const time_point<seconds>& tp) const {
std::int_fast64_t unix_time = ToUnixSeconds(tp);
const std::size_t timecnt = transitions_.size();
assert(timecnt != 0); // We always add a transition.
......@@ -788,7 +788,7 @@ time_zone::absolute_lookup TimeZoneInfo::BreakTime(
const std::int_fast64_t diff =
unix_time - transitions_[timecnt - 1].unix_time;
const year_t shift = diff / kSecsPer400Years + 1;
const auto d = sys_seconds(shift * kSecsPer400Years);
const auto d = seconds(shift * kSecsPer400Years);
time_zone::absolute_lookup al = BreakTime(tp - d);
al.cs = YearShift(al.cs, shift * 400);
return al;
......@@ -847,7 +847,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
if (tr->prev_civil_sec >= cs) {
// Before first transition, so use the default offset.
const TransitionType& tt(transition_types_[default_transition_type_]);
if (cs < tt.civil_min) return MakeUnique(time_point<sys_seconds>::min());
if (cs < tt.civil_min) return MakeUnique(time_point<seconds>::min());
return MakeUnique(cs - (civil_second() + tt.utc_offset));
}
// tr->prev_civil_sec < cs < tr->civil_sec
......@@ -864,7 +864,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
return TimeLocal(YearShift(cs, shift * -400), shift);
}
const TransitionType& tt(transition_types_[tr->type_index]);
if (cs > tt.civil_max) return MakeUnique(time_point<sys_seconds>::max());
if (cs > tt.civil_max) return MakeUnique(time_point<seconds>::max());
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
}
// tr->civil_sec <= cs <= tr->prev_civil_sec
......@@ -895,7 +895,7 @@ std::string TimeZoneInfo::Description() const {
return oss.str();
}
bool TimeZoneInfo::NextTransition(time_point<sys_seconds>* tp) const {
bool TimeZoneInfo::NextTransition(time_point<seconds>* tp) const {
if (transitions_.empty()) return false;
const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size();
......@@ -919,7 +919,7 @@ bool TimeZoneInfo::NextTransition(time_point<sys_seconds>* tp) const {
return true;
}
bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const {
bool TimeZoneInfo::PrevTransition(time_point<seconds>* tp) const {
if (transitions_.empty()) return false;
const Transition* begin = &transitions_[0];
const Transition* end = begin + transitions_.size();
......
......@@ -71,12 +71,12 @@ class TimeZoneInfo : public TimeZoneIf {
// TimeZoneIf implementations.
time_zone::absolute_lookup BreakTime(
const time_point<sys_seconds>& tp) const override;
const time_point<seconds>& tp) const override;
time_zone::civil_lookup MakeTime(
const civil_second& cs) const override;
std::string Description() const override;
bool NextTransition(time_point<sys_seconds>* tp) const override;
bool PrevTransition(time_point<sys_seconds>* tp) const override;
bool NextTransition(time_point<seconds>* tp) const override;
bool PrevTransition(time_point<seconds>* tp) const override;
private:
struct Header { // counts of:
......@@ -98,7 +98,7 @@ class TimeZoneInfo : public TimeZoneIf {
std::uint_fast8_t tt2_index) const;
void ExtendTransitions(const std::string& name, const Header& hdr);
bool ResetToBuiltinUTC(const sys_seconds& offset);
bool ResetToBuiltinUTC(const seconds& offset);
bool Load(const std::string& name, ZoneInfoSource* zip);
// Helpers for BreakTime() and MakeTime().
......
......@@ -91,7 +91,7 @@ TimeZoneLibC::TimeZoneLibC(const std::string& name)
: local_(name == "localtime") {}
time_zone::absolute_lookup TimeZoneLibC::BreakTime(
const time_point<sys_seconds>& tp) const {
const time_point<seconds>& tp) const {
time_zone::absolute_lookup al;
std::time_t t = ToUnixSeconds(tp);
std::tm tm;
......@@ -143,11 +143,11 @@ std::string TimeZoneLibC::Description() const {
return local_ ? "localtime" : "UTC";
}
bool TimeZoneLibC::NextTransition(time_point<sys_seconds>* tp) const {
bool TimeZoneLibC::NextTransition(time_point<seconds>* tp) const {
return false;
}
bool TimeZoneLibC::PrevTransition(time_point<sys_seconds>* tp) const {
bool TimeZoneLibC::PrevTransition(time_point<seconds>* tp) const {
return false;
}
......
......@@ -32,12 +32,12 @@ class TimeZoneLibC : public TimeZoneIf {
// TimeZoneIf implementations.
time_zone::absolute_lookup BreakTime(
const time_point<sys_seconds>& tp) const override;
const time_point<seconds>& tp) const override;
time_zone::civil_lookup MakeTime(
const civil_second& cs) const override;
std::string Description() const override;
bool NextTransition(time_point<sys_seconds>* tp) const override;
bool PrevTransition(time_point<sys_seconds>* tp) const override;
bool NextTransition(time_point<seconds>* tp) const override;
bool PrevTransition(time_point<seconds>* tp) const override;
private:
const bool local_; // localtime or UTC
......
......@@ -65,7 +65,7 @@ std::string time_zone::name() const {
}
time_zone::absolute_lookup time_zone::lookup(
const time_point<sys_seconds>& tp) const {
const time_point<seconds>& tp) const {
return time_zone::Impl::get(*this).BreakTime(tp);
}
......@@ -85,7 +85,7 @@ time_zone utc_time_zone() {
return time_zone::Impl::UTC(); // avoid name lookup
}
time_zone fixed_time_zone(const sys_seconds& offset) {
time_zone fixed_time_zone(const seconds& offset) {
time_zone tz;
load_time_zone(FixedOffsetToName(offset), &tz);
return tz;
......
......@@ -60,9 +60,17 @@ ZoneInfoSourceFactory default_factory = DefaultFactory;
#else
#error Unsupported MSVC platform
#endif
#else
#else // _MSC_VER
#if !defined(__has_attribute)
#define __has_attribute(x) 0
#endif
#if __has_attribute(weak) || defined(__GNUC__)
ZoneInfoSourceFactory zone_info_source_factory
__attribute__((weak)) = DefaultFactory;
#else
// Make it a "strong" definition if we have no other choice.
ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory;
#endif
#endif // _MSC_VER
} // namespace cctz_extension
......
......@@ -44,8 +44,8 @@ namespace absl {
namespace {
inline cctz::time_point<cctz::sys_seconds> unix_epoch() {
return std::chrono::time_point_cast<cctz::sys_seconds>(
inline cctz::time_point<cctz::seconds> unix_epoch() {
return std::chrono::time_point_cast<cctz::seconds>(
std::chrono::system_clock::from_time_t(0));
}
......@@ -110,12 +110,12 @@ inline TimeConversion InfinitePastTimeConversion() {
// Makes a Time from sec, overflowing to InfiniteFuture/InfinitePast as
// necessary. If sec is min/max, then consult cs+tz to check for overlow.
Time MakeTimeWithOverflow(const cctz::time_point<cctz::sys_seconds>& sec,
Time MakeTimeWithOverflow(const cctz::time_point<cctz::seconds>& sec,
const cctz::civil_second& cs,
const cctz::time_zone& tz,
bool* normalized = nullptr) {
const auto max = cctz::time_point<cctz::sys_seconds>::max();
const auto min = cctz::time_point<cctz::sys_seconds>::min();
const auto max = cctz::time_point<cctz::seconds>::max();
const auto min = cctz::time_point<cctz::seconds>::min();
if (sec == max) {
const auto al = tz.lookup(max);
if (cs > al.cs) {
......@@ -174,8 +174,7 @@ absl::Time::Breakdown Time::In(absl::TimeZone tz) const {
if (*this == absl::InfiniteFuture()) return absl::InfiniteFutureBreakdown();
if (*this == absl::InfinitePast()) return absl::InfinitePastBreakdown();
const auto tp =
unix_epoch() + cctz::sys_seconds(time_internal::GetRepHi(rep_));
const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(rep_));
const auto al = cctz::time_zone(tz).lookup(tp);
const auto cs = al.cs;
const auto cd = cctz::civil_day(cs);
......
......@@ -59,7 +59,7 @@ TEST(TimeZone, DefaultTimeZones) {
TEST(TimeZone, FixedTimeZone) {
const absl::TimeZone tz = absl::FixedTimeZone(123);
const cctz::time_zone cz = cctz::fixed_time_zone(cctz::sys_seconds(123));
const cctz::time_zone cz = cctz::fixed_time_zone(cctz::seconds(123));
EXPECT_EQ(tz, absl::TimeZone(cz));
}
......
......@@ -579,12 +579,9 @@ struct VariantCoreAccess {
self.index_ = other.index();
}
// Access a variant alternative, assuming the index is correct.
template <std::size_t I, class Variant>
static VariantAccessResult<I, Variant> Access(Variant&& self) {
if (ABSL_PREDICT_FALSE(self.index_ != I)) {
TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
}
// This cast instead of invocation of AccessUnion with an rvalue is a
// workaround for msvc. Without this there is a runtime failure when dealing
// with rvalues.
......@@ -593,6 +590,16 @@ struct VariantCoreAccess {
variant_internal::AccessUnion(self.state_, SizeT<I>()));
}
// Access a variant alternative, throwing if the index is incorrect.
template <std::size_t I, class Variant>
static VariantAccessResult<I, Variant> CheckedAccess(Variant&& self) {
if (ABSL_PREDICT_FALSE(self.index_ != I)) {
TypedThrowBadVariantAccess<VariantAccessResult<I, Variant>>();
}
return Access<I>(absl::forward<Variant>(self));
}
// The implementation of the move-assignment operation for a variant.
template <class VType>
struct MoveAssignVisitor {
......
......@@ -290,7 +290,7 @@ constexpr bool holds_alternative(const variant<Types...>& v) noexcept {
// Overload for getting a variant's lvalue by type.
template <class T, class... Types>
constexpr T& get(variant<Types...>& v) { // NOLINT
return variant_internal::VariantCoreAccess::Access<
return variant_internal::VariantCoreAccess::CheckedAccess<
variant_internal::IndexOf<T, Types...>::value>(v);
}
......@@ -298,14 +298,14 @@ constexpr T& get(variant<Types...>& v) { // NOLINT
// Note: `absl::move()` is required to allow use of constexpr in C++11.
template <class T, class... Types>
constexpr T&& get(variant<Types...>&& v) {
return variant_internal::VariantCoreAccess::Access<
return variant_internal::VariantCoreAccess::CheckedAccess<
variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
}
// Overload for getting a variant's const lvalue by type.
template <class T, class... Types>
constexpr const T& get(const variant<Types...>& v) {
return variant_internal::VariantCoreAccess::Access<
return variant_internal::VariantCoreAccess::CheckedAccess<
variant_internal::IndexOf<T, Types...>::value>(v);
}
......@@ -313,7 +313,7 @@ constexpr const T& get(const variant<Types...>& v) {
// Note: `absl::move()` is required to allow use of constexpr in C++11.
template <class T, class... Types>
constexpr const T&& get(const variant<Types...>&& v) {
return variant_internal::VariantCoreAccess::Access<
return variant_internal::VariantCoreAccess::CheckedAccess<
variant_internal::IndexOf<T, Types...>::value>(absl::move(v));
}
......@@ -321,7 +321,7 @@ constexpr const T&& get(const variant<Types...>&& v) {
template <std::size_t I, class... Types>
constexpr variant_alternative_t<I, variant<Types...>>& get(
variant<Types...>& v) { // NOLINT
return variant_internal::VariantCoreAccess::Access<I>(v);
return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
}
// Overload for getting a variant's rvalue by index.
......@@ -329,14 +329,14 @@ constexpr variant_alternative_t<I, variant<Types...>>& get(
template <std::size_t I, class... Types>
constexpr variant_alternative_t<I, variant<Types...>>&& get(
variant<Types...>&& v) {
return variant_internal::VariantCoreAccess::Access<I>(absl::move(v));
return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
}
// Overload for getting a variant's const lvalue by index.
template <std::size_t I, class... Types>
constexpr const variant_alternative_t<I, variant<Types...>>& get(
const variant<Types...>& v) {
return variant_internal::VariantCoreAccess::Access<I>(v);
return variant_internal::VariantCoreAccess::CheckedAccess<I>(v);
}
// Overload for getting a variant's const rvalue by index.
......@@ -344,7 +344,7 @@ constexpr const variant_alternative_t<I, variant<Types...>>& get(
template <std::size_t I, class... Types>
constexpr const variant_alternative_t<I, variant<Types...>>&& get(
const variant<Types...>&& v) {
return variant_internal::VariantCoreAccess::Access<I>(absl::move(v));
return variant_internal::VariantCoreAccess::CheckedAccess<I>(absl::move(v));
}
// get_if()
......@@ -362,7 +362,9 @@ constexpr const variant_alternative_t<I, variant<Types...>>&& get(
template <std::size_t I, class... Types>
constexpr absl::add_pointer_t<variant_alternative_t<I, variant<Types...>>>
get_if(variant<Types...>* v) noexcept {
return (v != nullptr && v->index() == I) ? std::addressof(absl::get<I>(*v))
return (v != nullptr && v->index() == I)
? std::addressof(
variant_internal::VariantCoreAccess::Access<I>(*v))
: nullptr;
}
......@@ -371,7 +373,9 @@ get_if(variant<Types...>* v) noexcept {
template <std::size_t I, class... Types>
constexpr absl::add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
get_if(const variant<Types...>* v) noexcept {
return (v != nullptr && v->index() == I) ? std::addressof(absl::get<I>(*v))
return (v != nullptr && v->index() == I)
? std::addressof(
variant_internal::VariantCoreAccess::Access<I>(*v))
: nullptr;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment