Commit e5ab813f authored by Victor Zverovich's avatar Victor Zverovich

Add a subset of the text library as an optional component

parent 0fc7bd15
......@@ -33,6 +33,8 @@ if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
endif ()
option(FMT_USE_TEXT "Use the text library." OFF)
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
......@@ -160,6 +162,10 @@ if (HAVE_OPEN)
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
endif ()
if (FMT_USE_TEXT)
set(FMT_SOURCES ${FMT_SOURCES} src/text/grapheme_break.cpp)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
......@@ -180,6 +186,11 @@ target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
if (FMT_USE_TEXT)
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/text>)
endif ()
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX d)
......
#ifndef BOOST_ASSERT
#include <assert.h>
# define BOOST_ASSERT(condition) assert(condition)
#endif
#ifndef BOOST_CXX14_CONSTEXPR
# define BOOST_CXX14_CONSTEXPR
#endif
#ifndef TEXT_BOOST_CONTAINER_SMALL_VECTOR_HPP
#define TEXT_BOOST_CONTAINER_SMALL_VECTOR_HPP
#include <vector>
namespace boost {
namespace container {
template <typename T, size_t>
using small_vector = std::vector<T>;
}
}
#endif // TEXT_BOOST_CONTAINER_SMALL_VECTOR_HPP
#ifndef BOOST_TEXT_ALGORITHM_HPP
#define BOOST_TEXT_ALGORITHM_HPP
#include <boost/text/detail/sentinel_tag.hpp>
#include <cstddef>
#include <iterator>
namespace boost { namespace text {
namespace detail {
template<typename Iter>
std::ptrdiff_t distance(Iter first, Iter last, non_sentinel_tag)
{
return std::distance(first, last);
}
template<typename Iter, typename Sentinel>
std::ptrdiff_t distance(Iter first, Sentinel last, sentinel_tag)
{
std::ptrdiff_t retval = 0;
while (first != last) {
++retval;
++first;
}
return retval;
}
}
/** Range-friendly version of `std::distance()`, taking an iterator and a
sentinel. */
template<typename Iter, typename Sentinel>
std::ptrdiff_t distance(Iter first, Sentinel last)
{
return detail::distance(
first,
last,
typename std::conditional<
std::is_same<Iter, Sentinel>::value,
detail::non_sentinel_tag,
detail::sentinel_tag>::type());
}
/** Range-friendly version of `std::find()`, taking an iterator and a
sentinel. */
template<typename BidiIter, typename Sentinel, typename T>
BidiIter find(BidiIter first, Sentinel last, T const & x)
{
while (first != last) {
if (*first == x)
return first;
++first;
}
return first;
}
/** A range-friendly compliment to `std::find()`; returns an iterator to
the first element not equal to `x`. */
template<typename BidiIter, typename Sentinel, typename T>
BidiIter find_not(BidiIter first, Sentinel last, T const & x)
{
while (first != last) {
if (*first != x)
return first;
++first;
}
return first;
}
/** Range-friendly version of `std::find_if()`, taking an iterator and a
sentinel. */
template<typename BidiIter, typename Sentinel, typename Pred>
BidiIter find_if(BidiIter first, Sentinel last, Pred p)
{
while (first != last) {
if (p(*first))
return first;
++first;
}
return first;
}
/** Range-friendly version of `std::find_if_not()`, taking an iterator and
a sentinel. */
template<typename BidiIter, typename Sentinel, typename Pred>
BidiIter find_if_not(BidiIter first, Sentinel last, Pred p)
{
while (first != last) {
if (!p(*first))
return first;
++first;
}
return first;
}
/** Analogue of `std::find()` that finds the last value in `[first, last)`
equal to `x`. */
template<typename BidiIter, typename T>
BidiIter find_backward(BidiIter first, BidiIter last, T const & x)
{
auto it = last;
while (it != first) {
if (*--it == x)
return it;
}
return last;
}
/** Analogue of `std::find()` that finds the last value in `[first, last)`
not equal to `x`. */
template<typename BidiIter, typename T>
BidiIter find_not_backward(BidiIter first, BidiIter last, T const & x)
{
auto it = last;
while (it != first) {
if (*--it != x)
return it;
}
return last;
}
/** Analogue of `std::find()` that finds the last value `v` in `[first,
last)` for which `p(v)` is true. */
template<typename BidiIter, typename Pred>
BidiIter find_if_backward(BidiIter first, BidiIter last, Pred p)
{
auto it = last;
while (it != first) {
if (p(*--it))
return it;
}
return last;
}
/** Analogue of `std::find()` that finds the last value `v` in `[first,
last)` for which `p(v)` is false. */
template<typename BidiIter, typename Pred>
BidiIter find_if_not_backward(BidiIter first, BidiIter last, Pred p)
{
auto it = last;
while (it != first) {
if (!p(*--it))
return it;
}
return last;
}
/** A utility range type returned by `foreach_subrange*()`. */
template<typename Iter, typename Sentinel = Iter>
struct foreach_subrange_range
{
using iterator = Iter;
using sentinel = Sentinel;
foreach_subrange_range() {}
foreach_subrange_range(iterator first, sentinel last) :
first_(first),
last_(last)
{}
iterator begin() const noexcept { return first_; }
sentinel end() const noexcept { return last_; }
private:
iterator first_;
sentinel last_;
};
/** Calls `f(sub)` for each subrange `sub` in `[first, last)`. A subrange
is a contiguous subsequence of elements that each compares equal to
the first element of the subsequence. Subranges passed to `f` are
non-overlapping. */
template<typename FwdIter, typename Sentinel, typename Func>
Func foreach_subrange(FwdIter first, Sentinel last, Func f)
{
while (first != last) {
auto const & x = *first;
auto const next = find_not(first, last, x);
if (first != next)
f(foreach_subrange_range<FwdIter, Sentinel>(first, next));
first = next;
}
return f;
}
/** Calls `f(sub)` for each subrange `sub` in `[first, last)`. A subrange
is a contiguous subsequence of elements that for each element `e`,
`proj(e)` each compares equal to `proj()` of the first element of the
subsequence. Subranges passed to `f` are non-overlapping. */
template<typename FwdIter, typename Sentinel, typename Func, typename Proj>
Func foreach_subrange(FwdIter first, Sentinel last, Func f, Proj proj)
{
using value_type = typename std::iterator_traits<FwdIter>::value_type;
while (first != last) {
auto const & x = proj(*first);
auto const next = find_if_not(
first, last, [&x, proj](const value_type & element) {
return proj(element) == x;
});
if (first != next)
f(foreach_subrange_range<FwdIter, Sentinel>(first, next));
first = next;
}
return f;
}
/** Calls `f(sub)` for each subrange `sub` in `[first, last)`. A subrange
is a contiguous subsequence of elements, each of which is equal to
`x`. Subranges passed to `f` are non-overlapping. */
template<typename FwdIter, typename Sentinel, typename T, typename Func>
Func foreach_subrange_of(FwdIter first, Sentinel last, T const & x, Func f)
{
while (first != last) {
first = find(first, last, x);
auto const next = find_not(first, last, x);
if (first != next)
f(foreach_subrange_range<FwdIter, Sentinel>(first, next));
first = next;
}
return f;
}
/** Calls `f(sub)` for each subrange `sub` in `[first, last)`. A subrange
is a contiguous subsequence of elements `ei` for which `p(ei)` is
true. Subranges passed to `f` are non-overlapping. */
template<typename FwdIter, typename Sentinel, typename Pred, typename Func>
Func foreach_subrange_if(FwdIter first, Sentinel last, Pred p, Func f)
{
while (first != last) {
first = boost::text::find_if(first, last, p);
auto const next = boost::text::find_if_not(first, last, p);
if (first != next)
f(foreach_subrange_range<FwdIter, Sentinel>(first, next));
first = next;
}
return f;
}
/** Sentinel-friendly version of `std::all_of()`. */
template<typename Iter, typename Sentinel, typename Pred>
bool all_of(Iter first, Sentinel last, Pred p)
{
for (; first != last; ++first) {
if (!p(*first))
return false;
}
return true;
}
}}
#endif
#ifndef BOOST_TEXT_CONFIG_HPP
#define BOOST_TEXT_CONFIG_HPP
#include <boost/config.hpp>
/** There are ICU-based implementations of many operations, but those are only
defined when BOOST_TEXT_HAS_ICU is nonzero. If you define this, you must
make sure the the ICU headers are in your path, and that your build
properly links in ICU. */
#ifndef BOOST_TEXT_HAS_ICU
# define BOOST_TEXT_HAS_ICU 0
#endif
/** There are ICU-based implementations of many operations, but those are only
used when BOOST_TEXT_HAS_ICU and BOOST_TEXT_USE_ICU are both nonzero. */
#ifndef BOOST_TEXT_USE_ICU
# define BOOST_TEXT_USE_ICU 0
#endif
/** When you insert into a rope, the incoming sequence may be inserted as a
new segment, or if it falls within an existing string-segment, it may be
inserted into the string object used to represent that segment. This only
happens if the incoming sequence will fit within the existing segment's
capacity, or if the segment is smaller than a certain limit.
BOOST_TEXT_STRING_INSERT_MAX is that limit. */
#ifndef BOOST_TEXT_STRING_INSERT_MAX
# define BOOST_TEXT_STRING_INSERT_MAX 4096
#endif
#ifndef BOOST_TEXT_DOXYGEN
// Nothing before GCC 6 has proper C++14 constexpr support.
#if defined(__GNUC__) && __GNUC__ < 6 && !defined(__clang__)
# define BOOST_TEXT_CXX14_CONSTEXPR
# define BOOST_TEXT_NO_CXX14_CONSTEXPR
#elif defined(_MSC_VER) && _MSC_VER <= 1915
# define BOOST_TEXT_CXX14_CONSTEXPR
# define BOOST_TEXT_NO_CXX14_CONSTEXPR
#else
# define BOOST_TEXT_CXX14_CONSTEXPR BOOST_CXX14_CONSTEXPR
# if defined(BOOST_NO_CXX14_CONSTEXPR)
# define BOOST_TEXT_NO_CXX14_CONSTEXPR
# endif
#endif
// Implements separate compilation features as described in
// http://www.boost.org/more/separate_compilation.html
// normalize macros
#if !defined(BOOST_TEXT_DYN_LINK) && !defined(BOOST_TEXT_STATIC_LINK) && \
!defined(BOOST_ALL_DYN_LINK) && !defined(BOOST_ALL_STATIC_LINK)
# define BOOST_TEXT_STATIC_LINK
#endif
#if defined(BOOST_ALL_DYN_LINK) && !defined(BOOST_TEXT_DYN_LINK)
# define BOOST_TEXT_DYN_LINK
#elif defined(BOOST_ALL_STATIC_LINK) && !defined(BOOST_TEXT_STATIC_LINK)
# define BOOST_TEXT_STATIC_LINK
#endif
#if defined(BOOST_TEXT_DYN_LINK) && defined(BOOST_TEXT_STATIC_LINK)
# error Must not define both BOOST_TEXT_DYN_LINK and BOOST_TEXT_STATIC_LINK
#endif
// enable dynamic or static linking as requested
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_TEXT_DYN_LINK)
# if defined(BOOST_TEXT_SOURCE)
# define BOOST_TEXT_DECL BOOST_SYMBOL_EXPORT
# else
# define BOOST_TEXT_DECL BOOST_SYMBOL_IMPORT
# endif
#else
# define BOOST_TEXT_DECL
#endif
#if 0 // TODO: Disabled for now.
// enable automatic library variant selection
#if !defined(BOOST_TEXT_SOURCE) && !defined(BOOST_ALL_NO_LIB) && \
!defined(BOOST_TEXT_NO_LIB)
//
// Set the name of our library, this will get undef'ed by auto_link.hpp
// once it's done with it:
//
#define BOOST_LIB_NAME boost_text
//
// If we're importing code from a dll, then tell auto_link.hpp about it:
//
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_TEXT_DYN_LINK)
# define BOOST_DYN_LINK
#endif
//
// And include the header that does the work:
//
#include <boost/config/auto_link.hpp>
#endif // auto-linking disabled
#endif
#endif // doxygen
#endif
This diff is collapsed.
#ifndef BOOST_TEXT_DETAIL_BREAK_PROP_ITER_HPP
#define BOOST_TEXT_DETAIL_BREAK_PROP_ITER_HPP
#include <boost/text/detail/lzw.hpp>
#include <unordered_map>
namespace boost { namespace text { namespace detail {
template<typename Enum>
struct lzw_to_break_prop_iter
{
using value_type = std::pair<uint32_t, Enum>;
using difference_type = int;
using pointer = unsigned char *;
using reference = unsigned char &;
using iterator_category = std::output_iterator_tag;
using buffer_t = container::small_vector<unsigned char, 256>;
lzw_to_break_prop_iter(
std::unordered_map<uint32_t, Enum> & map, buffer_t & buf) :
map_(&map),
buf_(&buf)
{}
lzw_to_break_prop_iter & operator=(unsigned char c)
{
buf_->push_back(c);
auto const element_bytes = 4;
auto it = buf_->begin();
for (auto end = buf_->end() - buf_->size() % element_bytes;
it != end;
it += element_bytes) {
(*map_)[bytes_to_cp(&*it)] = Enum(*(it + 3));
}
buf_->erase(buf_->begin(), it);
return *this;
}
lzw_to_break_prop_iter & operator*() { return *this; }
lzw_to_break_prop_iter & operator++() { return *this; }
lzw_to_break_prop_iter & operator++(int) { return *this; }
private:
std::unordered_map<uint32_t, Enum> * map_;
buffer_t * buf_;
};
}}}
#endif
This diff is collapsed.
#ifndef BOOST_TEXT_DETAIL_LZW_HPP
#define BOOST_TEXT_DETAIL_LZW_HPP
#include <boost/container/small_vector.hpp>
#include <vector>
namespace boost { namespace text { namespace detail {
inline uint32_t bytes_to_uint32_t(unsigned char const * chars)
{
return chars[0] << 24 | chars[1] << 16 | chars[2] << 8 | chars[3] << 0;
}
inline uint32_t bytes_to_cp(unsigned char const * chars)
{
return chars[0] << 16 | chars[1] << 8 | chars[2] << 0;
}
inline uint32_t bytes_to_uint16_t(unsigned char const * chars)
{
return chars[0] << 8 | chars[1] << 0;
}
enum : uint16_t { no_predecessor = 0xffff, no_value = 0xffff };
struct lzw_reverse_table_element
{
lzw_reverse_table_element(
uint16_t pred = no_predecessor, uint16_t value = no_value) :
pred_(pred),
value_(value)
{}
uint16_t pred_;
uint16_t value_;
};
using lzw_reverse_table = std::vector<lzw_reverse_table_element>;
template<typename OutIter>
OutIter
copy_table_entry(lzw_reverse_table const & table, uint16_t i, OutIter out)
{
*out++ = table[i].value_;
while (table[i].pred_ != no_predecessor) {
i = table[i].pred_;
*out++ = table[i].value_;
}
return out;
}
// Hardcoded to 16 bits. Takes unsigned 16-bit LZW-compressed values as
// input and writes the decompressed unsigned char values to out.
template<typename Iter, typename OutIter>
OutIter lzw_decompress(Iter first, Iter last, OutIter out)
{
lzw_reverse_table reverse_table(1 << 16);
for (uint16_t i = 0; i < 256u; ++i) {
reverse_table[i].value_ = i;
}
container::small_vector<unsigned char, 256> table_entry;
uint32_t next_table_value = 256;
uint32_t const end_table_value = 1 << 16;
uint16_t prev_code = *first++;
BOOST_ASSERT(prev_code < 256);
unsigned char c = (unsigned char)prev_code;
table_entry.push_back(c);
*out++ = table_entry;
while (first != last) {
uint16_t const code = *first++;
table_entry.clear();
if (reverse_table[code].value_ == no_value) {
table_entry.push_back(c);
copy_table_entry(
reverse_table, prev_code, std::back_inserter(table_entry));
} else {
copy_table_entry(
reverse_table, code, std::back_inserter(table_entry));
}
*out++ = table_entry;
c = table_entry.back();
if (next_table_value < end_table_value) {
reverse_table[next_table_value++] =
lzw_reverse_table_element{prev_code, c};
}
prev_code = code;
}
return out;
}
}}}
#endif
#ifndef BOOST_TEXT_DETAIL_SENTINEL_TAG_HPP
#define BOOST_TEXT_DETAIL_SENTINEL_TAG_HPP
namespace boost { namespace text { namespace detail {
struct sentinel_tag
{};
struct non_sentinel_tag
{};
}}}
#endif
This diff is collapsed.
#ifndef BOOST_TEXT_LAZY_SEGMENT_RANGE_HPP
#define BOOST_TEXT_LAZY_SEGMENT_RANGE_HPP
#include <boost/text/utility.hpp>
namespace boost { namespace text {
namespace detail {
template<typename CPIter, typename CPRange>
struct segment_arrow_proxy
{
explicit segment_arrow_proxy(CPRange value) : value_(value) {}
CPRange * operator->() const noexcept
{
return &value_;
}
private:
CPRange value_;
};
template<
typename CPIter,
typename Sentinel,
typename NextFunc,
typename CPRange>
struct const_lazy_segment_iterator
{
private:
NextFunc * next_func_;
CPIter prev_;
CPIter it_;
Sentinel last_;
public:
using value_type = CPRange;
using pointer = detail::segment_arrow_proxy<CPIter, CPRange>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
const_lazy_segment_iterator() noexcept :
next_func_(),
prev_(),
it_(),
last_()
{}
const_lazy_segment_iterator(CPIter it, Sentinel last) noexcept :
next_func_(),
prev_(it),
it_(),
last_(last)
{}
const_lazy_segment_iterator(Sentinel last) noexcept :
next_func_(),
prev_(),
it_(),
last_(last)
{}
reference operator*() const noexcept
{
return value_type{prev_, it_};
}
pointer operator->() const noexcept { return pointer(**this); }
const_lazy_segment_iterator & operator++() noexcept
{
auto const next_it = (*next_func_)(it_, last_);
prev_ = it_;
it_ = next_it;
return *this;
}
void set_next_func(NextFunc * next_func) noexcept
{
next_func_ = next_func;
it_ = (*next_func_)(prev_, last_);
}
friend bool operator==(
const_lazy_segment_iterator lhs,
const_lazy_segment_iterator rhs) noexcept
{
return lhs.prev_ == rhs.last_;
}
friend bool operator!=(
const_lazy_segment_iterator lhs,
const_lazy_segment_iterator rhs) noexcept
{
return !(lhs == rhs);
}
};
template<typename CPIter, typename, typename PrevFunc, typename CPRange>
struct const_reverse_lazy_segment_iterator
{
private:
PrevFunc * prev_func_;
CPIter first_;
CPIter it_;
CPIter next_;
public:
using value_type = CPRange;
using pointer = detail::segment_arrow_proxy<CPIter, CPRange>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
const_reverse_lazy_segment_iterator() noexcept :
prev_func_(),
first_(),
it_(),
next_()
{}
const_reverse_lazy_segment_iterator(
CPIter first, CPIter it, CPIter last) noexcept :
prev_func_(),
first_(first),
it_(it),
next_(last)
{}
reference operator*() const noexcept
{
return value_type{it_, next_};
}
pointer operator->() const noexcept { return pointer(**this); }
const_reverse_lazy_segment_iterator & operator++() noexcept
{
if (it_ == first_) {
next_ = first_;
return *this;
}
auto const prev_it =
(*prev_func_)(first_, std::prev(it_), next_);
next_ = it_;
it_ = prev_it;
return *this;
}
void set_next_func(PrevFunc * prev_func) noexcept
{
prev_func_ = prev_func;
++*this;
}
friend bool operator==(
const_reverse_lazy_segment_iterator lhs,
const_reverse_lazy_segment_iterator rhs) noexcept
{
return lhs.next_ == rhs.first_;
}
friend bool operator!=(
const_reverse_lazy_segment_iterator lhs,
const_reverse_lazy_segment_iterator rhs) noexcept
{
return !(lhs == rhs);
}
};
}
/** Represents a range of non-overlapping subranges. Each subrange
represents some semantically significant segment, the semantics of
which are controlled by the `NextFunc` template parameter. For
instance, if `NextFunc` is next_paragraph_break, the subranges
produced by lazy_segment_range will be paragraphs. Each subrange is
lazily produced; an output subrange is not produced until a lazy range
iterator is dereferenced. */
template<
typename CPIter,
typename Sentinel,
typename NextFunc,
typename CPRange = cp_range<CPIter>,
template<class, class, class, class> class IteratorTemplate =
detail::const_lazy_segment_iterator,
bool Reverse = false>
struct lazy_segment_range
{
using iterator = IteratorTemplate<CPIter, Sentinel, NextFunc, CPRange>;
lazy_segment_range() noexcept {}
lazy_segment_range(
NextFunc next_func, iterator first, iterator last) noexcept :
next_func_(std::move(next_func)),
first_(first),
last_(last)
{}
iterator begin() const noexcept
{
const_cast<iterator &>(first_).set_next_func(
const_cast<NextFunc *>(&next_func_));
return first_;
}
iterator end() const noexcept { return last_; }
/** Moves the contained `NextFunc` out of *this. */
NextFunc && next_func() && noexcept { return std::move(next_func_); }
private:
NextFunc next_func_;
iterator first_;
iterator last_;
};
}}
#endif
This diff is collapsed.
This diff is collapsed.
#ifndef TEXT_BOOST_THROW_EXCEPTION_HPP
#define TEXT_BOOST_THROW_EXCEPTION_HPP
namespace boost {
template <typename E>
void throw_exception(const E& e) { throw e; }
}
#endif // TEXT_BOOST_THROW_EXCEPTION_HPP
This diff is collapsed.
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