blob: b8019cc8722bf3b87b22c5a94d97da44411ecc7a [file]
// Copyright 2022 Google LLC
//
// 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 RIEGELI_BYTES_STRINGIFY_H_
#define RIEGELI_BYTES_STRINGIFY_H_
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/numeric/int128.h"
#include "absl/strings/cord.h"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/string_view.h"
#include "riegeli/base/byte_fill.h"
#include "riegeli/base/bytes_ref.h"
#include "riegeli/base/chain.h"
#include "riegeli/base/types.h"
#include "riegeli/bytes/write_int_internal.h"
namespace riegeli {
namespace stringify_internal {
template <typename Src, typename Enable = void>
struct HasRiegeliStringifiedSize : std::false_type {};
template <typename Src>
struct HasRiegeliStringifiedSize<
Src, std::enable_if_t<std::is_convertible_v<
decltype(RiegeliStringifiedSize(std::declval<const Src&>())),
Position>>> : std::true_type {};
} // namespace stringify_internal
// `riegeli::StringifiedSize()` of a stringifiable value returns the size of its
// stringification as `Position` if easily known, otherwise it is only declared
// as returning `void`.
//
// It has the same overloads as `Writer::Write()`, assuming that the parameter
// is passed by const reference.
//
// To customize `riegeli::StringifiedSize()` for a class `Src` supporting
// `AbslStringify()`, define a free function
// `friend Position RiegeliStringifiedSize(const Src& src)` as a friend of `Src`
// inside class definition or in the same namespace as `Src`, so that it can be
// found via ADL. A function returning `void` is treated as absent.
//
// There is no need to define `RiegeliStringifiedSize()` for types convertible
// to `BytesRef`, even if they support `AbslStringify()`.
inline Position StringifiedSize(char /*src*/) { return 1; }
#if __cpp_char8_t
inline Position StringifiedSize(char8_t /*src*/) { return 1; }
#endif
inline Position StringifiedSize(BytesRef src) { return src.size(); }
ABSL_ATTRIBUTE_ALWAYS_INLINE inline Position StringifiedSize(const char* src) {
return absl::string_view(src).size();
}
inline Position StringifiedSize(const Chain& src) { return src.size(); }
inline Position StringifiedSize(const absl::Cord& src) { return src.size(); }
inline Position StringifiedSize(ByteFill src) { return src.size(); }
inline Position StringifiedSize(signed char src) {
return write_int_internal::StringifiedSizeSigned(src);
}
inline Position StringifiedSize(unsigned char src) {
return write_int_internal::StringifiedSizeUnsigned(src);
}
inline Position StringifiedSize(short src) {
return write_int_internal::StringifiedSizeSigned(src);
}
inline Position StringifiedSize(unsigned short src) {
return write_int_internal::StringifiedSizeUnsigned(src);
}
inline Position StringifiedSize(int src) {
return write_int_internal::StringifiedSizeSigned(src);
}
inline Position StringifiedSize(unsigned src) {
return write_int_internal::StringifiedSizeUnsigned(src);
}
inline Position StringifiedSize(long src) {
return write_int_internal::StringifiedSizeSigned(src);
}
inline Position StringifiedSize(unsigned long src) {
return write_int_internal::StringifiedSizeUnsigned(src);
}
inline Position StringifiedSize(long long src) {
return write_int_internal::StringifiedSizeSigned(src);
}
inline Position StringifiedSize(unsigned long long src) {
return write_int_internal::StringifiedSizeUnsigned(src);
}
inline Position StringifiedSize(absl::int128 src) {
return write_int_internal::StringifiedSizeSigned(src);
}
inline Position StringifiedSize(absl::uint128 src) {
return write_int_internal::StringifiedSizeUnsigned(src);
}
void StringifiedSize(float);
void StringifiedSize(double);
void StringifiedSize(long double);
template <
typename Src,
std::enable_if_t<
std::conjunction_v<
absl::HasAbslStringify<Src>,
std::negation<std::is_convertible<const Src&, BytesRef>>,
std::negation<std::is_convertible<const Src&, const Chain&>>,
std::negation<std::is_convertible<const Src&, const absl::Cord&>>,
std::negation<std::is_convertible<const Src&, ByteFill>>>,
int> = 0>
inline auto StringifiedSize(const Src& src) {
if constexpr (stringify_internal::HasRiegeliStringifiedSize<Src>::value) {
return RiegeliStringifiedSize(src);
}
}
void StringifiedSize(bool) = delete;
void StringifiedSize(wchar_t) = delete;
void StringifiedSize(char16_t) = delete;
void StringifiedSize(char32_t) = delete;
namespace stringify_internal {
template <typename T, typename Enable = void>
struct IsStringifiableImpl : std::false_type {};
template <typename T>
struct IsStringifiableImpl<T, std::void_t<decltype(riegeli::StringifiedSize(
std::declval<const T&>()))>>
: std::true_type {};
template <typename T, typename Enable = void>
struct HasStringifiedSizeImpl : std::false_type {};
template <typename T>
struct HasStringifiedSizeImpl<
T, std::enable_if_t<std::is_convertible_v<decltype(riegeli::StringifiedSize(
std::declval<const T&>())),
Position>>> : std::true_type {};
} // namespace stringify_internal
// `IsStringifiable` checks if the type is an appropriate argument for
// `Writer::Write()`, `BackwardWriter::Write()`, `riegeli::Write()`, and e.g.
// `riegeli::WriteLine()`.
template <typename... T>
struct IsStringifiable
: std::conjunction<stringify_internal::IsStringifiableImpl<T>...> {};
// `HasStringifiedSize` checks if the type has `riegeli::StringifiedSize()`
// defined returning the size.
template <typename... T>
struct HasStringifiedSize
: std::conjunction<stringify_internal::HasStringifiedSizeImpl<T>...> {};
// `riegeli::StringifiedSize()` of multiple stringifiable values returns the
// total size of their stringifications, interpreted as for
// `riegeli::StringifiedSize()` with a single parameter.
template <typename... Srcs
#if !__cpp_concepts
,
std::enable_if_t<
std::conjunction_v<std::bool_constant<sizeof...(Srcs) != 1>,
IsStringifiable<Srcs...>>,
int> = 0
#endif
>
inline auto StringifiedSize(const Srcs&... srcs)
#if __cpp_concepts
// For conjunctions, `requires` gives better error messages than
// `std::enable_if_t`, indicating the relevant argument.
requires(sizeof...(Srcs) != 1) && (IsStringifiable<Srcs>::value && ...)
#endif
{
if constexpr (HasStringifiedSize<Srcs...>::value) {
return (Position{0} + ... + riegeli::StringifiedSize(srcs));
}
}
} // namespace riegeli
#endif // RIEGELI_BYTES_STRINGIFY_H_