boost/beast/core/static_string.hpp
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_STATIC_STRING_HPP
#define BOOST_BEAST_STATIC_STRING_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/detail/static_string.hpp>
#include <algorithm>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>
namespace boost {
namespace beast {
/** A modifiable string with a fixed-size storage area.
These objects behave like `std::string` except that the storage
is not dynamically allocated but rather fixed in size.
These strings offer performance advantages when a protocol
imposes a natural small upper limit on the size of a value.
@note The stored string is always null-terminated.
@see @ref to_static_string
*/
template<
std::size_t N,
class CharT = char,
class Traits = std::char_traits<CharT>>
class static_string
{
template<std::size_t, class, class>
friend class static_string;
void
term()
{
Traits::assign(s_[n_], 0);
}
std::size_t n_;
CharT s_[N+1];
public:
//
// Member types
//
using traits_type = Traits;
using value_type = typename Traits::char_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using const_pointer = value_type const*;
using const_reference = value_type const&;
using iterator = value_type*;
using const_iterator = value_type const*;
using reverse_iterator =
std::reverse_iterator<iterator>;
using const_reverse_iterator =
std::reverse_iterator<const_iterator>;
/// The type of `string_view` returned by the interface
using string_view_type =
basic_string_view<CharT, Traits>;
//
// Constants
//
/// Maximum size of the string excluding the null terminator
static std::size_t constexpr max_size_n = N;
/// A special index
static constexpr size_type npos = size_type(-1);
//
// (constructor)
//
/// Default constructor (empty string).
static_string();
/** Construct with count copies of character `ch`.
The behavior is undefined if `count >= npos`
*/
static_string(size_type count, CharT ch);
/// Construct with a substring (pos, other.size()) of `other`.
template<std::size_t M>
static_string(static_string<M, CharT, Traits> const& other,
size_type pos);
/// Construct with a substring (pos, count) of `other`.
template<std::size_t M>
static_string(static_string<M, CharT, Traits> const& other,
size_type pos, size_type count);
/// Construct with the first `count` characters of `s`, including nulls.
static_string(CharT const* s, size_type count);
/// Construct from a null terminated string.
static_string(CharT const* s);
/// Construct from a range of characters
template<class InputIt>
static_string(InputIt first, InputIt last);
/// Copy constructor.
static_string(static_string const& other);
/// Copy constructor.
template<std::size_t M>
static_string(static_string<M, CharT, Traits> const& other);
/// Construct from an initializer list
static_string(std::initializer_list<CharT> init);
/// Construct from a `string_view`
explicit
static_string(string_view_type sv);
/** Construct from any object convertible to `string_view_type`.
The range (pos, n) is extracted from the value
obtained by converting `t` to `string_view_type`,
and used to construct the string.
*/
#if BOOST_BEAST_DOXYGEN
template<class T>
#else
template<class T, class = typename std::enable_if<
std::is_convertible<T, string_view_type>::value>::type>
#endif
static_string(T const& t, size_type pos, size_type n);
//
// (assignment)
//
/// Copy assignment.
static_string&
operator=(static_string const& str)
{
return assign(str);
}
/// Copy assignment.
template<std::size_t M>
static_string&
operator=(static_string<M, CharT, Traits> const& str)
{
return assign(str);
}
/// Assign from null-terminated string.
static_string&
operator=(CharT const* s)
{
return assign(s);
}
/// Assign from single character.
static_string&
operator=(CharT ch)
{
return assign_char(ch,
std::integral_constant<bool, (N>0)>{});
}
/// Assign from initializer list.
static_string&
operator=(std::initializer_list<CharT> init)
{
return assign(init);
}
/// Assign from `string_view_type`.
static_string&
operator=(string_view_type sv)
{
return assign(sv);
}
/// Assign `count` copies of `ch`.
static_string&
assign(size_type count, CharT ch);
/// Assign from another `static_string`
static_string&
assign(static_string const& str);
// VFALCO NOTE this could come in two flavors,
// N>M and N<M, and skip the exception
// check when N>M
/// Assign from another `static_string`
template<std::size_t M>
static_string&
assign(static_string<M, CharT, Traits> const& str)
{
return assign(str.data(), str.size());
}
/// Assign `count` characterss starting at `npos` from `other`.
template<std::size_t M>
static_string&
assign(static_string<M, CharT, Traits> const& str,
size_type pos, size_type count = npos);
/// Assign the first `count` characters of `s`, including nulls.
static_string&
assign(CharT const* s, size_type count);
/// Assign a null terminated string.
static_string&
assign(CharT const* s)
{
return assign(s, Traits::length(s));
}
/// Assign from an iterator range of characters.
template<class InputIt>
static_string&
assign(InputIt first, InputIt last);
/// Assign from initializer list.
static_string&
assign(std::initializer_list<CharT> init)
{
return assign(init.begin(), init.end());
}
/// Assign from `string_view_type`.
static_string&
assign(string_view_type str)
{
return assign(str.data(), str.size());
}
/** Assign from any object convertible to `string_view_type`.
The range (pos, n) is extracted from the value
obtained by converting `t` to `string_view_type`,
and used to assign the string.
*/
template<class T>
#if BOOST_BEAST_DOXYGEN
static_string&
#else
typename std::enable_if<std::is_convertible<T,
string_view_type>::value, static_string&>::type
#endif
assign(T const& t,
size_type pos, size_type count = npos);
//
// Element access
//
/// Access specified character with bounds checking.
reference
at(size_type pos);
/// Access specified character with bounds checking.
const_reference
at(size_type pos) const;
/// Access specified character.
reference
operator[](size_type pos)
{
return s_[pos];
}
/// Access specified character.
const_reference
operator[](size_type pos) const
{
return s_[pos];
}
/// Accesses the first character.
CharT&
front()
{
return s_[0];
}
/// Accesses the first character.
CharT const&
front() const
{
return s_[0];
}
/// Accesses the last character.
CharT&
back()
{
return s_[n_-1];
}
/// Accesses the last character.
CharT const&
back() const
{
return s_[n_-1];
}
/// Returns a pointer to the first character of a string.
CharT*
data()
{
return &s_[0];
}
/// Returns a pointer to the first character of a string.
CharT const*
data() const
{
return &s_[0];
}
/// Returns a non-modifiable standard C character array version of the string.
CharT const*
c_str() const
{
return data();
}
/// Convert a static string to a `string_view_type`
operator string_view_type() const
{
return basic_string_view<
CharT, Traits>{data(), size()};
}
//
// Iterators
//
/// Returns an iterator to the beginning.
iterator
begin()
{
return &s_[0];
}
/// Returns an iterator to the beginning.
const_iterator
begin() const
{
return &s_[0];
}
/// Returns an iterator to the beginning.
const_iterator
cbegin() const
{
return &s_[0];
}
/// Returns an iterator to the end.
iterator
end()
{
return &s_[n_];
}
/// Returns an iterator to the end.
const_iterator
end() const
{
return &s_[n_];
}
/// Returns an iterator to the end.
const_iterator
cend() const
{
return &s_[n_];
}
/// Returns a reverse iterator to the beginning.
reverse_iterator
rbegin()
{
return reverse_iterator{end()};
}
/// Returns a reverse iterator to the beginning.
const_reverse_iterator
rbegin() const
{
return const_reverse_iterator{cend()};
}
/// Returns a reverse iterator to the beginning.
const_reverse_iterator
crbegin() const
{
return const_reverse_iterator{cend()};
}
/// Returns a reverse iterator to the end.
reverse_iterator
rend()
{
return reverse_iterator{begin()};
}
/// Returns a reverse iterator to the end.
const_reverse_iterator
rend() const
{
return const_reverse_iterator{cbegin()};
}
/// Returns a reverse iterator to the end.
const_reverse_iterator
crend() const
{
return const_reverse_iterator{cbegin()};
}
//
// Capacity
//
/// Returns `true` if the string is empty.
bool
empty() const
{
return n_ == 0;
}
/// Returns the number of characters, excluding the null terminator.
size_type
size() const
{
return n_;
}
/// Returns the number of characters, excluding the null terminator.
size_type
length() const
{
return size();
}
/// Returns the maximum number of characters that can be stored, excluding the null terminator.
size_type constexpr
max_size() const
{
return N;
}
/** Reserves storage.
This actually just throws an exception if `n > N`,
otherwise does nothing since the storage is fixed.
*/
void
reserve(std::size_t n);
/// Returns the number of characters that can be held in currently allocated storage.
size_type constexpr
capacity() const
{
return max_size();
}
/** Reduces memory usage by freeing unused memory.
This actually does nothing, since the storage is fixed.
*/
void
shrink_to_fit()
{
}
//
// Operations
//
/// Clears the contents.
void
clear();
static_string&
insert(size_type index, size_type count, CharT ch);
static_string&
insert(size_type index, CharT const* s)
{
return insert(index, s, Traits::length(s));
}
static_string&
insert(size_type index, CharT const* s, size_type count);
template<std::size_t M>
static_string&
insert(size_type index,
static_string<M, CharT, Traits> const& str)
{
return insert(index, str.data(), str.size());
}
template<std::size_t M>
static_string&
insert(size_type index,
static_string<M, CharT, Traits> const& str,
size_type index_str, size_type count = npos);
iterator
insert(const_iterator pos, CharT ch)
{
return insert(pos, 1, ch);
}
iterator
insert(const_iterator pos, size_type count, CharT ch);
template<class InputIt>
#if BOOST_BEAST_DOXYGEN
iterator
#else
typename std::enable_if<
detail::is_input_iterator<InputIt>::value,
iterator>::type
#endif
insert(const_iterator pos, InputIt first, InputIt last);
iterator
insert(const_iterator pos, std::initializer_list<CharT> init)
{
return insert(pos, init.begin(), init.end());
}
static_string&
insert(size_type index, string_view_type str)
{
return insert(index, str.data(), str.size());
}
template<class T>
#if BOOST_BEAST_DOXYGEN
static_string&
#else
typename std::enable_if<
std::is_convertible<T const&, string_view_type>::value &&
! std::is_convertible<T const&, CharT const*>::value,
static_string&>::type
#endif
insert(size_type index, T const& t,
size_type index_str, size_type count = npos);
static_string&
erase(size_type index = 0, size_type count = npos);
iterator
erase(const_iterator pos);
iterator
erase(const_iterator first, const_iterator last);
void
push_back(CharT ch);
void
pop_back()
{
Traits::assign(s_[--n_], 0);
}
static_string&
append(size_type count, CharT ch)
{
insert(end(), count, ch);
return *this;
}
template<std::size_t M>
static_string&
append(static_string<M, CharT, Traits> const& str)
{
insert(size(), str);
return *this;
}
template<std::size_t M>
static_string&
append(static_string<M, CharT, Traits> const& str,
size_type pos, size_type count = npos);
static_string&
append(CharT const* s, size_type count)
{
insert(size(), s, count);
return *this;
}
static_string&
append(CharT const* s)
{
insert(size(), s);
return *this;
}
template<class InputIt>
#if BOOST_BEAST_DOXYGEN
static_string&
#else
typename std::enable_if<
detail::is_input_iterator<InputIt>::value,
static_string&>::type
#endif
append(InputIt first, InputIt last)
{
insert(end(), first, last);
return *this;
}
static_string&
append(std::initializer_list<CharT> init)
{
insert(end(), init);
return *this;
}
static_string&
append(string_view_type sv)
{
insert(size(), sv);
return *this;
}
template<class T>
typename std::enable_if<
std::is_convertible<T const&, string_view_type>::value &&
! std::is_convertible<T const&, CharT const*>::value,
static_string&>::type
append(T const& t, size_type pos, size_type count = npos)
{
insert(size(), t, pos, count);
return *this;
}
template<std::size_t M>
static_string&
operator+=(static_string<M, CharT, Traits> const& str)
{
return append(str.data(), str.size());
}
static_string&
operator+=(CharT ch)
{
push_back(ch);
return *this;
}
static_string&
operator+=(CharT const* s)
{
return append(s);
}
static_string&
operator+=(std::initializer_list<CharT> init)
{
return append(init);
}
static_string&
operator+=(string_view_type const& str)
{
return append(str);
}
template<std::size_t M>
int
compare(static_string<M, CharT, Traits> const& str) const
{
return detail::lexicographical_compare<CharT, Traits>(
&s_[0], n_, &str.s_[0], str.n_);
}
template<std::size_t M>
int
compare(size_type pos1, size_type count1,
static_string<M, CharT, Traits> const& str) const
{
return detail::lexicographical_compare<CharT, Traits>(
substr(pos1, count1), str.data(), str.size());
}
template<std::size_t M>
int
compare(size_type pos1, size_type count1,
static_string<M, CharT, Traits> const& str,
size_type pos2, size_type count2 = npos) const
{
return detail::lexicographical_compare(
substr(pos1, count1), str.substr(pos2, count2));
}
int
compare(CharT const* s) const
{
return detail::lexicographical_compare<CharT, Traits>(
&s_[0], n_, s, Traits::length(s));
}
int
compare(size_type pos1, size_type count1,
CharT const* s) const
{
return detail::lexicographical_compare<CharT, Traits>(
substr(pos1, count1), s, Traits::length(s));
}
int
compare(size_type pos1, size_type count1,
CharT const*s, size_type count2) const
{
return detail::lexicographical_compare<CharT, Traits>(
substr(pos1, count1), s, count2);
}
int
compare(string_view_type str) const
{
return detail::lexicographical_compare<CharT, Traits>(
&s_[0], n_, str.data(), str.size());
}
int
compare(size_type pos1, size_type count1,
string_view_type str) const
{
return detail::lexicographical_compare<CharT, Traits>(
substr(pos1, count1), str);
}
template<class T>
#if BOOST_BEAST_DOXYGEN
int
#else
typename std::enable_if<
std::is_convertible<T const&, string_view_type>::value &&
! std::is_convertible<T const&, CharT const*>::value,
int>::type
#endif
compare(size_type pos1, size_type count1,
T const& t, size_type pos2,
size_type count2 = npos) const
{
return compare(pos1, count1,
string_view_type(t).substr(pos2, count2));
}
string_view_type
substr(size_type pos = 0, size_type count = npos) const;
/// Copy a substring (pos, pos+count) to character string pointed to by `dest`.
size_type
copy(CharT* dest, size_type count, size_type pos = 0) const;
/** Changes the number of characters stored.
If the resulting string is larger, the new
characters are uninitialized.
*/
void
resize(std::size_t n);
/** Changes the number of characters stored.
If the resulting string is larger, the new
characters are initialized to the value of `c`.
*/
void
resize(std::size_t n, CharT c);
/// Exchange the contents of this string with another.
void
swap(static_string& str);
/// Exchange the contents of this string with another.
template<std::size_t M>
void
swap(static_string<M, CharT, Traits>& str);
//
// Search
//
private:
static_string&
assign_char(CharT ch, std::true_type);
static_string&
assign_char(CharT ch, std::false_type);
};
//
// Disallowed operations
//
// These operations are explicitly deleted since
// there is no reasonable implementation possible.
template<std::size_t N, std::size_t M, class CharT, class Traits>
void
operator+(
static_string<N, CharT, Traits>const& lhs,
static_string<M, CharT, Traits>const& rhs) = delete;
template<std::size_t N, class CharT, class Traits>
void
operator+(CharT const* lhs,
static_string<N, CharT, Traits>const& rhs) = delete;
template<std::size_t N, class CharT, class Traits>
void
operator+(CharT lhs,
static_string<N, CharT, Traits> const& rhs) = delete;
template<std::size_t N, class CharT, class Traits>
void
operator+(static_string<N, CharT, Traits> const& lhs,
CharT const* rhs) = delete;
template<std::size_t N, class CharT, class Traits>
void
operator+(static_string<N, CharT, Traits> const& lhs,
CharT rhs) = delete;
//
// Non-member functions
//
template<std::size_t N, std::size_t M,
class CharT, class Traits>
bool
operator==(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) == 0;
}
template<std::size_t N, std::size_t M,
class CharT, class Traits>
bool
operator!=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) != 0;
}
template<std::size_t N, std::size_t M,
class CharT, class Traits>
bool
operator<(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) < 0;
}
template<std::size_t N, std::size_t M,
class CharT, class Traits>
bool
operator<=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) <= 0;
}
template<std::size_t N, std::size_t M,
class CharT, class Traits>
bool
operator>(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) > 0;
}
template<std::size_t N, std::size_t M,
class CharT, class Traits>
bool
operator>=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) >= 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator==(
CharT const* lhs,
static_string<N, CharT, Traits> const& rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs, Traits::length(lhs),
rhs.data(), rhs.size()) == 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator==(
static_string<N, CharT, Traits> const& lhs,
CharT const* rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs.data(), lhs.size(),
rhs, Traits::length(rhs)) == 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator!=(
CharT const* lhs,
static_string<N, CharT, Traits> const& rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs, Traits::length(lhs),
rhs.data(), rhs.size()) != 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator!=(
static_string<N, CharT, Traits> const& lhs,
CharT const* rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs.data(), lhs.size(),
rhs, Traits::length(rhs)) != 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator<(
CharT const* lhs,
static_string<N, CharT, Traits> const& rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs, Traits::length(lhs),
rhs.data(), rhs.size()) < 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator<(
static_string<N, CharT, Traits> const& lhs,
CharT const* rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs.data(), lhs.size(),
rhs, Traits::length(rhs)) < 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator<=(
CharT const* lhs,
static_string<N, CharT, Traits> const& rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs, Traits::length(lhs),
rhs.data(), rhs.size()) <= 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator<=(
static_string<N, CharT, Traits> const& lhs,
CharT const* rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs.data(), lhs.size(),
rhs, Traits::length(rhs)) <= 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator>(
CharT const* lhs,
static_string<N, CharT, Traits> const& rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs, Traits::length(lhs),
rhs.data(), rhs.size()) > 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator>(
static_string<N, CharT, Traits> const& lhs,
CharT const* rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs.data(), lhs.size(),
rhs, Traits::length(rhs)) > 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator>=(
CharT const* lhs,
static_string<N, CharT, Traits> const& rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs, Traits::length(lhs),
rhs.data(), rhs.size()) >= 0;
}
template<std::size_t N, class CharT, class Traits>
bool
operator>=(
static_string<N, CharT, Traits> const& lhs,
CharT const* rhs)
{
return detail::lexicographical_compare<CharT, Traits>(
lhs.data(), lhs.size(),
rhs, Traits::length(rhs)) >= 0;
}
//
// swap
//
template<std::size_t N, class CharT, class Traits>
void
swap(
static_string<N, CharT, Traits>& lhs,
static_string<N, CharT, Traits>& rhs)
{
lhs.swap(rhs);
}
template<std::size_t N, std::size_t M,
class CharT, class Traits>
void
swap(
static_string<N, CharT, Traits>& lhs,
static_string<M, CharT, Traits>& rhs)
{
lhs.swap(rhs);
}
//
// Input/Output
//
template<std::size_t N, class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
static_string<N, CharT, Traits> const& str)
{
return os << static_cast<
beast::basic_string_view<CharT, Traits>>(str);
}
//
// Numeric conversions
//
/** Returns a static string representing an integer as a decimal.
@param x The signed or unsigned integer to convert.
This must be an integral type.
@return A @ref static_string with an implementation defined
maximum size large enough to hold the longest possible decimal
representation of any integer of the given type.
*/
template<class Integer>
static_string<detail::max_digits(sizeof(Integer))>
to_static_string(Integer x);
} // beast
} // boost
#include <boost/beast/core/impl/static_string.ipp>
#endif