blob: b90bd9ed951962b453eb5b6cb25dd17e585b47cd [file] [log] [blame]
// Copyright 2022 The Pigweed 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.
#pragma once
#include <string> // for std::char_traits
#include <type_traits>
#if PW_CXX_STANDARD_IS_SUPPORTED(17) // std::string_view is a C++17 feature
#include <string_view>
#endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
#include "pw_assert/assert.h"
#include "pw_polyfill/language_feature_macros.h"
namespace pw {
namespace string_impl {
// pw::InlineString<>::size_type is unsigned short so the capacity and current
// size fit into a single word.
using size_type = unsigned short;
template <typename CharType, typename T>
using EnableIfNonArrayCharPointer = std::enable_if_t<
std::is_pointer<T>::value && !std::is_array<T>::value &&
std::is_same<CharType, std::remove_cv_t<std::remove_pointer_t<T>>>::value>;
template <typename T>
using EnableIfInputIterator = std::enable_if_t<
std::is_convertible<typename std::iterator_traits<T>::iterator_category,
std::input_iterator_tag>::value>;
#if PW_CXX_STANDARD_IS_SUPPORTED(17) // std::string_view is a C++17 feature
template <typename CharType, typename T>
using EnableIfStringViewLike = std::enable_if_t<
std::is_convertible<const T&, std::basic_string_view<CharType>>() &&
!std::is_convertible<const T&, const CharType*>()>;
template <typename CharType, typename T>
using EnableIfStringViewLikeButNotStringView = std::enable_if_t<
!std::is_same<T, std::basic_string_view<CharType>>() &&
std::is_convertible<const T&, std::basic_string_view<CharType>>() &&
!std::is_convertible<const T&, const CharType*>()>;
#endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
// Reserved capacity that is used to represent a generic-length
// pw::InlineString.
PW_INLINE_VARIABLE constexpr size_type kGeneric = size_type(-1);
#if defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
// Like std::string and std::string_view, pw::InlineString uses std::char_traits
// for low-level operations.
using std::char_traits;
#else
// If constexpr std::char_traits is not available, provide a custom traits class
// with constexpr versions of the necessary functions. Defer to std::char_traits
// when possible.
template <typename T>
class char_traits : private std::char_traits<T> {
public:
static constexpr void assign(T& dest, const T& source) noexcept {
dest = source;
}
static constexpr T* assign(T* dest, size_t count, T value) {
for (size_t i = 0; i < count; ++i) {
dest[i] = value;
}
return dest;
}
using std::char_traits<T>::eq;
static constexpr T* copy(T* dest, const T* source, size_t count) {
for (size_type i = 0; i < count; ++i) {
char_traits<T>::assign(dest[i], source[i]);
}
return dest;
}
using std::char_traits<T>::compare;
};
#endif // __cpp_lib_constexpr_string
// Constexpr utility functions for pw::InlineString. These are NOT intended for
// general use. These mostly map directly to general purpose standard library
// utilities that are not constexpr until C++20.
// Calculates the length of a C string up to the capacity. Returns capacity +
// 1 if the string is longer than the capacity. Use this instead of
// std::char_traits<T>::length, which is unbounded.
template <typename T>
constexpr size_type BoundedStringLength(const T* string, size_type capacity) {
size_type index = 0;
for (; !char_traits<T>::eq(string[index], T()); ++index) {
if (index > capacity) {
break; // Return if size is too large. This may trigger an assert later.
}
}
return index;
}
// InlineString literals and character arrays are the same type, but string
// literals are always null terminated. This returns the size of a character
// array, ignoring the final character if it is a null terminator.
template <typename T, size_t kCharArraySize>
constexpr size_type ArrayStringLength(const T (&array)[kCharArraySize]) {
static_assert(kCharArraySize < kGeneric,
"The size of this literal or character array is too large "
"for pw::InlineString<>::size_type");
return kCharArraySize -
(char_traits<T>::eq(array[kCharArraySize - 1], T()) ? 1 : 0);
}
// Constexpr version of std::copy that returns the number of copied characters.
template <typename InputIterator, typename T>
constexpr size_type IteratorCopyAndTerminate(InputIterator begin,
InputIterator end,
T* const string_begin,
const T* const string_end) {
T* current_position = string_begin;
for (InputIterator it = begin; it != end; ++it) {
PW_ASSERT(current_position != string_end);
char_traits<T>::assign(*current_position++, *it);
}
char_traits<T>::assign(*current_position, T()); // Null terminate
return static_cast<size_type>(current_position - string_begin);
}
// Constexpr lexicographical comparison.
template <typename T>
constexpr int Compare(const T* lhs,
size_type lhs_size,
const T* rhs,
size_type rhs_size) noexcept {
int result = char_traits<T>::compare(lhs, rhs, std::min(lhs_size, rhs_size));
if (result != 0 || lhs_size == rhs_size) {
return result;
}
return lhs_size < rhs_size ? -1 : 1;
}
} // namespace string_impl
} // namespace pw