Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_BOUNDARY_HPP_INCLUDED
#define BOOST_LOCALE_BOUNDARY_HPP_INCLUDED
#include <boost/locale/boundary/boundary_point.hpp>
#include <boost/locale/boundary/facets.hpp>
#include <boost/locale/boundary/index.hpp>
#include <boost/locale/boundary/segment.hpp>
#include <boost/locale/boundary/types.hpp>
#endif
+127
View File
@@ -0,0 +1,127 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_BOUNDARY_BOUNDARY_POINT_HPP_INCLUDED
#define BOOST_LOCALE_BOUNDARY_BOUNDARY_POINT_HPP_INCLUDED
#include <boost/locale/boundary/types.hpp>
#include <string>
namespace boost { namespace locale { namespace boundary {
/// \addtogroup boundary
/// @{
/// \brief This class represents a boundary point in the text.
///
/// It represents a pair - an iterator and a rule that defines this
/// point.
///
/// This type of object is dereferenced by the iterators of boundary_point_index. Using a rule()
/// member function you can get the reason why this specific boundary point was selected.
///
/// For example, when you use sentence boundary analysis, the (rule() & \ref sentence_term) != 0 means
/// that this boundary point was selected because a sentence terminator (like .?!) was spotted
/// and the (rule() & \ref sentence_sep)!=0 means that a separator like line feed or carriage
/// return was observed.
///
/// \note
///
/// - The beginning of the analyzed range is always considered a boundary point and its rule is always 0.
/// - When using word boundary analysis, the returned rule relates to a chunk of text preceding
/// this point.
///
/// \see
///
/// - \ref boundary_point_index
/// - \ref segment
/// - \ref segment_index
///
template<typename IteratorType>
class boundary_point {
public:
/// The type of the base iterator that iterates the original text
typedef IteratorType iterator_type;
/// Empty default constructor
boundary_point() : rule_(0) {}
/// Create a new boundary_point using iterator \p and a rule \a r
boundary_point(iterator_type p, rule_type r) : iterator_(p), rule_(r) {}
/// Set an new iterator value \a i
void iterator(iterator_type i) { iterator_ = i; }
/// Fetch an iterator
iterator_type iterator() const { return iterator_; }
/// Set an new rule value \a r
void rule(rule_type r) { rule_ = r; }
/// Fetch a rule
rule_type rule() const { return rule_; }
/// Check if two boundary points are the same
bool operator==(const boundary_point& other) const
{
return iterator_ == other.iterator_ && rule_ = other.rule_;
}
/// Check if two boundary points are different
bool operator!=(const boundary_point& other) const { return !(*this == other); }
/// Check if the boundary point points to same location as an iterator \a other
bool operator==(const iterator_type& other) const { return iterator_ == other; }
/// Check if the boundary point points to different location from an iterator \a other
bool operator!=(const iterator_type& other) const { return iterator_ != other; }
/// Automatic cast to the iterator it represents
operator iterator_type() const { return iterator_; }
private:
iterator_type iterator_;
rule_type rule_;
};
/// Check if the boundary point \a r points to same location as an iterator \a l
template<typename BaseIterator>
bool operator==(const BaseIterator& l, const boundary_point<BaseIterator>& r)
{
return r == l;
}
/// Check if the boundary point \a r points to different location from an iterator \a l
template<typename BaseIterator>
bool operator!=(const BaseIterator& l, const boundary_point<BaseIterator>& r)
{
return r != l;
}
/// @}
typedef boundary_point<std::string::const_iterator> sboundary_point; ///< convenience typedef
typedef boundary_point<std::wstring::const_iterator> wsboundary_point; ///< convenience typedef
#ifndef BOOST_LOCALE_NO_CXX20_STRING8
typedef boundary_point<std::u8string::const_iterator> u8sboundary_point; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
typedef boundary_point<std::u16string::const_iterator> u16sboundary_point; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
typedef boundary_point<std::u32string::const_iterator> u32sboundary_point; ///< convenience typedef
#endif
typedef boundary_point<const char*> cboundary_point; ///< convenience typedef
typedef boundary_point<const wchar_t*> wcboundary_point; ///< convenience typedef
#ifdef __cpp_char8_t
typedef boundary_point<const char8_t*> u8cboundary_point; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
typedef boundary_point<const char16_t*> u16cboundary_point; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
typedef boundary_point<const char32_t*> u32cboundary_point; ///< convenience typedef
#endif
}}} // namespace boost::locale::boundary
#endif
+83
View File
@@ -0,0 +1,83 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_BOUNDARY_FACETS_HPP_INCLUDED
#define BOOST_LOCALE_BOUNDARY_FACETS_HPP_INCLUDED
#include <boost/locale/boundary/types.hpp>
#include <boost/locale/detail/facet_id.hpp>
#include <boost/locale/detail/is_supported_char.hpp>
#include <locale>
#include <vector>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \brief This namespace contains all operations required for boundary analysis of text
namespace boundary {
/// \addtogroup boundary
///
/// @{
/// \brief This structure is used for representing boundary points
/// that follow the offset.
struct break_info {
/// Create empty break point at beginning
break_info() : offset(0), rule(0) {}
/// Create an empty break point at offset v.
/// it is useful for order comparison with other points.
break_info(size_t v) : offset(v), rule(0) {}
/// Offset from the beginning of the text where a break occurs.
size_t offset;
/// The identification of this break point according to
/// various break types
rule_type rule;
/// Compare two break points' offset. Allows to search with
/// standard algorithms over the index.
bool operator<(const break_info& other) const { return offset < other.offset; }
};
/// This type holds the analysis of the text - all its break points
/// with marks
typedef std::vector<break_info> index_type;
/// \brief This facet generates an index for boundary analysis of a given text.
///
/// It is implemented for supported character types, at least \c char, \c wchar_t
template<typename Char>
class BOOST_SYMBOL_VISIBLE boundary_indexing : public std::locale::facet,
public boost::locale::detail::facet_id<boundary_indexing<Char>> {
BOOST_LOCALE_ASSERT_IS_SUPPORTED(Char);
public:
/// Default constructor typical for facets
boundary_indexing(size_t refs = 0) : std::locale::facet(refs) {}
/// Create index for boundary type \a t for text in range [begin,end)
///
/// The returned value is an index of type \ref index_type. Note that this
/// index is never empty, even if the range [begin,end) is empty it consists
/// of at least one boundary point with the offset 0.
virtual index_type map(boundary_type t, const Char* begin, const Char* end) const = 0;
};
/// @}
} // namespace boundary
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+930
View File
@@ -0,0 +1,930 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_BOUNDARY_INDEX_HPP_INCLUDED
#define BOOST_LOCALE_BOUNDARY_INDEX_HPP_INCLUDED
#include <boost/locale/boundary/boundary_point.hpp>
#include <boost/locale/boundary/facets.hpp>
#include <boost/locale/boundary/segment.hpp>
#include <boost/locale/boundary/types.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <locale>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale { namespace boundary {
///
/// \defgroup boundary Boundary Analysis
///
/// This module contains all operations required for %boundary analysis of text: character, word, line and sentence
/// boundaries
///
/// @{
///
/// \cond INTERNAL
namespace detail {
template<typename Char>
const boundary_indexing<Char>& get_boundary_indexing(const std::locale& l)
{
using facet_type = boundary_indexing<Char>;
if(!std::has_facet<facet_type>(l))
throw std::runtime_error("Locale was generated without segmentation support!");
return std::use_facet<facet_type>(l);
}
template<typename IteratorType,
typename CategoryType = typename std::iterator_traits<IteratorType>::iterator_category>
struct mapping_traits {
typedef typename std::iterator_traits<IteratorType>::value_type char_type;
static index_type map(boundary_type t, IteratorType b, IteratorType e, const std::locale& l)
{
std::basic_string<char_type> str(b, e);
return get_boundary_indexing<char_type>(l).map(t, str.c_str(), str.c_str() + str.size());
}
};
template<typename CharType, typename SomeIteratorType>
struct linear_iterator_traits {
static constexpr bool is_linear =
std::is_same<SomeIteratorType, CharType*>::value || std::is_same<SomeIteratorType, const CharType*>::value
|| std::is_same<SomeIteratorType, typename std::basic_string<CharType>::iterator>::value
|| std::is_same<SomeIteratorType, typename std::basic_string<CharType>::const_iterator>::value
|| std::is_same<SomeIteratorType, typename std::vector<CharType>::iterator>::value
|| std::is_same<SomeIteratorType, typename std::vector<CharType>::const_iterator>::value;
};
template<typename IteratorType>
struct mapping_traits<IteratorType, std::random_access_iterator_tag> {
typedef typename std::iterator_traits<IteratorType>::value_type char_type;
static index_type map(boundary_type t, IteratorType b, IteratorType e, const std::locale& l)
{
index_type result;
// Optimize for most common cases
//
// C++11 requires that string is continuous in memory and all known
// string implementations do this because of c_str() support.
if(linear_iterator_traits<char_type, IteratorType>::is_linear && b != e) {
const char_type* begin = &*b;
const char_type* end = begin + (e - b);
index_type tmp = get_boundary_indexing<char_type>(l).map(t, begin, end);
result.swap(tmp);
} else {
std::basic_string<char_type> str(b, e);
index_type tmp = get_boundary_indexing<char_type>(l).map(t, str.c_str(), str.c_str() + str.size());
result.swap(tmp);
}
return result;
}
};
template<typename BaseIterator>
class mapping {
public:
typedef BaseIterator base_iterator;
typedef typename std::iterator_traits<base_iterator>::value_type char_type;
mapping(boundary_type type, base_iterator begin, base_iterator end, const std::locale& loc) :
index_(new index_type()), begin_(begin), end_(end)
{
index_type idx = detail::mapping_traits<base_iterator>::map(type, begin, end, loc);
index_->swap(idx);
}
mapping() {}
const index_type& index() const { return *index_; }
base_iterator begin() const { return begin_; }
base_iterator end() const { return end_; }
private:
std::shared_ptr<index_type> index_;
base_iterator begin_, end_;
};
template<typename BaseIterator>
class segment_index_iterator : public boost::iterator_facade<segment_index_iterator<BaseIterator>,
segment<BaseIterator>,
boost::bidirectional_traversal_tag,
const segment<BaseIterator>&> {
public:
typedef BaseIterator base_iterator;
typedef mapping<base_iterator> mapping_type;
typedef segment<base_iterator> segment_type;
segment_index_iterator() : current_(0, 0), map_(nullptr), mask_(0), full_select_(false) {}
segment_index_iterator(base_iterator p, const mapping_type* map, rule_type mask, bool full_select) :
map_(map), mask_(mask), full_select_(full_select)
{
set(p);
}
segment_index_iterator(bool is_begin, const mapping_type* map, rule_type mask, bool full_select) :
map_(map), mask_(mask), full_select_(full_select)
{
if(is_begin)
set_begin();
else
set_end();
}
const segment_type& dereference() const { return value_; }
bool equal(const segment_index_iterator& other) const
{
return map_ == other.map_ && current_.second == other.current_.second;
}
void increment()
{
std::pair<size_t, size_t> next = current_;
if(full_select_) {
next.first = next.second;
while(next.second < size()) {
next.second++;
if(valid_offset(next.second))
break;
}
if(next.second == size())
next.first = next.second - 1;
} else {
while(next.second < size()) {
next.first = next.second;
next.second++;
if(valid_offset(next.second))
break;
}
}
update_current(next);
}
void decrement()
{
std::pair<size_t, size_t> next = current_;
if(full_select_) {
while(next.second > 1) {
next.second--;
if(valid_offset(next.second))
break;
}
next.first = next.second;
while(next.first > 0) {
next.first--;
if(valid_offset(next.first))
break;
}
} else {
while(next.second > 1) {
next.second--;
if(valid_offset(next.second))
break;
}
next.first = next.second - 1;
}
update_current(next);
}
private:
void set_end()
{
current_.first = size() - 1;
current_.second = size();
value_ = segment_type(map_->end(), map_->end(), 0);
}
void set_begin()
{
current_.first = current_.second = 0;
value_ = segment_type(map_->begin(), map_->begin(), 0);
increment();
}
void set(base_iterator p)
{
const auto b = map_->index().begin(), e = map_->index().end();
auto boundary_point = std::upper_bound(b, e, break_info(std::distance(map_->begin(), p)));
while(boundary_point != e && (boundary_point->rule & mask_) == 0)
++boundary_point;
current_.first = current_.second = boundary_point - b;
if(full_select_) {
while(current_.first > 0) {
current_.first--;
if(valid_offset(current_.first))
break;
}
} else {
if(current_.first > 0)
current_.first--;
}
value_.first = map_->begin();
std::advance(value_.first, get_offset(current_.first));
value_.second = value_.first;
std::advance(value_.second, get_offset(current_.second) - get_offset(current_.first));
update_rule();
}
void update_current(std::pair<size_t, size_t> pos)
{
std::ptrdiff_t first_diff = get_offset(pos.first) - get_offset(current_.first);
std::ptrdiff_t second_diff = get_offset(pos.second) - get_offset(current_.second);
std::advance(value_.first, first_diff);
std::advance(value_.second, second_diff);
current_ = pos;
update_rule();
}
void update_rule()
{
if(current_.second != size())
value_.rule(index()[current_.second].rule);
}
size_t get_offset(size_t ind) const
{
if(ind == size())
return index().back().offset;
return index()[ind].offset;
}
bool valid_offset(size_t offset) const
{
return offset == 0 || offset == size() // make sure we not acess index[size]
|| (index()[offset].rule & mask_) != 0;
}
size_t size() const { return index().size(); }
const index_type& index() const { return map_->index(); }
segment_type value_;
std::pair<size_t, size_t> current_;
const mapping_type* map_;
rule_type mask_;
bool full_select_;
};
template<typename BaseIterator>
class boundary_point_index_iterator : public boost::iterator_facade<boundary_point_index_iterator<BaseIterator>,
boundary_point<BaseIterator>,
boost::bidirectional_traversal_tag,
const boundary_point<BaseIterator>&> {
public:
typedef BaseIterator base_iterator;
typedef mapping<base_iterator> mapping_type;
typedef boundary_point<base_iterator> boundary_point_type;
boundary_point_index_iterator() : current_(0), map_(nullptr), mask_(0) {}
boundary_point_index_iterator(bool is_begin, const mapping_type* map, rule_type mask) :
map_(map), mask_(mask)
{
if(is_begin)
set_begin();
else
set_end();
}
boundary_point_index_iterator(base_iterator p, const mapping_type* map, rule_type mask) :
map_(map), mask_(mask)
{
set(p);
}
const boundary_point_type& dereference() const { return value_; }
bool equal(const boundary_point_index_iterator& other) const
{
return map_ == other.map_ && current_ == other.current_;
}
void increment()
{
size_t next = current_;
while(next < size()) {
next++;
if(valid_offset(next))
break;
}
update_current(next);
}
void decrement()
{
size_t next = current_;
while(next > 0) {
next--;
if(valid_offset(next))
break;
}
update_current(next);
}
private:
void set_end()
{
current_ = size();
value_ = boundary_point_type(map_->end(), 0);
}
void set_begin()
{
current_ = 0;
value_ = boundary_point_type(map_->begin(), 0);
}
void set(base_iterator p)
{
size_t dist = std::distance(map_->begin(), p);
const auto b = index().begin(), e = index().end();
const auto ptr = std::lower_bound(b, e, break_info(dist));
if(ptr == e)
current_ = size() - 1;
else
current_ = ptr - b;
while(!valid_offset(current_))
current_++;
std::ptrdiff_t diff = get_offset(current_) - dist;
std::advance(p, diff);
value_.iterator(p);
update_rule();
}
void update_current(size_t pos)
{
std::ptrdiff_t diff = get_offset(pos) - get_offset(current_);
base_iterator i = value_.iterator();
std::advance(i, diff);
current_ = pos;
value_.iterator(i);
update_rule();
}
void update_rule()
{
if(current_ != size())
value_.rule(index()[current_].rule);
}
size_t get_offset(size_t ind) const
{
if(ind == size())
return index().back().offset;
return index()[ind].offset;
}
bool valid_offset(size_t offset) const
{
return offset == 0 || offset + 1 >= size() // last and first are always valid regardless of mark
|| (index()[offset].rule & mask_) != 0;
}
size_t size() const { return index().size(); }
const index_type& index() const { return map_->index(); }
boundary_point_type value_;
size_t current_;
const mapping_type* map_;
rule_type mask_;
};
} // namespace detail
/// \endcond
template<typename BaseIterator>
class segment_index;
template<typename BaseIterator>
class boundary_point_index;
/// \brief This class holds an index of segments in the text range and allows to iterate over them
///
/// This class is provides \ref begin() and \ref end() member functions that return bidirectional iterators
/// to the \ref segment objects.
///
/// It provides two options on way of selecting segments:
///
/// - \ref rule(rule_type mask) - a mask that allows to select only specific types of segments according to
/// various masks %as \ref word_any.
/// \n
/// The default is to select any types of boundaries.
/// \n
/// For example: using word %boundary analysis, when the provided mask is \ref word_kana then the iterators
/// would iterate only over the words containing Kana letters and \ref word_any would select all types of
/// words excluding ranges that consist of white space and punctuation marks. So iterating over the text
/// "to be or not to be?" with \ref word_any rule would return segments "to", "be", "or", "not", "to", "be",
/// instead of default "to", " ", "be", " ", "or", " ", "not", " ", "to", " ", "be", "?".
/// - \ref full_select(bool how) - a flag that defines the way a range is selected if the rule of the previous
/// %boundary point does not fit the selected rule.
/// \n
/// For example: We want to fetch all sentences from the following text: "Hello! How\nare you?".
/// \n
/// This text contains three %boundary points separating it to sentences by different rules:
/// - The exclamation mark "!" ends the sentence "Hello!"
/// - The line feed that splits the sentence "How\nare you?" into two parts.
/// - The question mark that ends the second sentence.
/// \n
/// If you would only change the \ref rule() to \ref sentence_term then the segment_index would
/// provide two sentences "Hello!" and "are you?" %as only them actually terminated with required
/// terminator "!" or "?". But changing \ref full_select() to true, the selected segment would include
/// all the text up to previous valid %boundary point and would return two expected sentences:
/// "Hello!" and "How\nare you?".
///
/// This class allows to find a segment according to the given iterator in range using \ref find() member
/// function.
///
/// \note
///
/// - Changing any of the options - \ref rule() or \ref full_select() and of course re-indexing the text
/// invalidates existing iterators and they can't be used any more.
/// - segment_index can be created from boundary_point_index or other segment_index that was created with
/// same \ref boundary_type. This is very fast operation %as they shared same index
/// and it does not require its regeneration.
///
/// \see
///
/// - \ref boundary_point_index
/// - \ref segment
/// - \ref boundary_point
template<typename BaseIterator>
class segment_index {
public:
/// The type of the iterator used to iterate over the original text
typedef BaseIterator base_iterator;
#ifdef BOOST_LOCALE_DOXYGEN
/// The bidirectional iterator that iterates over \ref value_type objects.
///
/// - The iterators may be invalidated by use of any non-const member function
/// including but not limited to \ref rule(rule_type) and \ref full_select(bool).
/// - The returned value_type object is valid %as long %as iterator points to it.
/// So this following code is wrong %as t used after p was updated:
/// \code
/// segment_index<some_iterator>::iterator p=index.begin();
/// segment<some_iterator> &t = *p;
/// ++p;
/// std::cout << t.str() << std::endl;
/// \endcode
typedef unspecified_iterator_type iterator;
/// \copydoc iterator
typedef unspecified_iterator_type const_iterator;
#else
typedef detail::segment_index_iterator<base_iterator> iterator;
typedef detail::segment_index_iterator<base_iterator> const_iterator;
#endif
/// The type dereferenced by the \ref iterator and \ref const_iterator. It is
/// an object that represents selected segment.
typedef segment<base_iterator> value_type;
/// Default constructor.
///
/// \note
///
/// When this object is constructed by default it does not include a valid index, thus
/// calling \ref begin(), \ref end() or \ref find() member functions would lead to undefined
/// behavior
segment_index() : mask_(0xFFFFFFFFu), full_select_(false) {}
/// Create a segment_index for %boundary analysis \ref boundary_type "type" of the text
/// in range [begin,end) using a rule \a mask for locale \a loc.
segment_index(boundary_type type,
base_iterator begin,
base_iterator end,
rule_type mask,
const std::locale& loc = std::locale()) :
map_(type, begin, end, loc),
mask_(mask), full_select_(false)
{}
/// Create a segment_index for %boundary analysis \ref boundary_type "type" of the text
/// in range [begin,end) selecting all possible segments (full mask) for locale \a loc.
segment_index(boundary_type type,
base_iterator begin,
base_iterator end,
const std::locale& loc = std::locale()) :
map_(type, begin, end, loc),
mask_(0xFFFFFFFFu), full_select_(false)
{}
/// Create a segment_index from a \ref boundary_point_index. It copies all indexing information
/// and used default rule (all possible segments)
///
/// This operation is very cheap, so if you use boundary_point_index and segment_index on same text
/// range it is much better to create one from another rather then indexing the same
/// range twice.
///
/// \note \ref rule() flags are not copied
segment_index(const boundary_point_index<base_iterator>&);
/// Copy an index from a \ref boundary_point_index. It copies all indexing information
/// and uses the default rule (all possible segments)
///
/// This operation is very cheap, so if you use boundary_point_index and segment_index on same text
/// range it is much better to create one from another rather then indexing the same
/// range twice.
///
/// \note \ref rule() flags are not copied
segment_index& operator=(const boundary_point_index<base_iterator>&);
/// Create a new index for %boundary analysis \ref boundary_type "type" of the text
/// in range [begin,end) for locale \a loc.
///
/// \note \ref rule() and \ref full_select() remain unchanged.
void map(boundary_type type, base_iterator begin, base_iterator end, const std::locale& loc = std::locale())
{
map_ = mapping_type(type, begin, end, loc);
}
/// Get the \ref iterator on the beginning of the segments range.
///
/// Preconditions: the segment_index should have a mapping
///
/// \note
///
/// The returned iterator is invalidated by access to any non-const member functions of this object
iterator begin() const
{
return iterator(true, &map_, mask_, full_select_);
}
/// Get the \ref iterator on the ending of the segments range.
///
/// Preconditions: the segment_index should have a mapping
///
/// The returned iterator is invalidated by access to any non-const member functions of this object
iterator end() const
{
return iterator(false, &map_, mask_, full_select_);
}
/// Find a first valid segment following a position \a p.
///
/// If \a p is inside a valid segment this segment is selected:
///
/// For example: For \ref word %boundary analysis with \ref word_any rule():
///
/// - "to| be or ", would point to "be",
/// - "t|o be or ", would point to "to",
/// - "to be or| ", would point to end.
///
///
/// Preconditions: the segment_index should have a mapping and \a p should be valid iterator
/// to the text in the mapped range.
///
/// The returned iterator is invalidated by access to any non-const member functions of this object
iterator find(base_iterator p) const
{
return iterator(p, &map_, mask_, full_select_);
}
/// Get the mask of rules that are used
rule_type rule() const
{
return mask_;
}
/// Set the mask of rules that are used
void rule(rule_type v)
{
mask_ = v;
}
/// Get the full_select property value - should segment include in the range
/// values that not belong to specific \ref rule() or not.
///
/// The default value is false.
///
/// For example for \ref sentence %boundary with rule \ref sentence_term the segments
/// of text "Hello! How\nare you?" are "Hello!\", "are you?" when full_select() is false
/// because "How\n" is selected %as sentence by a rule spits the text by line feed. If full_select()
/// is true the returned segments are "Hello! ", "How\nare you?" where "How\n" is joined with the
/// following part "are you?"
bool full_select() const
{
return full_select_;
}
/// Set the full_select property value - should segment include in the range
/// values that not belong to specific \ref rule() or not.
///
/// The default value is false.
///
/// For example for \ref sentence %boundary with rule \ref sentence_term the segments
/// of text "Hello! How\nare you?" are "Hello!\", "are you?" when full_select() is false
/// because "How\n" is selected %as sentence by a rule spits the text by line feed. If full_select()
/// is true the returned segments are "Hello! ", "How\nare you?" where "How\n" is joined with the
/// following part "are you?"
void full_select(bool v)
{
full_select_ = v;
}
private:
friend class boundary_point_index<base_iterator>;
typedef detail::mapping<base_iterator> mapping_type;
mapping_type map_;
rule_type mask_;
bool full_select_;
};
/// \brief This class holds an index of \ref boundary_point "boundary points" and allows iterating
/// over them.
///
/// This class is provides \ref begin() and \ref end() member functions that return bidirectional iterators
/// to the \ref boundary_point objects.
///
/// It provides an option that affects selecting %boundary points according to different rules:
/// using \ref rule(rule_type mask) member function. It allows to set a mask that select only specific
/// types of %boundary points like \ref sentence_term.
///
/// For example for a sentence %boundary analysis of a text "Hello! How\nare you?" when the default
/// rule is used the %boundary points would be:
///
/// - "|Hello! How\nare you?"
/// - "Hello! |How\nare you?"
/// - "Hello! How\n|are you?"
/// - "Hello! How\nare you?|"
///
/// However if \ref rule() is set to \ref sentence_term then the selected %boundary points would be:
///
/// - "|Hello! How\nare you?"
/// - "Hello! |How\nare you?"
/// - "Hello! How\nare you?|"
///
/// Such that a %boundary point defined by a line feed character would be ignored.
///
/// This class allows to find a boundary_point according to the given iterator in range using \ref find() member
/// function.
///
/// \note
/// - Even an empty text range [x,x) considered to have a one %boundary point x.
/// - \a a and \a b points of the range [a,b) are always considered %boundary points
/// regardless the rules used.
/// - Changing any of the option \ref rule() or course re-indexing the text
/// invalidates existing iterators and they can't be used any more.
/// - boundary_point_index can be created from segment_index or other boundary_point_index that was created with
/// same \ref boundary_type. This is very fast operation %as they shared same index
/// and it does not require its regeneration.
///
/// \see
///
/// - \ref segment_index
/// - \ref boundary_point
/// - \ref segment
template<typename BaseIterator>
class boundary_point_index {
public:
/// The type of the iterator used to iterate over the original text
typedef BaseIterator base_iterator;
#ifdef BOOST_LOCALE_DOXYGEN
/// The bidirectional iterator that iterates over \ref value_type objects.
///
/// - The iterators may be invalidated by use of any non-const member function
/// including but not limited to \ref rule(rule_type) member function.
/// - The returned value_type object is valid %as long %as iterator points to it.
/// So this following code is wrong %as t used after p was updated:
/// \code
/// boundary_point_index<some_iterator>::iterator p=index.begin();
/// boundary_point<some_iterator> &t = *p;
/// ++p;
/// rule_type r = t->rule();
/// \endcode
///
typedef unspecified_iterator_type iterator;
/// \copydoc iterator
typedef unspecified_iterator_type const_iterator;
#else
typedef detail::boundary_point_index_iterator<base_iterator> iterator;
typedef detail::boundary_point_index_iterator<base_iterator> const_iterator;
#endif
/// The type dereferenced by the \ref iterator and \ref const_iterator. It is
/// an object that represents the selected \ref boundary_point "boundary point".
typedef boundary_point<base_iterator> value_type;
/// Default constructor.
///
/// \note
///
/// When this object is constructed by default it does not include a valid index, thus
/// calling \ref begin(), \ref end() or \ref find() member functions would lead to undefined
/// behavior
boundary_point_index() : mask_(0xFFFFFFFFu) {}
/// Create a segment_index for %boundary analysis \ref boundary_type "type" of the text
/// in range [begin,end) using a rule \a mask for locale \a loc.
boundary_point_index(boundary_type type,
base_iterator begin,
base_iterator end,
rule_type mask,
const std::locale& loc = std::locale()) :
map_(type, begin, end, loc),
mask_(mask)
{}
/// Create a segment_index for %boundary analysis \ref boundary_type "type" of the text
/// in range [begin,end) selecting all possible %boundary points (full mask) for locale \a loc.
boundary_point_index(boundary_type type,
base_iterator begin,
base_iterator end,
const std::locale& loc = std::locale()) :
map_(type, begin, end, loc),
mask_(0xFFFFFFFFu)
{}
/// Create a boundary_point_index from a \ref segment_index. It copies all indexing information
/// and uses the default rule (all possible %boundary points)
///
/// This operation is very cheap, so if you use boundary_point_index and segment_index on the same text
/// range it is much better to create one from another rather then indexing the same
/// range twice.
///
/// \note \ref rule() flags are not copied
boundary_point_index(const segment_index<base_iterator>& other);
/// Copy a boundary_point_index from a \ref segment_index. It copies all indexing information
/// and keeps the current \ref rule() unchanged
///
/// This operation is very cheap, so if you use boundary_point_index and segment_index on the same text
/// range it is much better to create one from another rather then indexing the same
/// range twice.
///
/// \note \ref rule() flags are not copied
boundary_point_index& operator=(const segment_index<base_iterator>& other);
/// Create a new index for %boundary analysis \ref boundary_type "type" of the text
/// in range [begin,end) for locale \a loc.
///
/// \note \ref rule() remains unchanged.
void map(boundary_type type, base_iterator begin, base_iterator end, const std::locale& loc = std::locale())
{
map_ = mapping_type(type, begin, end, loc);
}
/// Get the \ref iterator on the beginning of the %boundary points range.
///
/// Preconditions: this boundary_point_index should have a mapping
///
/// \note
///
/// The returned iterator is invalidated by access to any non-const member functions of this object
iterator begin() const
{
return iterator(true, &map_, mask_);
}
/// Get the \ref iterator on the ending of the %boundary points range.
///
/// Preconditions: this boundary_point_index should have a mapping
///
/// \note
///
/// The returned iterator is invalidated by access to any non-const member functions of this object
iterator end() const
{
return iterator(false, &map_, mask_);
}
/// Find a first valid %boundary point on a position \a p or following it.
///
/// For example: For \ref word %boundary analysis of the text "to be or"
///
/// - "|to be", would return %boundary point at "|to be",
/// - "t|o be", would point to "to| be"
///
/// Preconditions: the boundary_point_index should have a mapping and \a p should be valid iterator
/// to the text in the mapped range.
///
/// The returned iterator is invalidated by access to any non-const member functions of this object
iterator find(base_iterator p) const
{
return iterator(p, &map_, mask_);
}
/// Get the mask of rules that are used
rule_type rule() const
{
return mask_;
}
/// Set the mask of rules that are used
void rule(rule_type v)
{
mask_ = v;
}
private:
friend class segment_index<base_iterator>;
typedef detail::mapping<base_iterator> mapping_type;
mapping_type map_;
rule_type mask_;
};
/// \cond INTERNAL
template<typename BaseIterator>
segment_index<BaseIterator>::segment_index(const boundary_point_index<BaseIterator>& other) :
map_(other.map_), mask_(0xFFFFFFFFu), full_select_(false)
{}
template<typename BaseIterator>
boundary_point_index<BaseIterator>::boundary_point_index(const segment_index<BaseIterator>& other) :
map_(other.map_), mask_(0xFFFFFFFFu)
{}
template<typename BaseIterator>
segment_index<BaseIterator>& segment_index<BaseIterator>::operator=(const boundary_point_index<BaseIterator>& other)
{
map_ = other.map_;
return *this;
}
template<typename BaseIterator>
boundary_point_index<BaseIterator>&
boundary_point_index<BaseIterator>::operator=(const segment_index<BaseIterator>& other)
{
map_ = other.map_;
return *this;
}
/// \endcond
typedef segment_index<std::string::const_iterator> ssegment_index; ///< convenience typedef
typedef segment_index<std::wstring::const_iterator> wssegment_index; ///< convenience typedef
#ifndef BOOST_LOCALE_NO_CXX20_STRING8
typedef segment_index<std::u8string::const_iterator> u8ssegment_index; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
typedef segment_index<std::u16string::const_iterator> u16ssegment_index; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
typedef segment_index<std::u32string::const_iterator> u32ssegment_index; ///< convenience typedef
#endif
typedef segment_index<const char*> csegment_index; ///< convenience typedef
typedef segment_index<const wchar_t*> wcsegment_index; ///< convenience typedef
#ifdef __cpp_char8_t
typedef segment_index<const char8_t*> u8csegment_index; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
typedef segment_index<const char16_t*> u16csegment_index; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
typedef segment_index<const char32_t*> u32csegment_index; ///< convenience typedef
#endif
typedef boundary_point_index<std::string::const_iterator> sboundary_point_index; ///< convenience typedef
typedef boundary_point_index<std::wstring::const_iterator> wsboundary_point_index; ///< convenience typedef
#ifndef BOOST_LOCALE_NO_CXX20_STRING8
typedef boundary_point_index<std::u8string::const_iterator> u8sboundary_point_index; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
typedef boundary_point_index<std::u16string::const_iterator> u16sboundary_point_index; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
typedef boundary_point_index<std::u32string::const_iterator> u32sboundary_point_index; ///< convenience typedef
#endif
typedef boundary_point_index<const char*> cboundary_point_index; ///< convenience typedef
typedef boundary_point_index<const wchar_t*> wcboundary_point_index; ///< convenience typedef
#ifdef __cpp_char8_t
typedef boundary_point_index<const char8_t*> u8cboundary_point_index; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
typedef boundary_point_index<const char16_t*> u16cboundary_point_index; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
typedef boundary_point_index<const char32_t*> u32cboundary_point_index; ///< convenience typedef
#endif
}}} // namespace boost::locale::boundary
///
/// \example boundary.cpp
/// Example of using segment_index
/// \example wboundary.cpp
/// Example of using segment_index over wide strings
///
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+383
View File
@@ -0,0 +1,383 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_BOUNDARY_SEGMENT_HPP_INCLUDED
#define BOOST_LOCALE_BOUNDARY_SEGMENT_HPP_INCLUDED
#include <boost/locale/util/string.hpp>
#include <iosfwd>
#include <iterator>
#include <locale>
#include <string>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale { namespace boundary {
/// \cond INTERNAL
namespace detail {
template<typename LeftIterator, typename RightIterator>
int compare_text(LeftIterator l_begin, LeftIterator l_end, RightIterator r_begin, RightIterator r_end)
{
typedef LeftIterator left_iterator;
typedef typename std::iterator_traits<left_iterator>::value_type char_type;
typedef std::char_traits<char_type> traits;
while(l_begin != l_end && r_begin != r_end) {
char_type lchar = *l_begin++;
char_type rchar = *r_begin++;
if(traits::eq(lchar, rchar))
continue;
if(traits::lt(lchar, rchar))
return -1;
else
return 1;
}
if(l_begin == l_end && r_begin == r_end)
return 0;
if(l_begin == l_end)
return -1;
else
return 1;
}
template<typename Left, typename Right>
int compare_text(const Left& l, const Right& r)
{
return compare_text(l.begin(), l.end(), r.begin(), r.end());
}
template<typename Left, typename Char>
int compare_string(const Left& l, const Char* begin)
{
return compare_text(l.begin(), l.end(), begin, util::str_end(begin));
}
template<typename Right, typename Char>
int compare_string(const Char* begin, const Right& r)
{
return compare_text(begin, util::str_end(begin), r.begin(), r.end());
}
} // namespace detail
/// \endcond
/// \addtogroup boundary
/// @{
/// \brief a segment object that represents a pair of two iterators that define the range where
/// this segment exits and a rule that defines it.
///
/// This type of object is dereferenced by the iterators of segment_index. Using a rule() member function
/// you can get a specific rule this segment was selected with. For example, when you use
/// word boundary analysis, you can check if the specific word contains Kana letters by checking (rule() & \ref
/// word_kana)!=0 For a sentence analysis you can check if the sentence is selected because a sentence terminator is
/// found (\ref sentence_term) or there is a line break (\ref sentence_sep).
///
/// This object can be automatically converted to std::basic_string with the same type of character. It is also
/// valid range that has begin() and end() member functions returning iterators on the location of the segment.
///
/// \see
///
/// - \ref segment_index
/// - \ref boundary_point
/// - \ref boundary_point_index
template<typename IteratorType>
class segment : public std::pair<IteratorType, IteratorType> {
public:
/// The type of the underlying character
typedef typename std::iterator_traits<IteratorType>::value_type char_type;
/// The type of the string it is converted to
typedef std::basic_string<char_type> string_type;
/// The value that iterators return - the character itself
typedef char_type value_type;
/// The iterator that allows to iterate the range
typedef IteratorType iterator;
/// The iterator that allows to iterate the range
typedef IteratorType const_iterator;
/// The type that represent a difference between two iterators
typedef typename std::iterator_traits<IteratorType>::difference_type difference_type;
/// Default constructor
segment() : rule_(0) {}
/// Create a segment using two iterators and a rule that represents this point
segment(iterator b, iterator e, rule_type r) : std::pair<IteratorType, IteratorType>(b, e), rule_(r) {}
/// Set the start of the range
void begin(const iterator& v) { this->first = v; }
/// Set the end of the range
void end(const iterator& v) { this->second = v; }
/// Get the start of the range
IteratorType begin() const { return this->first; }
/// Set the end of the range
IteratorType end() const { return this->second; }
/// Convert the range to a string automatically
template<class T, class A>
operator std::basic_string<char_type, T, A>() const
{
return std::basic_string<char_type, T, A>(this->first, this->second);
}
/// Create a string from the range explicitly
string_type str() const { return string_type(begin(), end()); }
/// Get the length of the text chunk
size_t length() const { return std::distance(begin(), end()); }
/// Check if the segment is empty
bool empty() const { return begin() == end(); }
/// Get the rule that is used for selection of this segment.
rule_type rule() const { return rule_; }
/// Set a rule that is used for segment selection
void rule(rule_type r) { rule_ = r; }
// make sure we override std::pair's operator==
/// Compare two segments
bool operator==(const segment& other) const { return detail::compare_text(*this, other) == 0; }
/// Compare two segments
bool operator!=(const segment& other) const { return detail::compare_text(*this, other) != 0; }
private:
rule_type rule_;
};
/// Compare two segments
template<typename IteratorL, typename IteratorR>
bool operator==(const segment<IteratorL>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) == 0;
}
/// Compare two segments
template<typename IteratorL, typename IteratorR>
bool operator!=(const segment<IteratorL>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) != 0;
}
/// Compare two segments
template<typename IteratorL, typename IteratorR>
bool operator<(const segment<IteratorL>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) < 0;
}
/// Compare two segments
template<typename IteratorL, typename IteratorR>
bool operator<=(const segment<IteratorL>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) <= 0;
}
/// Compare two segments
template<typename IteratorL, typename IteratorR>
bool operator>(const segment<IteratorL>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) > 0;
}
/// Compare two segments
template<typename IteratorL, typename IteratorR>
bool operator>=(const segment<IteratorL>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) >= 0;
}
/// Compare string and segment
template<typename CharType, typename Traits, typename Alloc, typename IteratorR>
bool operator==(const std::basic_string<CharType, Traits, Alloc>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) == 0;
}
/// Compare string and segment
template<typename CharType, typename Traits, typename Alloc, typename IteratorR>
bool operator!=(const std::basic_string<CharType, Traits, Alloc>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) != 0;
}
/// Compare string and segment
template<typename CharType, typename Traits, typename Alloc, typename IteratorR>
bool operator<(const std::basic_string<CharType, Traits, Alloc>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) < 0;
}
/// Compare string and segment
template<typename CharType, typename Traits, typename Alloc, typename IteratorR>
bool operator<=(const std::basic_string<CharType, Traits, Alloc>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) <= 0;
}
/// Compare string and segment
template<typename CharType, typename Traits, typename Alloc, typename IteratorR>
bool operator>(const std::basic_string<CharType, Traits, Alloc>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) > 0;
}
/// Compare string and segment
template<typename CharType, typename Traits, typename Alloc, typename IteratorR>
bool operator>=(const std::basic_string<CharType, Traits, Alloc>& l, const segment<IteratorR>& r)
{
return detail::compare_text(l, r) >= 0;
}
/// Compare string and segment
template<typename Iterator, typename CharType, typename Traits, typename Alloc>
bool operator==(const segment<Iterator>& l, const std::basic_string<CharType, Traits, Alloc>& r)
{
return detail::compare_text(l, r) == 0;
}
/// Compare string and segment
template<typename Iterator, typename CharType, typename Traits, typename Alloc>
bool operator!=(const segment<Iterator>& l, const std::basic_string<CharType, Traits, Alloc>& r)
{
return detail::compare_text(l, r) != 0;
}
/// Compare string and segment
template<typename Iterator, typename CharType, typename Traits, typename Alloc>
bool operator<(const segment<Iterator>& l, const std::basic_string<CharType, Traits, Alloc>& r)
{
return detail::compare_text(l, r) < 0;
}
/// Compare string and segment
template<typename Iterator, typename CharType, typename Traits, typename Alloc>
bool operator<=(const segment<Iterator>& l, const std::basic_string<CharType, Traits, Alloc>& r)
{
return detail::compare_text(l, r) <= 0;
}
/// Compare string and segment
template<typename Iterator, typename CharType, typename Traits, typename Alloc>
bool operator>(const segment<Iterator>& l, const std::basic_string<CharType, Traits, Alloc>& r)
{
return detail::compare_text(l, r) > 0;
}
/// Compare string and segment
template<typename Iterator, typename CharType, typename Traits, typename Alloc>
bool operator>=(const segment<Iterator>& l, const std::basic_string<CharType, Traits, Alloc>& r)
{
return detail::compare_text(l, r) >= 0;
}
/// Compare C string and segment
template<typename CharType, typename IteratorR>
bool operator==(const CharType* l, const segment<IteratorR>& r)
{
return detail::compare_string(l, r) == 0;
}
/// Compare C string and segment
template<typename CharType, typename IteratorR>
bool operator!=(const CharType* l, const segment<IteratorR>& r)
{
return detail::compare_string(l, r) != 0;
}
/// Compare C string and segment
template<typename CharType, typename IteratorR>
bool operator<(const CharType* l, const segment<IteratorR>& r)
{
return detail::compare_string(l, r) < 0;
}
/// Compare C string and segment
template<typename CharType, typename IteratorR>
bool operator<=(const CharType* l, const segment<IteratorR>& r)
{
return detail::compare_string(l, r) <= 0;
}
/// Compare C string and segment
template<typename CharType, typename IteratorR>
bool operator>(const CharType* l, const segment<IteratorR>& r)
{
return detail::compare_string(l, r) > 0;
}
/// Compare C string and segment
template<typename CharType, typename IteratorR>
bool operator>=(const CharType* l, const segment<IteratorR>& r)
{
return detail::compare_string(l, r) >= 0;
}
/// Compare C string and segment
template<typename Iterator, typename CharType>
bool operator==(const segment<Iterator>& l, const CharType* r)
{
return detail::compare_string(l, r) == 0;
}
/// Compare C string and segment
template<typename Iterator, typename CharType>
bool operator!=(const segment<Iterator>& l, const CharType* r)
{
return detail::compare_string(l, r) != 0;
}
/// Compare C string and segment
template<typename Iterator, typename CharType>
bool operator<(const segment<Iterator>& l, const CharType* r)
{
return detail::compare_string(l, r) < 0;
}
/// Compare C string and segment
template<typename Iterator, typename CharType>
bool operator<=(const segment<Iterator>& l, const CharType* r)
{
return detail::compare_string(l, r) <= 0;
}
/// Compare C string and segment
template<typename Iterator, typename CharType>
bool operator>(const segment<Iterator>& l, const CharType* r)
{
return detail::compare_string(l, r) > 0;
}
/// Compare C string and segment
template<typename Iterator, typename CharType>
bool operator>=(const segment<Iterator>& l, const CharType* r)
{
return detail::compare_string(l, r) >= 0;
}
typedef segment<std::string::const_iterator> ssegment; ///< convenience typedef
typedef segment<std::wstring::const_iterator> wssegment; ///< convenience typedef
#ifndef BOOST_LOCALE_NO_CXX20_STRING8
typedef segment<std::u8string::const_iterator> u8ssegment; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
typedef segment<std::u16string::const_iterator> u16ssegment; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
typedef segment<std::u32string::const_iterator> u32ssegment; ///< convenience typedef
#endif
typedef segment<const char*> csegment; ///< convenience typedef
typedef segment<const wchar_t*> wcsegment; ///< convenience typedef
#ifdef __cpp_char8_t
typedef segment<const char8_t*> u8csegment; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
typedef segment<const char16_t*> u16csegment; ///< convenience typedef
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
typedef segment<const char32_t*> u32csegment; ///< convenience typedef
#endif
/// Write the segment to the stream character by character
template<typename CharType, typename TraitsType, typename Iterator>
std::basic_ostream<CharType, TraitsType>& operator<<(std::basic_ostream<CharType, TraitsType>& out,
const segment<Iterator>& seg)
{
for(const auto& p : seg)
out << p;
return out;
}
/// @}
}}} // namespace boost::locale::boundary
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+110
View File
@@ -0,0 +1,110 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_BOUNDARY_TYPES_HPP_INCLUDED
#define BOOST_LOCALE_BOUNDARY_TYPES_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <cstdint>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \brief This namespace contains all operations required for boundary analysis of text
namespace boundary {
/// \defgroup boundary Boundary Analysis
///
/// This module contains all operations required for boundary analysis of text: character, word, like and
/// sentence boundaries
///
/// @{
/// This type describes a possible boundary analysis alternatives.
enum boundary_type {
character, ///< Analyse the text for character boundaries
word, ///< Analyse the text for word boundaries
sentence, ///< Analyse the text for Find sentence boundaries
line ///< Analyse the text for positions suitable for line breaks
};
/// \brief Flags used with word boundary analysis -- the type of the word, line or sentence boundary found.
///
/// It is a bit-mask that represents various combinations of rules used to select this specific boundary.
typedef uint32_t rule_type;
/// \anchor bl_boundary_word_rules
/// \name Flags that describe a type of word selected
/// @{
constexpr rule_type word_none = 0x0000F, ///< Not a word, like white space or punctuation mark
word_number = 0x000F0, ///< Word that appear to be a number
word_letter = 0x00F00, ///< Word that contains letters, excluding kana and ideographic characters
word_kana = 0x0F000, ///< Word that contains kana characters
word_ideo = 0xF0000, ///< Word that contains ideographic characters
word_any = 0xFFFF0, ///< Any word including numbers, 0 is special flag, equivalent to 15
word_letters = 0xFFF00, ///< Any word, excluding numbers but including letters, kana and ideograms.
word_kana_ideo = 0xFF000, ///< Word that includes kana or ideographic characters
word_mask = 0xFFFFF; ///< Full word mask - select all possible variants
/// @}
/// \anchor bl_boundary_line_rules
/// \name Flags that describe a type of line break
/// @{
constexpr rule_type line_soft = 0x0F, ///< Soft line break: optional but not required
line_hard = 0xF0, ///< Hard line break: like break is required (as per CR/LF)
line_any = 0xFF, ///< Soft or Hard line break
line_mask = 0xFF; ///< Select all types of line breaks
/// @}
/// \anchor bl_boundary_sentence_rules
/// \name Flags that describe a type of sentence break
///
/// @{
constexpr rule_type sentence_term = 0x0F, ///< \brief The sentence was terminated with a sentence terminator
/// like ".", "!" possible followed by hard separator like CR, LF, PS
sentence_sep =
0xF0, ///< \brief The sentence does not contain terminator like ".", "!" but ended with hard separator
/// like CR, LF, PS or end of input.
sentence_any = 0xFF, ///< Either first or second sentence break type;.
sentence_mask = 0xFF; ///< Select all sentence breaking points
///@}
/// \name Flags that describe a type of character break.
///
/// At this point break iterator does not distinguish different
/// kinds of characters so it is used for consistency.
///@{
constexpr rule_type character_any = 0xF, ///< Not in use, just for consistency
character_mask = 0xF; ///< Select all character breaking points
///@}
/// This function returns the mask that covers all variants for specific boundary type
inline rule_type boundary_rule(boundary_type t)
{
switch(t) {
case character: return character_mask;
case word: return word_mask;
case sentence: return sentence_mask;
case line: return line_mask;
}
return 0;
}
///@}
} // namespace boundary
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+202
View File
@@ -0,0 +1,202 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_COLLATOR_HPP_INCLUDED
#define BOOST_LOCALE_COLLATOR_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <locale>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \defgroup collation Collation
///
/// This module introduces collation related classes
/// @{
/// Unicode collation level types
enum class collate_level {
primary = 0, ///< 1st collation level: base letters
secondary = 1, ///< 2nd collation level: letters and accents
tertiary = 2, ///< 3rd collation level: letters, accents and case
quaternary = 3, ///< 4th collation level: letters, accents, case and punctuation
identical = 4 ///< identical collation level: include code-point comparison
};
class BOOST_DEPRECATED("Use collate_level") collator_base {
public:
using level_type = collate_level;
static constexpr auto primary = collate_level::primary;
static constexpr auto secondary = collate_level::secondary;
static constexpr auto tertiary = collate_level::tertiary;
static constexpr auto quaternary = collate_level::quaternary;
static constexpr auto identical = collate_level::identical;
};
/// \brief Collation facet.
///
/// It reimplements standard C++ std::collate,
/// allowing usage of std::locale for direct string comparison
template<typename CharType>
class collator : public std::collate<CharType> {
public:
/// Type of the underlying character
typedef CharType char_type;
/// Type of string used with this facet
typedef std::basic_string<CharType> string_type;
/// Compare two strings in rage [b1,e1), [b2,e2) according using a collation level \a level. Calls do_compare
///
/// Returns -1 if the first of the two strings sorts before the seconds, returns 1 if sorts after and 0 if
/// they considered equal.
int compare(collate_level level,
const char_type* b1,
const char_type* e1,
const char_type* b2,
const char_type* e2) const
{
return do_compare(level, b1, e1, b2, e2);
}
/// Create a binary string that can be compared to other in order to get collation order. The string is created
/// for text in range [b,e). It is useful for collation of multiple strings for text.
///
/// The transformation follows these rules:
/// \code
/// compare(level,b1,e1,b2,e2) == sign( transform(level,b1,e1).compare(transform(level,b2,e2)) );
/// \endcode
///
/// Calls do_transform
string_type transform(collate_level level, const char_type* b, const char_type* e) const
{
return do_transform(level, b, e);
}
/// Calculate a hash of a text in range [b,e). The value can be used for collation sensitive string comparison.
///
/// If compare(level,b1,e1,b2,e2) == 0 then hash(level,b1,e1) == hash(level,b2,e2)
///
/// Calls do_hash
long hash(collate_level level, const char_type* b, const char_type* e) const { return do_hash(level, b, e); }
/// Compare two strings \a l and \a r using collation level \a level
///
/// Returns -1 if the first of the two strings sorts before the seconds, returns 1 if sorts after and 0 if
/// they considered equal.
int compare(collate_level level, const string_type& l, const string_type& r) const
{
return do_compare(level, l.data(), l.data() + l.size(), r.data(), r.data() + r.size());
}
/// Calculate a hash that can be used for collation sensitive string comparison of a string \a s
///
/// If compare(level,s1,s2) == 0 then hash(level,s1) == hash(level,s2)
long hash(collate_level level, const string_type& s) const
{
return do_hash(level, s.data(), s.data() + s.size());
}
/// Create a binary string from string \a s, that can be compared to other, useful for collation of multiple
/// strings.
///
/// The transformation follows these rules:
/// \code
/// compare(level,s1,s2) == sign( transform(level,s1).compare(transform(level,s2)) );
/// \endcode
string_type transform(collate_level level, const string_type& s) const
{
return do_transform(level, s.data(), s.data() + s.size());
}
protected:
/// constructor of the collator object
collator(size_t refs = 0) : std::collate<CharType>(refs) {}
/// This function is used to override default collation function that does not take in account collation level.
/// Uses primary level
int
do_compare(const char_type* b1, const char_type* e1, const char_type* b2, const char_type* e2) const override
{
return do_compare(collate_level::identical, b1, e1, b2, e2);
}
/// This function is used to override default collation function that does not take in account collation level.
/// Uses primary level
string_type do_transform(const char_type* b, const char_type* e) const override
{
return do_transform(collate_level::identical, b, e);
}
/// This function is used to override default collation function that does not take in account collation level.
/// Uses primary level
long do_hash(const char_type* b, const char_type* e) const override
{
return do_hash(collate_level::identical, b, e);
}
/// Actual function that performs comparison between the strings. For details see compare member function. Can
/// be overridden.
virtual int do_compare(collate_level level,
const char_type* b1,
const char_type* e1,
const char_type* b2,
const char_type* e2) const = 0;
/// Actual function that performs transformation. For details see transform member function. Can be overridden.
virtual string_type do_transform(collate_level level, const char_type* b, const char_type* e) const = 0;
/// Actual function that calculates hash. For details see hash member function. Can be overridden.
virtual long do_hash(collate_level level, const char_type* b, const char_type* e) const = 0;
};
/// \brief This class can be used in STL algorithms and containers for comparison of strings
/// with a level other than primary
///
/// For example:
///
/// \code
/// std::map<std::string,std::string,comparator<char,collate_level::secondary> > data;
/// \endcode
///
/// Would create a map the keys of which are sorted using secondary collation level
template<typename CharType, collate_level default_level = collate_level::identical>
struct comparator {
public:
/// Create a comparator class for locale \a l and with collation leval \a level
///
/// \throws std::bad_cast: \a l does not have \ref collator facet installed
comparator(const std::locale& l = std::locale(), collate_level level = default_level) :
locale_(l), level_(level)
{}
/// Compare two strings -- equivalent to return left < right according to collation rules
bool operator()(const std::basic_string<CharType>& left, const std::basic_string<CharType>& right) const
{
return std::use_facet<collator<CharType>>(locale_).compare(level_, left, right) < 0;
}
private:
std::locale locale_;
collate_level level_;
};
///@}
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
///
/// \example collate.cpp
/// Example of using collation functions
///
#endif
+99
View File
@@ -0,0 +1,99 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_CONFIG_HPP_INCLUDED
#define BOOST_LOCALE_CONFIG_HPP_INCLUDED
#include <boost/config.hpp>
#include <boost/config/workaround.hpp>
#ifdef __has_include
# if __has_include(<version>)
# include <version>
# endif
#endif
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_LOCALE_DYN_LINK)
# ifdef BOOST_LOCALE_SOURCE
# define BOOST_LOCALE_DECL BOOST_SYMBOL_EXPORT
# else
# define BOOST_LOCALE_DECL BOOST_SYMBOL_IMPORT
# endif // BOOST_LOCALE_SOURCE
#else
# define BOOST_LOCALE_DECL
#endif // BOOST_LOCALE_DYN_LINK
//
// Automatically link to the correct build variant where possible.
//
#if !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_LOCALE_NO_LIB) && !defined(BOOST_LOCALE_SOURCE)
//
// 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_locale
//
// If we're importing code from a dll, then tell auto_link.hpp about it:
//
# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_LOCALE_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
// Check for some C++11 features to provide easier checks for what is missing
// shortly after the requirement of C++11 in Boost 1.81
// clang-format off
#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || \
defined(BOOST_NO_CXX11_DEFAULTED_MOVES) || \
defined(BOOST_NO_CXX11_HDR_FUNCTIONAL) || \
defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) || \
defined(BOOST_NO_CXX11_NOEXCEPT) || \
defined(BOOST_NO_CXX11_OVERRIDE) || \
defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \
defined(BOOST_NO_CXX11_SMART_PTR) || \
defined(BOOST_NO_CXX11_STATIC_ASSERT)
// clang-format on
# error "Boost.Locale requires C++11 since Boost 1.81."
#endif
#ifdef _MSC_VER
// Denote a constant condition, e.g. for if(sizeof(...
# define BOOST_LOCALE_START_CONST_CONDITION __pragma(warning(push)) __pragma(warning(disable : 4127))
# define BOOST_LOCALE_END_CONST_CONDITION __pragma(warning(pop))
#else
# define BOOST_LOCALE_START_CONST_CONDITION
# define BOOST_LOCALE_END_CONST_CONDITION
#endif
/// \cond INTERNAL
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
// Internal define to check if we have access to the Win32 API
# define BOOST_LOCALE_USE_WIN32_API 1
#else
# define BOOST_LOCALE_USE_WIN32_API 0
#endif
// To be used to suppress false positives of UBSAN
#if defined(__clang__) && defined(__has_attribute)
# if __has_attribute(no_sanitize)
# define BOOST_LOCALE_NO_SANITIZE(what) __attribute__((no_sanitize(what)))
# endif
#endif
#if !defined(BOOST_LOCALE_NO_SANITIZE)
# define BOOST_LOCALE_NO_SANITIZE(what)
#endif
#if !defined(__cpp_lib_char8_t) || BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 150000)
// No std::basic_string<char8_t> or bug in Clang: https://github.com/llvm/llvm-project/issues/55560
# define BOOST_LOCALE_NO_CXX20_STRING8
#endif
/// \endcond
#endif // boost/locale/config.hpp
+249
View File
@@ -0,0 +1,249 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_CONVERTER_HPP_INCLUDED
#define BOOST_LOCALE_CONVERTER_HPP_INCLUDED
#include <boost/locale/detail/facet_id.hpp>
#include <boost/locale/detail/is_supported_char.hpp>
#include <boost/locale/util/string.hpp>
#include <locale>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \defgroup convert Text Conversions
///
/// This module provides various function for string manipulation like Unicode normalization, case conversion etc.
/// @{
/// \brief This class provides base flags for text manipulation. It is used as base for converter facet.
class converter_base {
public:
/// The flag used for facet - the type of operation to perform
enum conversion_type {
normalization, ///< Apply Unicode normalization on the text
upper_case, ///< Convert text to upper case
lower_case, ///< Convert text to lower case
case_folding, ///< Fold case in the text
title_case ///< Convert text to title case
};
};
/// \brief The facet that implements text manipulation
///
/// It is used to perform text conversion operations defined by \ref converter_base::conversion_type.
/// It is implemented for supported character types, at least \c char, \c wchar_t
template<typename Char>
class BOOST_SYMBOL_VISIBLE converter : public converter_base,
public std::locale::facet,
public detail::facet_id<converter<Char>> {
BOOST_LOCALE_ASSERT_IS_SUPPORTED(Char);
public:
/// Standard constructor
converter(size_t refs = 0) : std::locale::facet(refs) {}
/// Convert text in range [\a begin, \a end) according to conversion method \a how. Parameter
/// \a flags is used for specification of normalization method like nfd, nfc etc.
virtual std::basic_string<Char>
convert(conversion_type how, const Char* begin, const Char* end, int flags = 0) const = 0;
};
/// The type that defined <a href="http://unicode.org/reports/tr15/#Norm_Forms">normalization form</a>
enum norm_type {
norm_nfd, ///< Canonical decomposition
norm_nfc, ///< Canonical decomposition followed by canonical composition
norm_nfkd, ///< Compatibility decomposition
norm_nfkc, ///< Compatibility decomposition followed by canonical composition.
norm_default = norm_nfc, ///< Default normalization - canonical decomposition followed by canonical composition
};
/// Normalize Unicode string in range [begin,end) according to \ref norm_type "normalization form" \a n
///
/// Note: This function receives only Unicode strings, i.e.: UTF-8, UTF-16 or UTF-32. It does not take
/// in account the locale encoding, because Unicode decomposition and composition are meaningless outside
/// of a Unicode character set.
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> normalize(const CharType* begin,
const CharType* end,
norm_type n = norm_default,
const std::locale& loc = std::locale())
{
return std::use_facet<converter<CharType>>(loc).convert(converter_base::normalization, begin, end, n);
}
/// Normalize Unicode string \a str according to \ref norm_type "normalization form" \a n
///
/// Note: This function receives only Unicode strings, i.e.: UTF-8, UTF-16 or UTF-32. It does not take
/// in account the locale encoding, because Unicode decomposition and composition are meaningless outside
/// of a Unicode character set.
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> normalize(const std::basic_string<CharType>& str,
norm_type n = norm_default,
const std::locale& loc = std::locale())
{
return normalize(str.data(), str.data() + str.size(), n, loc);
}
/// Normalize NULL terminated Unicode string \a str according to \ref norm_type "normalization form" \a n
///
/// Note: This function receives only Unicode strings, i.e.: UTF-8, UTF-16 or UTF-32. It does not take
/// in account the locale encoding, because Unicode decomposition and composition are meaningless outside
/// of a Unicode character set.
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType>
normalize(const CharType* str, norm_type n = norm_default, const std::locale& loc = std::locale())
{
return normalize(str, util::str_end(str), n, loc);
}
///////////////////////////////////////////////////
/// Convert a string in range [begin,end) to upper case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType>
to_upper(const CharType* begin, const CharType* end, const std::locale& loc = std::locale())
{
return std::use_facet<converter<CharType>>(loc).convert(converter_base::upper_case, begin, end);
}
/// Convert a string \a str to upper case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> to_upper(const std::basic_string<CharType>& str, const std::locale& loc = std::locale())
{
return to_upper(str.data(), str.data() + str.size(), loc);
}
/// Convert a NULL terminated string \a str to upper case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> to_upper(const CharType* str, const std::locale& loc = std::locale())
{
return to_upper(str, util::str_end(str), loc);
}
///////////////////////////////////////////////////
/// Convert a string in range [begin,end) to lower case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType>
to_lower(const CharType* begin, const CharType* end, const std::locale& loc = std::locale())
{
return std::use_facet<converter<CharType>>(loc).convert(converter_base::lower_case, begin, end);
}
/// Convert a string \a str to lower case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> to_lower(const std::basic_string<CharType>& str, const std::locale& loc = std::locale())
{
return to_lower(str.data(), str.data() + str.size(), loc);
}
/// Convert a NULL terminated string \a str to lower case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> to_lower(const CharType* str, const std::locale& loc = std::locale())
{
return to_lower(str, util::str_end(str), loc);
}
///////////////////////////////////////////////////
/// Convert a string in range [begin,end) to title case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType>
to_title(const CharType* begin, const CharType* end, const std::locale& loc = std::locale())
{
return std::use_facet<converter<CharType>>(loc).convert(converter_base::title_case, begin, end);
}
/// Convert a string \a str to title case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> to_title(const std::basic_string<CharType>& str, const std::locale& loc = std::locale())
{
return to_title(str.data(), str.data() + str.size(), loc);
}
/// Convert a NULL terminated string \a str to title case according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> to_title(const CharType* str, const std::locale& loc = std::locale())
{
return to_title(str, util::str_end(str), loc);
}
///////////////////////////////////////////////////
/// Fold case of a string in range [begin,end) according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType>
fold_case(const CharType* begin, const CharType* end, const std::locale& loc = std::locale())
{
return std::use_facet<converter<CharType>>(loc).convert(converter_base::case_folding, begin, end);
}
/// Fold case of a string \a str according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> fold_case(const std::basic_string<CharType>& str,
const std::locale& loc = std::locale())
{
return fold_case(str.data(), str.data() + str.size(), loc);
}
/// Fold case of a NULL terminated string \a str according to locale \a loc
///
/// \throws std::bad_cast: \a loc does not have \ref converter facet installed
template<typename CharType>
std::basic_string<CharType> fold_case(const CharType* str, const std::locale& loc = std::locale())
{
return fold_case(str, util::str_end(str), loc);
}
///@}
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
/// \example conversions.cpp
///
/// Example of using various text conversion functions.
///
/// \example wconversions.cpp
///
/// Example of using various text conversion functions with wide strings.
#endif
+1004
View File
File diff suppressed because it is too large Load Diff
+185
View File
@@ -0,0 +1,185 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_DATE_TIME_FACET_HPP_INCLUDED
#define BOOST_LOCALE_DATE_TIME_FACET_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <boost/locale/detail/facet_id.hpp>
#include <cstdint>
#include <locale>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \brief Namespace that contains various types for manipulation with dates
namespace period {
/// \brief This namespace holds a enum of various period types like era, year, month, etc..
namespace marks {
/// \brief the type that defines a flag that holds a period identifier
enum period_mark {
invalid, ///< Special invalid value, should not be used directly
era, ///< Era i.e. AC, BC in Gregorian and Julian calendar, range [0,1]
year, ///< Year, it is calendar specific, for example 2011 in Gregorian calendar.
extended_year, ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == -1.
month, ///< The month of year, calendar specific, in Gregorian [0..11]
day, ///< The day of month, calendar specific, in Gregorian [1..31]
day_of_year, ///< The number of day in year, starting from 1, in Gregorian [1..366]
day_of_week, ///< Day of week, Sunday=1, Monday=2,..., Saturday=7.
///< Note that updating this value respects local day of week, so for example,
///< If first day of week is Monday and the current day is Tuesday then setting
///< the value to Sunday (1) would forward the date by 5 days forward and not backward
///< by two days as it could be expected if the numbers were taken as is.
day_of_week_in_month, ///< Original number of the day of the week in month. For example 1st Sunday,
///< 2nd Sunday, etc. in Gregorian [1..5]
day_of_week_local, ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, [1..7]
hour, ///< 24 clock hour [0..23]
hour_12, ///< 12 clock hour [0..11]
am_pm, ///< am or pm marker [0..1]
minute, ///< minute [0..59]
second, ///< second [0..59]
week_of_year, ///< The week number in the year
week_of_month, ///< The week number within current month
first_day_of_week, ///< First day of week, constant, for example Sunday in US = 1, Monday in France = 2
};
} // namespace marks
/// \brief This class holds a type that represents certain period of time like
/// year, hour, second and so on.
///
/// It can be created from either marks::period_mark type or by using shortcuts in period
/// namespace - calling functions like period::year(), period::hour() and so on.
///
/// Basically it represents the same object as enum marks::period_mark but allows to
/// provide save operator overloading that would not collide with casing of enum to
/// numeric values.
class period_type {
public:
/// Create a period of specific type, default is invalid.
period_type(marks::period_mark m = marks::invalid) : mark_(m) {}
/// Get the value of marks::period_mark it was created with.
marks::period_mark mark() const { return mark_; }
/// Check if two periods are the same
bool operator==(const period_type& other) const { return mark() == other.mark(); }
/// Check if two periods are different
bool operator!=(const period_type& other) const { return mark() != other.mark(); }
private:
marks::period_mark mark_;
};
} // namespace period
/// Structure that define POSIX time, seconds and milliseconds
/// since Jan 1, 1970, 00:00 not including leap seconds.
struct posix_time {
int64_t seconds; ///< Seconds since epoch
uint32_t nanoseconds; ///< Nanoseconds resolution
};
/// This class defines generic calendar class, it is used by date_time and calendar
/// objects internally. It is less useful for end users, but it is build for localization
/// backend implementation
class BOOST_SYMBOL_VISIBLE abstract_calendar {
public:
/// Type that defines how to fetch the value
enum value_type {
absolute_minimum, ///< Absolute possible minimum for the value, for example for day is 1
actual_minimum, ///< Actual minimal value for this period.
greatest_minimum, ///< Maximal minimum value that can be for this period
current, ///< Current value of this period
least_maximum, ///< The last maximal value for this period, For example for Gregorian calendar
///< day it is 28
actual_maximum, ///< Actual maximum, for it can be 28, 29, 30, 31 for day according to current month
absolute_maximum, ///< Maximal value, for Gregorian day it would be 31.
};
/// A way to update the value
enum update_type {
move, ///< Change the value up or down effecting others for example 1990-12-31 + 1 day = 1991-01-01
roll, ///< Change the value up or down not effecting others for example 1990-12-31 + 1 day = 1990-12-01
};
/// Information about calendar
enum calendar_option_type {
is_gregorian, ///< Check if the calendar is Gregorian
is_dst ///< Check if the current time is in daylight time savings
};
/// Make a polymorphic copy of the calendar
virtual abstract_calendar* clone() const = 0;
/// Set specific \a value for period \a p, note not all values are settable.
///
/// After calling set_value you may want to call normalize() function to make sure
/// all periods are updated, if you set several fields that are part of a single
/// date/time representation you should call set_value several times and then
/// call normalize().
///
/// If normalize() is not called after set_value, the behavior is undefined
virtual void set_value(period::marks::period_mark m, int value) = 0;
/// Recalculate all periods after setting them, should be called after use of set_value() function.
virtual void normalize() = 0;
/// Get specific value for period \a p according to a value_type \a v
virtual int get_value(period::marks::period_mark m, value_type v) const = 0;
/// Set current time point
virtual void set_time(const posix_time& p) = 0;
/// Get current time point
virtual posix_time get_time() const = 0;
/// Get current time since epoch in milliseconds
virtual double get_time_ms() const = 0;
/// Set option for calendar, for future use
virtual void set_option(calendar_option_type opt, int v) = 0;
/// Get option for calendar, currently only check if it is Gregorian calendar
virtual int get_option(calendar_option_type opt) const = 0;
/// Adjust period's \a p value by \a difference items using a update_type \a u.
/// Note: not all values are adjustable
virtual void adjust_value(period::marks::period_mark m, update_type u, int difference) = 0;
/// Calculate the difference between this calendar and \a other in \a p units
virtual int difference(const abstract_calendar& other, period::marks::period_mark m) const = 0;
/// Set time zone, empty - use system
virtual void set_timezone(const std::string& tz) = 0;
/// Get current time zone, empty - system one
virtual std::string get_timezone() const = 0;
/// Check of two calendars have same rules
virtual bool same(const abstract_calendar* other) const = 0;
virtual ~abstract_calendar() = default;
};
/// \brief the facet that generates calendar for specific locale
class BOOST_SYMBOL_VISIBLE calendar_facet : public std::locale::facet, public detail::facet_id<calendar_facet> {
public:
/// Basic constructor
calendar_facet(size_t refs = 0) : std::locale::facet(refs) {}
/// Create a new calendar that points to current point of time.
virtual abstract_calendar* create_calendar() const = 0;
};
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+58
View File
@@ -0,0 +1,58 @@
//
// Copyright (c) 2022-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_DETAIL_ENCODING_HPP_INCLUDED
#define BOOST_LOCALE_DETAIL_ENCODING_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <boost/locale/encoding_errors.hpp>
#include <boost/utility/string_view.hpp>
#include <memory>
#include <string>
/// \cond INTERNAL
namespace boost { namespace locale { namespace conv { namespace detail {
template<typename CharIn, typename CharOut>
class BOOST_SYMBOL_VISIBLE charset_converter {
public:
using char_out_type = CharOut;
using char_in_type = CharIn;
using string_type = std::basic_string<CharOut>;
virtual ~charset_converter() = default;
virtual string_type convert(const CharIn* begin, const CharIn* end) = 0;
string_type convert(const boost::basic_string_view<CharIn>& text)
{
return convert(text.data(), text.data() + text.length());
}
};
using narrow_converter = charset_converter<char, char>;
template<typename CharType>
using utf_encoder = charset_converter<char, CharType>;
template<typename CharType>
using utf_decoder = charset_converter<CharType, char>;
enum class conv_backend { Default, IConv, ICU, WinAPI };
template<typename Char>
BOOST_LOCALE_DECL std::unique_ptr<utf_encoder<Char>>
make_utf_encoder(const std::string& charset, method_type how, conv_backend impl = conv_backend::Default);
template<typename Char>
BOOST_LOCALE_DECL std::unique_ptr<utf_decoder<Char>>
make_utf_decoder(const std::string& charset, method_type how, conv_backend impl = conv_backend::Default);
BOOST_LOCALE_DECL std::unique_ptr<narrow_converter>
make_narrow_converter(const std::string& src_encoding,
const std::string& target_encoding,
method_type how,
conv_backend impl = conv_backend::Default);
}}}} // namespace boost::locale::conv::detail
/// \endcond
#endif
+35
View File
@@ -0,0 +1,35 @@
//
// Copyright (c) 2022-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_DETAIL_FACET_ID_HPP_INCLUDED
#define BOOST_LOCALE_DETAIL_FACET_ID_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <locale>
/// \cond INTERNAL
namespace boost { namespace locale { namespace detail {
#if BOOST_CLANG_VERSION >= 40900
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wundefined-var-template"
#endif
/// CRTP base class to hold the id required for facets
///
/// Required because the id needs to be defined in a CPP file and hence ex/imported for shared libraries.
/// However the virtual classes need to be declared as BOOST_VISIBLE to combine the VTables because otherwise
/// casts/virtual-calls might be flagged as invalid by UBSAN
template<class Derived>
struct BOOST_LOCALE_DECL facet_id {
static std::locale::id id;
};
#if BOOST_CLANG_VERSION >= 40900
# pragma clang diagnostic pop
#endif
}}} // namespace boost::locale::detail
/// \endcond
#endif
+48
View File
@@ -0,0 +1,48 @@
//
// Copyright (c) 2022-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_DETAIL_IS_SUPPORTED_CHAR_HPP_INCLUDED
#define BOOST_LOCALE_DETAIL_IS_SUPPORTED_CHAR_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <type_traits>
/// \cond INTERNAL
namespace boost { namespace locale { namespace detail {
/// Trait, returns true iff the argument is a supported character type
template<typename Char>
struct is_supported_char : std::false_type {};
template<>
struct is_supported_char<char> : std::true_type {};
template<>
struct is_supported_char<wchar_t> : std::true_type {};
#ifdef __cpp_char8_t
template<>
struct is_supported_char<char8_t> : std::true_type {};
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
template<>
struct is_supported_char<char16_t> : std::true_type {};
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
template<>
struct is_supported_char<char32_t> : std::true_type {};
#endif
template<typename Char>
using enable_if_is_supported_char = typename std::enable_if<is_supported_char<Char>::value>::type;
}}} // namespace boost::locale::detail
#define BOOST_LOCALE_ASSERT_IS_SUPPORTED(Char) \
static_assert(boost::locale::detail::is_supported_char<Char>::value, "Unsupported Char type")
/// \endcond
#endif
+314
View File
@@ -0,0 +1,314 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_ENCODING_HPP_INCLUDED
#define BOOST_LOCALE_ENCODING_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <boost/locale/detail/encoding.hpp>
#include <boost/locale/encoding_errors.hpp>
#include <boost/locale/encoding_utf.hpp>
#include <boost/locale/info.hpp>
#include <boost/locale/util/string.hpp>
#include <memory>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \brief Namespace that contains all functions related to character set conversion
namespace conv {
/// \defgroup Charset conversion functions
///
/// @{
/// convert text in range [begin,end) encoded with \a charset to UTF according to policy \a how
///
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
BOOST_LOCALE_DECL std::basic_string<CharType>
to_utf(const char* begin, const char* end, const std::string& charset, method_type how = default_method);
/// convert UTF text in range [begin,end) to text encoded with \a charset according to policy \a how
///
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
BOOST_LOCALE_DECL std::string from_utf(const CharType* begin,
const CharType* end,
const std::string& charset,
method_type how = default_method);
/// convert \a text encoded with \a charset to UTF according to policy \a how
///
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::basic_string<CharType>
to_utf(const std::string& text, const std::string& charset, method_type how = default_method)
{
return to_utf<CharType>(text.c_str(), text.c_str() + text.size(), charset, how);
}
/// Convert \a text encoded with \a charset to UTF according to policy \a how
///
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::basic_string<CharType>
to_utf(const char* text, const std::string& charset, method_type how = default_method)
{
return to_utf<CharType>(text, util::str_end(text), charset, how);
}
/// convert text in range [begin,end) in locale encoding given by \a loc to UTF according to
/// policy \a how
///
/// \throws std::bad_cast: \a loc does not have \ref info facet installed
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::basic_string<CharType>
to_utf(const char* begin, const char* end, const std::locale& loc, method_type how = default_method)
{
return to_utf<CharType>(begin, end, std::use_facet<info>(loc).encoding(), how);
}
/// Convert \a text in locale encoding given by \a loc to UTF according to policy \a how
///
/// \throws std::bad_cast: \a loc does not have \ref info facet installed
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::basic_string<CharType>
to_utf(const std::string& text, const std::locale& loc, method_type how = default_method)
{
return to_utf<CharType>(text, std::use_facet<info>(loc).encoding(), how);
}
/// Convert \a text in locale encoding given by \a loc to UTF according to policy \a how
///
/// \throws std::bad_cast: \a loc does not have \ref info facet installed
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::basic_string<CharType> to_utf(const char* text, const std::locale& loc, method_type how = default_method)
{
return to_utf<CharType>(text, std::use_facet<info>(loc).encoding(), how);
}
/// convert \a text from UTF to text encoded with \a charset according to policy \a how
///
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::string
from_utf(const std::basic_string<CharType>& text, const std::string& charset, method_type how = default_method)
{
return from_utf(text.c_str(), text.c_str() + text.size(), charset, how);
}
/// Convert \a text from UTF to \a charset according to policy \a how
///
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::string from_utf(const CharType* text, const std::string& charset, method_type how = default_method)
{
return from_utf(text, util::str_end(text), charset, how);
}
/// Convert UTF text in range [begin,end) to text in locale encoding given by \a loc according to policy \a how
///
/// \throws std::bad_cast: \a loc does not have \ref info facet installed
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::string
from_utf(const CharType* begin, const CharType* end, const std::locale& loc, method_type how = default_method)
{
return from_utf(begin, end, std::use_facet<info>(loc).encoding(), how);
}
/// Convert \a text from UTF to locale encoding given by \a loc according to policy \a how
///
/// \throws std::bad_cast: \a loc does not have \ref info facet installed
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::string
from_utf(const std::basic_string<CharType>& text, const std::locale& loc, method_type how = default_method)
{
return from_utf(text, std::use_facet<info>(loc).encoding(), how);
}
/// Convert \a text from UTF to locale encoding given by \a loc according to policy \a how
///
/// \throws std::bad_cast: \a loc does not have \ref info facet installed
/// \throws invalid_charset_error: Character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
template<typename CharType>
std::string from_utf(const CharType* text, const std::locale& loc, method_type how = default_method)
{
return from_utf(text, std::use_facet<info>(loc).encoding(), how);
}
/// Convert a text in range [begin,end) to \a to_encoding from \a from_encoding according to
/// policy \a how
///
/// \throws invalid_charset_error: Either character set is not supported
/// \throws conversion_error: when the conversion fails (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
BOOST_LOCALE_DECL
std::string between(const char* begin,
const char* end,
const std::string& to_encoding,
const std::string& from_encoding,
method_type how = default_method);
/// Convert \a text to \a to_encoding from \a from_encoding according to
/// policy \a how
///
/// \throws invalid_charset_error: Either character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
inline std::string between(const char* text,
const std::string& to_encoding,
const std::string& from_encoding,
method_type how = default_method)
{
return between(text, util::str_end(text), to_encoding, from_encoding, how);
}
/// Convert \a text to \a to_encoding from \a from_encoding according to
/// policy \a how
///
/// \throws invalid_charset_error: Either character set is not supported
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be
/// encoded or decoded)
inline std::string between(const std::string& text,
const std::string& to_encoding,
const std::string& from_encoding,
method_type how = default_method)
{
return between(text.c_str(), text.c_str() + text.size(), to_encoding, from_encoding, how);
}
/// @}
/// Converter class to decode a narrow string using a local encoding and encode it with UTF
template<typename CharType>
class utf_encoder {
std::unique_ptr<detail::utf_encoder<CharType>> impl_;
public:
using char_type = CharType;
using string_type = std::basic_string<CharType>;
/// Create an instance to convert text encoded with \a charset to UTF according to policy \a how
///
/// Note: When converting only a single text \ref to_utf is likely faster.
/// \throws invalid_charset_error: Character set is not supported
utf_encoder(const std::string& charset, method_type how = default_method) :
impl_(detail::make_utf_encoder<CharType>(charset, how))
{}
/// Convert text in range [begin,end) to UTF
///
/// \throws conversion_error: Conversion failed
string_type convert(const char* begin, const char* end) const { return impl_->convert(begin, end); }
/// Convert \a text to UTF
///
/// \throws conversion_error: Conversion failed
string_type convert(const boost::string_view& text) const { return impl_->convert(text); }
/// Convert \a text to UTF
///
/// \throws conversion_error: Conversion failed
string_type operator()(const boost::string_view& text) const { return convert(text); }
};
/// Converter class to decode an UTF string and encode it using a local encoding
template<typename CharType>
class utf_decoder {
std::unique_ptr<detail::utf_decoder<CharType>> impl_;
public:
using char_type = CharType;
using stringview_type = boost::basic_string_view<CharType>;
/// Create an instance to convert UTF text to text encoded with \a charset according to policy \a how
///
/// Note: When converting only a single text \ref from_utf is likely faster.
/// \throws invalid_charset_error: Character set is not supported
utf_decoder(const std::string& charset, method_type how = default_method) :
impl_(detail::make_utf_decoder<CharType>(charset, how))
{}
/// Convert UTF text in range [begin,end) to local encoding
///
/// \throws conversion_error: Conversion failed
std::string convert(const CharType* begin, const CharType* end) const { return impl_->convert(begin, end); }
/// Convert \a text from UTF to local encoding
///
/// \throws conversion_error: Conversion failed
std::string convert(const stringview_type& text) const { return impl_->convert(text); }
/// Convert \a text from UTF to local encoding
///
/// \throws conversion_error: Conversion failed
std::string operator()(const stringview_type& text) const { return convert(text); }
};
class narrow_converter {
std::unique_ptr<detail::narrow_converter> impl_;
public:
/// Create converter to convert text from \a src_encoding to \a target_encoding according to policy \a how
///
/// \throws invalid_charset_error: Either character set is not supported
narrow_converter(const std::string& src_encoding,
const std::string& target_encoding,
method_type how = default_method) :
impl_(detail::make_narrow_converter(src_encoding, target_encoding, how))
{}
/// Convert text in range [begin,end)
///
/// \throws conversion_error: Conversion failed
std::string convert(const char* begin, const char* end) const { return impl_->convert(begin, end); }
/// Convert \a text
///
/// \throws conversion_error: Conversion failed
std::string convert(const boost::string_view& text) const { return impl_->convert(text); }
/// Convert \a text
///
/// \throws conversion_error: Conversion failed
std::string operator()(const boost::string_view& text) const { return convert(text); }
};
} // namespace conv
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+55
View File
@@ -0,0 +1,55 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_ENCODING_ERRORS_HPP_INCLUDED
#define BOOST_LOCALE_ENCODING_ERRORS_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <stdexcept>
#include <string>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale { namespace conv {
/// \addtogroup codepage
///
/// @{
/// \brief The exception that is thrown in case of conversion error
class BOOST_SYMBOL_VISIBLE conversion_error : public std::runtime_error {
public:
conversion_error() : std::runtime_error("Conversion failed") {}
};
/// \brief This exception is thrown in case of use of unsupported
/// or invalid character set
class BOOST_SYMBOL_VISIBLE invalid_charset_error : public std::runtime_error {
public:
/// Create an error for charset \a charset
invalid_charset_error(const std::string& charset) :
std::runtime_error("Invalid or unsupported charset: " + charset)
{}
};
/// enum that defines conversion policy
enum method_type {
skip = 0, ///< Skip illegal/unconvertible characters
stop = 1, ///< Stop conversion and throw conversion_error
default_method = skip ///< Default method - skip
};
/// @}
}}} // namespace boost::locale::conv
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+71
View File
@@ -0,0 +1,71 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_ENCODING_UTF_HPP_INCLUDED
#define BOOST_LOCALE_ENCODING_UTF_HPP_INCLUDED
#include <boost/locale/encoding_errors.hpp>
#include <boost/locale/utf.hpp>
#include <boost/locale/util/string.hpp>
#include <iterator>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale { namespace conv {
/// \addtogroup codepage
///
/// @{
/// Convert a Unicode text in range [begin,end) to other Unicode encoding
///
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be decoded)
template<typename CharOut, typename CharIn>
std::basic_string<CharOut> utf_to_utf(const CharIn* begin, const CharIn* end, method_type how = default_method)
{
std::basic_string<CharOut> result;
result.reserve(end - begin);
std::back_insert_iterator<std::basic_string<CharOut>> inserter(result);
while(begin != end) {
const utf::code_point c = utf::utf_traits<CharIn>::decode(begin, end);
if(c == utf::illegal || c == utf::incomplete) {
if(how == stop)
throw conversion_error();
} else
utf::utf_traits<CharOut>::encode(c, inserter);
}
return result;
}
/// Convert a Unicode NULL terminated string \a str other Unicode encoding
///
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be decoded)
template<typename CharOut, typename CharIn>
std::basic_string<CharOut> utf_to_utf(const CharIn* str, method_type how = default_method)
{
return utf_to_utf<CharOut, CharIn>(str, util::str_end(str), how);
}
/// Convert a Unicode string \a str other Unicode encoding
///
/// \throws conversion_error: Conversion failed (e.g. \a how is \c stop and any character cannot be decoded)
template<typename CharOut, typename CharIn>
std::basic_string<CharOut> utf_to_utf(const std::basic_string<CharIn>& str, method_type how = default_method)
{
return utf_to_utf<CharOut, CharIn>(str.c_str(), str.c_str() + str.size(), how);
}
/// @}
}}} // namespace boost::locale::conv
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+458
View File
@@ -0,0 +1,458 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2021-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_FORMAT_HPP_INCLUDED
#define BOOST_LOCALE_FORMAT_HPP_INCLUDED
#include <boost/locale/formatting.hpp>
#include <boost/locale/hold_ptr.hpp>
#include <boost/locale/message.hpp>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \defgroup format Format
///
/// This module provides printf like functionality integrated into iostreams and suitable for localization
///
/// @{
/// \cond INTERNAL
namespace detail {
template<typename CharType>
struct formattible {
typedef std::basic_ostream<CharType> stream_type;
typedef void (*writer_type)(stream_type& output, const void* ptr);
formattible() noexcept : pointer_(nullptr), writer_(&formattible::void_write) {}
formattible(const formattible&) noexcept = default;
formattible(formattible&&) noexcept = default;
formattible& operator=(const formattible&) noexcept = default;
formattible& operator=(formattible&&) noexcept = default;
template<typename Type>
explicit formattible(const Type& value) noexcept
{
pointer_ = static_cast<const void*>(&value);
writer_ = &write<Type>;
}
friend stream_type& operator<<(stream_type& out, const formattible& fmt)
{
fmt.writer_(out, fmt.pointer_);
return out;
}
private:
static void void_write(stream_type& output, const void* /*ptr*/)
{
CharType empty_string[1] = {0};
output << empty_string;
}
template<typename Type>
static void write(stream_type& output, const void* ptr)
{
output << *static_cast<const Type*>(ptr);
}
const void* pointer_;
writer_type writer_;
}; // formattible
class BOOST_LOCALE_DECL format_parser {
public:
format_parser(std::ios_base& ios, void*, void (*imbuer)(void*, const std::locale&));
~format_parser();
format_parser(const format_parser&) = delete;
format_parser& operator=(const format_parser&) = delete;
unsigned get_position();
void set_one_flag(const std::string& key, const std::string& value);
template<typename CharType>
void set_flag_with_str(const std::string& key, const std::basic_string<CharType>& value)
{
if(key == "ftime" || key == "strftime") {
as::strftime(ios_);
ios_info::get(ios_).date_time_pattern(value);
}
}
void restore();
private:
void imbue(const std::locale&);
std::ios_base& ios_;
struct data;
hold_ptr<data> d;
};
} // namespace detail
/// \endcond
/// \brief a printf like class that allows type-safe and locale aware message formatting
///
/// This class creates a formatted message similar to printf or boost::format and receives
/// formatted entries via operator %.
///
/// For example
/// \code
/// std::cout << format("Hello {1}, you are {2} years old") % name % age << std::endl;
/// \endcode
///
/// Formatting is enclosed between curly brackets \c { \c } and defined by a comma separated list of flags in the
/// format key[=value] value may also be text included between single quotes \c ' that is used for special purposes
/// where inclusion of non-ASCII text is allowed
///
/// Including of literal \c { and \c } is possible by specifying double brackets \c {{ and \c }} accordingly.
///
///
/// For example:
///
/// \code
/// std::cout << format("The height of water at {1,time} is {2,num=fixed,precision=3}") % time % height;
/// \endcode
///
/// The special key -- a number without a value defines the position of an input parameter.
/// List of keys:
/// - \c [0-9]+ -- digits, the index of a formatted parameter -- mandatory key.
/// - \c num or \c number -- format a number. Optional values are:
/// - \c hex -- display hexadecimal number
/// - \c oct -- display in octal format
/// - \c sci or \c scientific -- display in scientific format
/// - \c fix or \c fixed -- display in fixed format
/// .
/// For example \c number=sci
/// - \c cur or \c currency -- format currency. Optional values are:
///
/// - \c iso -- display using ISO currency symbol.
/// - \c nat or \c national -- display using national currency symbol.
/// .
/// - \c per or \c percent -- format percent value.
/// - \c date, \c time , \c datetime or \c dt -- format date, time or date and time. Optional values are:
/// - \c s or \c short -- display in short format
/// - \c m or \c medium -- display in medium format.
/// - \c l or \c long -- display in long format.
/// - \c f or \c full -- display in full format.
/// .
/// - \c ftime with string (quoted) parameter -- display as with \c strftime see, \c as::ftime manipulator
/// - \c spell or \c spellout -- spell the number.
/// - \c ord or \c ordinal -- format ordinal number (1st, 2nd... etc)
/// - \c left or \c < -- align to left.
/// - \c right or \c > -- align to right.
/// - \c width or \c w -- set field width (requires parameter).
/// - \c precision or \c p -- set precision (requires parameter).
/// - \c locale -- with parameter -- switch locale for current operation. This command generates locale
/// with formatting facets giving more fine grained control of formatting. For example:
/// \code
/// std::cout << format("Today {1,date} ({1,date,locale=he_IL.UTF-8@calendar=hebrew,date} Hebrew Date)") % date;
/// \endcode
/// - \c timezone or \c tz -- the name of the timezone to display the time in. For example:\n
/// \code
/// std::cout << format("Time is: Local {1,time}, ({1,time,tz=EET} Eastern European Time)") % date;
/// \endcode
/// - \c local - display the time in local time
/// - \c gmt - display the time in UTC time scale
/// \code
/// std::cout << format("Local time is: {1,time,local}, universal time is {1,time,gmt}") % time;
/// \endcode
///
///
/// Invalid formatting strings are silently ignored.
/// This protects against a translator crashing the program in an unexpected location.
template<typename CharType>
class basic_format {
int throw_if_params_bound() const;
public:
typedef CharType char_type; ///< Underlying character type
typedef basic_message<char_type> message_type; ///< The translation message type
/// \cond INTERNAL
typedef detail::formattible<CharType> formattible_type;
/// \endcond
typedef std::basic_string<CharType> string_type; ///< string type for this type of character
typedef std::basic_ostream<CharType> stream_type; ///< output stream type for this type of character
/// Create a format class for \a format_string
basic_format(const string_type& format_string) : format_(format_string), translate_(false), parameters_count_(0)
{}
/// Create a format class using message \a trans. The message if translated first according
/// to the rules of the target locale and then interpreted as a format string
basic_format(const message_type& trans) : message_(trans), translate_(true), parameters_count_(0) {}
/// Non-copyable
basic_format(const basic_format& other) = delete;
void operator=(const basic_format& other) = delete;
/// Moveable
basic_format(basic_format&& other) :
message_((other.throw_if_params_bound(), std::move(other.message_))), format_(std::move(other.format_)),
translate_(other.translate_), parameters_count_(0)
{}
basic_format& operator=(basic_format&& other)
{
other.throw_if_params_bound();
message_ = std::move(other.message_);
format_ = std::move(other.format_);
translate_ = other.translate_;
parameters_count_ = 0;
ext_params_.clear();
return *this;
}
/// Add new parameter to the format list. The object should be a type
/// with defined expression out << object where \c out is \c std::basic_ostream.
///
/// A reference to the object is stored, so do not store the format object longer
/// than the lifetime of the parameter.
/// It is advisable to directly print the result:
/// \code
/// basic_format<char> fmt("{0}");
/// fmt % (5 + 2); // INVALID: Dangling reference
/// int i = 42;
/// return fmt % i; // INVALID: Dangling reference
/// std::cout << fmt % (5 + 2); // OK, print immediately
/// return (fmt % (5 + 2)).str(); // OK, convert immediately to string
/// \endcode
template<typename Formattible>
basic_format& operator%(const Formattible& object)
{
add(formattible_type(object));
return *this;
}
/// Format a string using a locale \a loc
string_type str(const std::locale& loc = std::locale()) const
{
std::basic_ostringstream<CharType> buffer;
buffer.imbue(loc);
write(buffer);
return buffer.str();
}
/// write a formatted string to output stream \a out using out's locale
void write(stream_type& out) const
{
string_type format;
if(translate_)
format = message_.str(out.getloc(), ios_info::get(out).domain_id());
else
format = format_;
format_output(out, format);
}
private:
class format_guard {
public:
format_guard(detail::format_parser& fmt) : fmt_(fmt), restored_(false) {}
void restore()
{
if(restored_)
return;
fmt_.restore();
restored_ = true;
}
~format_guard()
{
// clang-format off
try { restore(); } catch(...) {}
// clang-format on
}
private:
detail::format_parser& fmt_;
bool restored_;
};
void format_output(stream_type& out, const string_type& sformat) const
{
constexpr char_type obrk = '{';
constexpr char_type cbrk = '}';
constexpr char_type eq = '=';
constexpr char_type comma = ',';
constexpr char_type quote = '\'';
const size_t size = sformat.size();
const CharType* format = sformat.c_str();
for(size_t pos = 0; format[pos];) {
if(format[pos] != obrk) {
if(format[pos] == cbrk && format[pos + 1] == cbrk) {
// Escaped closing brace
out << cbrk;
pos += 2;
} else {
out << format[pos];
pos++;
}
continue;
}
pos++;
if(format[pos] == obrk) {
// Escaped opening brace
out << obrk;
pos++;
continue;
}
detail::format_parser fmt(out, static_cast<void*>(&out), &basic_format::imbue_locale);
format_guard guard(fmt);
while(pos < size) {
std::string key;
std::string svalue;
string_type value;
bool use_svalue = true;
for(char_type c = format[pos]; !(c == 0 || c == comma || c == eq || c == cbrk); c = format[++pos]) {
key += static_cast<char>(c);
}
if(format[pos] == eq) {
pos++;
if(format[pos] == quote) {
pos++;
use_svalue = false;
while(format[pos]) {
if(format[pos] == quote) {
if(format[pos + 1] == quote) {
value += quote;
pos += 2;
} else {
pos++;
break;
}
} else {
value += format[pos];
pos++;
}
}
} else {
char_type c;
while((c = format[pos]) != 0 && c != comma && c != cbrk) {
svalue += static_cast<char>(c);
pos++;
}
}
}
if(use_svalue)
fmt.set_one_flag(key, svalue);
else
fmt.set_flag_with_str(key, value);
if(format[pos] == comma)
pos++;
else {
if(format[pos] == cbrk) {
unsigned position = fmt.get_position();
out << get(position);
pos++;
}
break;
}
}
}
}
void add(const formattible_type& param)
{
if(parameters_count_ >= base_params_)
ext_params_.push_back(param);
else
parameters_[parameters_count_] = param;
parameters_count_++;
}
formattible_type get(unsigned id) const
{
if(id >= parameters_count_)
return formattible_type();
else if(id >= base_params_)
return ext_params_[id - base_params_];
else
return parameters_[id];
}
static void imbue_locale(void* ptr, const std::locale& l) { static_cast<stream_type*>(ptr)->imbue(l); }
static constexpr unsigned base_params_ = 8;
message_type message_;
string_type format_;
bool translate_;
formattible_type parameters_[base_params_];
unsigned parameters_count_;
std::vector<formattible_type> ext_params_;
};
/// Write formatted message to stream.
///
/// This operator actually causes actual text formatting. It uses the locale of \a out stream
template<typename CharType>
std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const basic_format<CharType>& fmt)
{
fmt.write(out);
return out;
}
/// Definition of char based format
typedef basic_format<char> format;
/// Definition of wchar_t based format
typedef basic_format<wchar_t> wformat;
#ifndef BOOST_LOCALE_NO_CXX20_STRING8
/// Definition of char8_t based format
typedef basic_format<char8_t> u8format;
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
/// Definition of char16_t based format
typedef basic_format<char16_t> u16format;
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
/// Definition of char32_t based format
typedef basic_format<char32_t> u32format;
#endif
template<typename CharType>
int basic_format<CharType>::throw_if_params_bound() const
{
if(parameters_count_)
throw std::invalid_argument("Can't move a basic_format with bound parameters");
return 0;
}
/// @}
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
/// \example hello.cpp
///
/// Basic example of using various functions provided by this library
///
/// \example whello.cpp
///
/// Basic example of using various functions with wide strings provided by this library
#endif
+534
View File
@@ -0,0 +1,534 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2022-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_FORMATTING_HPP_INCLUDED
#define BOOST_LOCALE_FORMATTING_HPP_INCLUDED
#include <boost/locale/time_zone.hpp>
#include <boost/assert.hpp>
#include <boost/utility/string_view.hpp>
#include <cstdint>
#include <cstring>
#include <istream>
#include <ostream>
#include <string>
#include <typeinfo>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \brief This namespace holds additional formatting
/// flags that can be set using ios_info.
namespace flags {
/// Formatting flags, each one of them has corresponding manipulation
/// in namespace \a as
enum display_flags_type {
posix = 0,
number = 1,
currency = 2,
percent = 3,
date = 4,
time = 5,
datetime = 6,
strftime = 7,
spellout = 8,
ordinal = 9,
display_flags_mask = 31,
currency_default = 0 << 5,
currency_iso = 1 << 5,
currency_national = 2 << 5,
currency_flags_mask = 3 << 5,
time_default = 0 << 7,
time_short = 1 << 7,
time_medium = 2 << 7,
time_long = 3 << 7,
time_full = 4 << 7,
time_flags_mask = 7 << 7,
date_default = 0 << 10,
date_short = 1 << 10,
date_medium = 2 << 10,
date_long = 3 << 10,
date_full = 4 << 10,
date_flags_mask = 7 << 10,
};
/// Special string patterns that can be used for text formatting
enum pattern_type {
datetime_pattern, ///< strftime like formatting
time_zone_id ///< time zone name
};
/// Special integer values that can be used for formatting
enum value_type {
domain_id ///< Domain code - for message formatting
};
} // namespace flags
/// \brief This class holds external data beyond existing fmtflags that std::ios_base holds
///
/// You should almost never create this object directly. Instead, you should access it via
/// ios_info::get(stream_object) static member function. It automatically creates default formatting data for that
/// stream
class BOOST_LOCALE_DECL ios_info {
public:
/// \cond INTERNAL
ios_info();
ios_info(const ios_info&);
ios_info& operator=(const ios_info&);
~ios_info();
/// \endcond
/// Get ios_info instance for specific stream object
static ios_info& get(std::ios_base& ios);
/// Set flags that define how to format data, e.g. number, spell, currency etc.
void display_flags(uint64_t flags);
/// Get flags that define how to format data, e.g. number, spell, currency etc.
uint64_t display_flags() const;
/// Set flags that define how to format currency
void currency_flags(uint64_t flags);
/// Get flags that define how to format currency
uint64_t currency_flags() const;
/// Set flags that define how to format date
void date_flags(uint64_t flags);
/// Get flags that define how to format date
uint64_t date_flags() const;
/// Set flags that define how to format time
void time_flags(uint64_t flags);
/// Get flags that define how to format time
uint64_t time_flags() const;
/// Set special message domain identification
void domain_id(int);
/// Get special message domain identification
int domain_id() const;
/// Set time zone for formatting dates and time
void time_zone(const std::string&);
/// Get time zone for formatting dates and time
std::string time_zone() const;
/// Set date/time pattern (strftime like)
template<typename CharType>
void date_time_pattern(const std::basic_string<CharType>& str)
{
date_time_pattern_set().set<CharType>(str);
}
/// Get date/time pattern (strftime like)
template<typename CharType>
std::basic_string<CharType> date_time_pattern() const
{
return date_time_pattern_set().get<CharType>();
}
/// \cond INTERNAL
void on_imbue();
/// \endcond
private:
class string_set;
const string_set& date_time_pattern_set() const;
string_set& date_time_pattern_set();
class BOOST_LOCALE_DECL string_set {
public:
string_set();
~string_set();
string_set(const string_set& other);
string_set& operator=(string_set other);
void swap(string_set& other);
template<typename Char>
void set(const boost::basic_string_view<Char> s)
{
BOOST_ASSERT(!s.empty());
delete[] ptr;
ptr = nullptr;
type = &typeid(Char);
size = sizeof(Char) * s.size();
ptr = size ? new char[size] : nullptr;
memcpy(ptr, s.data(), size);
}
template<typename Char>
std::basic_string<Char> get() const
{
if(type == nullptr || *type != typeid(Char))
throw std::bad_cast();
std::basic_string<Char> result(size / sizeof(Char), Char(0));
memcpy(&result.front(), ptr, size);
return result;
}
private:
const std::type_info* type;
size_t size;
char* ptr;
};
uint64_t flags_;
int domain_id_;
std::string time_zone_;
string_set datetime_;
};
/// \brief This namespace includes all manipulators that can be used on IO streams
namespace as {
/// \defgroup manipulators I/O Stream manipulators
///
/// @{
/// Format values with "POSIX" or "C" locale. Note, if locale was created with additional non-classic locale
/// then These numbers may be localized
inline std::ios_base& posix(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::posix);
return ios;
}
/// Format a number. Note, unlike standard number formatting, integers would be treated like real numbers when
/// std::fixed or std::scientific manipulators were applied
inline std::ios_base& number(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::number);
return ios;
}
/// Format currency, number is treated like amount of money
inline std::ios_base& currency(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::currency);
return ios;
}
/// Format percent, value 0.3 is treated as 30%.
inline std::ios_base& percent(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::percent);
return ios;
}
/// Format a date, number is treated as POSIX time
inline std::ios_base& date(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::date);
return ios;
}
/// Format a time, number is treated as POSIX time
inline std::ios_base& time(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::time);
return ios;
}
/// Format a date and time, number is treated as POSIX time
inline std::ios_base& datetime(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::datetime);
return ios;
}
/// Create formatted date time, Please note, this manipulator only changes formatting mode,
/// and not format itself, so you are probably looking for ftime manipulator
inline std::ios_base& strftime(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::strftime);
return ios;
}
/// Spell the number, like "one hundred and ten"
inline std::ios_base& spellout(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::spellout);
return ios;
}
/// Write an order of the number like 4th.
inline std::ios_base& ordinal(std::ios_base& ios)
{
ios_info::get(ios).display_flags(flags::ordinal);
return ios;
}
/// Set default currency formatting style -- national, like "$"
inline std::ios_base& currency_default(std::ios_base& ios)
{
ios_info::get(ios).currency_flags(flags::currency_default);
return ios;
}
/// Set ISO currency formatting style, like "USD", (requires ICU >= 4.2)
inline std::ios_base& currency_iso(std::ios_base& ios)
{
ios_info::get(ios).currency_flags(flags::currency_iso);
return ios;
}
/// Set national currency formatting style, like "$"
inline std::ios_base& currency_national(std::ios_base& ios)
{
ios_info::get(ios).currency_flags(flags::currency_national);
return ios;
}
/// set default (medium) time formatting style
inline std::ios_base& time_default(std::ios_base& ios)
{
ios_info::get(ios).time_flags(flags::time_default);
return ios;
}
/// set short time formatting style
inline std::ios_base& time_short(std::ios_base& ios)
{
ios_info::get(ios).time_flags(flags::time_short);
return ios;
}
/// set medium time formatting style
inline std::ios_base& time_medium(std::ios_base& ios)
{
ios_info::get(ios).time_flags(flags::time_medium);
return ios;
}
/// set long time formatting style
inline std::ios_base& time_long(std::ios_base& ios)
{
ios_info::get(ios).time_flags(flags::time_long);
return ios;
}
/// set full time formatting style
inline std::ios_base& time_full(std::ios_base& ios)
{
ios_info::get(ios).time_flags(flags::time_full);
return ios;
}
/// set default (medium) date formatting style
inline std::ios_base& date_default(std::ios_base& ios)
{
ios_info::get(ios).date_flags(flags::date_default);
return ios;
}
/// set short date formatting style
inline std::ios_base& date_short(std::ios_base& ios)
{
ios_info::get(ios).date_flags(flags::date_short);
return ios;
}
/// set medium date formatting style
inline std::ios_base& date_medium(std::ios_base& ios)
{
ios_info::get(ios).date_flags(flags::date_medium);
return ios;
}
/// set long date formatting style
inline std::ios_base& date_long(std::ios_base& ios)
{
ios_info::get(ios).date_flags(flags::date_long);
return ios;
}
/// set full date formatting style
inline std::ios_base& date_full(std::ios_base& ios)
{
ios_info::get(ios).date_flags(flags::date_full);
return ios;
}
/// \cond INTERNAL
namespace detail {
inline bool is_datetime_display_flags(const uint64_t display_flags)
{
return (display_flags == flags::date || display_flags == flags::time || display_flags == flags::datetime
|| display_flags == flags::strftime);
}
template<typename CharType>
struct add_ftime {
std::basic_string<CharType> ftime;
void apply(std::basic_ios<CharType>& ios) const
{
ios_info::get(ios).date_time_pattern(ftime);
as::strftime(ios);
}
};
template<typename CharType>
std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const add_ftime<CharType>& fmt)
{
fmt.apply(out);
return out;
}
template<typename CharType>
std::basic_istream<CharType>& operator>>(std::basic_istream<CharType>& in, const add_ftime<CharType>& fmt)
{
fmt.apply(in);
return in;
}
} // namespace detail
/// \endcond
/// Set strftime like formatting string
///
/// Please note, formatting flags are very similar but not exactly the same as flags for C function strftime.
/// Differences: some flags as "%e" do not add blanks to fill text up to two spaces, not all flags supported.
///
/// Flags:
/// - "%a" -- Abbreviated weekday (Sun.)
/// - "%A" -- Full weekday (Sunday)
/// - "%b" -- Abbreviated month (Jan.)
/// - "%B" -- Full month (January)
/// - "%c" -- Locale date-time format. **Note:** prefer using "as::datetime"
/// - "%d" -- Day of Month [01,31]
/// - "%e" -- Day of Month [1,31]
/// - "%h" -- Same as "%b"
/// - "%H" -- 24 clock hour [00,23]
/// - "%I" -- 12 clock hour [01,12]
/// - "%j" -- Day of year [1,366]
/// - "%m" -- Month [01,12]
/// - "%M" -- Minute [00,59]
/// - "%n" -- New Line
/// - "%p" -- AM/PM in locale representation
/// - "%r" -- Time with AM/PM, same as "%I:%M:%S %p"
/// - "%R" -- Same as "%H:%M"
/// - "%S" -- Second [00,61]
/// - "%t" -- Tab character
/// - "%T" -- Same as "%H:%M:%S"
/// - "%x" -- Local date representation. **Note:** prefer using "as::date"
/// - "%X" -- Local time representation. **Note:** prefer using "as::time"
/// - "%y" -- Year [00,99]
/// - "%Y" -- 4 digits year. (2009)
/// - "%Z" -- Time Zone
/// - "%%" -- Percent symbol
///
template<typename CharType>
#ifdef BOOST_LOCALE_DOXYGEN
unspecified_type
#else
detail::add_ftime<CharType>
#endif
ftime(const std::basic_string<CharType>& format)
{
detail::add_ftime<CharType> fmt;
fmt.ftime = format;
return fmt;
}
/// See ftime(std::basic_string<CharType> const &format)
template<typename CharType>
#ifdef BOOST_LOCALE_DOXYGEN
unspecified_type
#else
detail::add_ftime<CharType>
#endif
ftime(const CharType* format)
{
detail::add_ftime<CharType> fmt;
fmt.ftime = format;
return fmt;
}
/// \cond INTERNAL
namespace detail {
struct set_timezone {
std::string id;
};
template<typename CharType>
std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const set_timezone& fmt)
{
ios_info::get(out).time_zone(fmt.id);
return out;
}
template<typename CharType>
std::basic_istream<CharType>& operator>>(std::basic_istream<CharType>& in, const set_timezone& fmt)
{
ios_info::get(in).time_zone(fmt.id);
return in;
}
} // namespace detail
/// \endcond
/// Set GMT time zone to stream
inline std::ios_base& gmt(std::ios_base& ios)
{
ios_info::get(ios).time_zone("GMT");
return ios;
}
/// Set local time zone to stream
inline std::ios_base& local_time(std::ios_base& ios)
{
ios_info::get(ios).time_zone(time_zone::global());
return ios;
}
/// Set time zone using \a id
inline
#ifdef BOOST_LOCALE_DOXYGEN
unspecified_type
#else
detail::set_timezone
#endif
time_zone(const char* id)
{
detail::set_timezone tz;
tz.id = id;
return tz;
}
/// Set time zone using \a id
inline
#ifdef BOOST_LOCALE_DOXYGEN
unspecified_type
#else
detail::set_timezone
#endif
time_zone(const std::string& id)
{
detail::set_timezone tz;
tz.id = id;
return tz;
}
/// @}
} // namespace as
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+241
View File
@@ -0,0 +1,241 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_GENERATOR_HPP
#define BOOST_LOCALE_GENERATOR_HPP
#include <boost/locale/hold_ptr.hpp>
#include <cstdint>
#include <locale>
#include <memory>
#include <string>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost {
///
/// \brief This is the main namespace that encloses all localization classes
///
namespace locale {
class localization_backend;
class localization_backend_manager;
/// Type that specifies the character type that locales can be generated for
///
/// Supports bitwise OR and bitwise AND (the latter returning if the type is set)
enum class char_facet_t : uint32_t {
nochar = 0, ///< Unspecified character category for character independent facets
char_f = 1 << 0, ///< 8-bit character facets
wchar_f = 1 << 1, ///< wide character facets
#ifdef __cpp_char8_t
char8_f = 1 << 2, ///< C++20 char8_t facets
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
char16_f = 1 << 3, ///< C++11 char16_t facets
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
char32_f = 1 << 4, ///< C++11 char32_t facets
#endif
};
typedef BOOST_DEPRECATED("Use char_facet_t") char_facet_t character_facet_type;
/// First facet specific for character type
constexpr char_facet_t character_facet_first = char_facet_t::char_f;
/// Last facet specific for character type
constexpr char_facet_t character_facet_last =
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
char_facet_t::char32_f;
#elif defined BOOST_LOCALE_ENABLE_CHAR16_T
char_facet_t::char16_f;
#elif defined __cpp_char8_t
char_facet_t::char8_f;
#else
char_facet_t::wchar_f;
#endif
/// Special mask -- generate all
constexpr char_facet_t all_characters = char_facet_t(0xFFFFFFFFu);
/// Type used for more fine grained generation of facets
///
/// Supports bitwise OR and bitwise AND (the latter returning if the type is set)
enum class category_t : uint32_t {
convert = 1 << 0, ///< Generate conversion facets
collation = 1 << 1, ///< Generate collation facets
formatting = 1 << 2, ///< Generate numbers, currency, date-time formatting facets
parsing = 1 << 3, ///< Generate numbers, currency, date-time formatting facets
message = 1 << 4, ///< Generate message facets
codepage = 1 << 5, ///< Generate character set conversion facets (derived from std::codecvt)
boundary = 1 << 6, ///< Generate boundary analysis facet
calendar = 1 << 16, ///< Generate boundary analysis facet
information = 1 << 17, ///< Generate general locale information facet
};
typedef BOOST_DEPRECATED("Use category_t") category_t locale_category_type;
/// First facet specific for character
constexpr category_t per_character_facet_first = category_t::convert;
/// Last facet specific for character
constexpr category_t per_character_facet_last = category_t::boundary;
/// First character independent facet
constexpr category_t non_character_facet_first = category_t::calendar;
/// Last character independent facet
constexpr category_t non_character_facet_last = category_t::information;
/// First category facet
constexpr category_t category_first = category_t::convert;
/// Last category facet
constexpr category_t category_last = category_t::information;
/// Generate all of them
constexpr category_t all_categories = category_t(0xFFFFFFFFu);
/// \brief the major class used for locale generation
///
/// This class is used for specification of all parameters required for locale generation and
/// caching. This class const member functions are thread safe if locale class implementation is thread safe.
class BOOST_LOCALE_DECL generator {
public:
/// Create new generator using global localization_backend_manager
generator();
/// Create new generator using specific localization_backend_manager
generator(const localization_backend_manager&);
~generator();
/// Set types of facets that should be generated, default all
void categories(category_t cats);
/// Get types of facets that should be generated, default all
category_t categories() const;
/// Set the characters type for which the facets should be generated, default all supported
void characters(char_facet_t chars);
/// Get the characters type for which the facets should be generated, default all supported
char_facet_t characters() const;
/// Add a new domain of messages that would be generated. It should be set in order to enable
/// messages support.
///
/// Messages domain has following format: "name" or "name/encoding"
/// where name is the base name of the "mo" file where the catalog is stored
/// without ".mo" extension. For example for file \c /usr/share/locale/he/LC_MESSAGES/blog.mo
/// it would be \c blog.
///
/// You can optionally specify the encoding of the keys in the sources by adding "/encoding_name"
/// For example blog/cp1255.
///
/// If not defined all keys are assumed to be UTF-8 encoded.
///
/// \note When you select a domain for the program using dgettext or message API, you
/// do not specify the encoding part. So for example if the provided
/// domain name was "blog/windows-1255" then for translation
/// you should use dgettext("blog","Hello")
void add_messages_domain(const std::string& domain);
/// Set default message domain. If this member was not called, the first added messages domain is used.
/// If the domain \a domain is not added yet it is added.
void set_default_messages_domain(const std::string& domain);
/// Remove all added domains from the list
void clear_domains();
/// Add a search path where dictionaries are looked in.
///
/// \note
///
/// - Under the Windows platform the path is treated as a path in the locale's encoding so
/// if you create locale "en_US.windows-1251" then path would be treated as cp1255,
/// and if it is en_US.UTF-8 it is treated as UTF-8. File name is always opened with
/// a wide file name as wide file names are the native file name on Windows.
///
/// - Under POSIX platforms all paths passed as-is regardless of encoding as narrow
/// encodings are the native encodings for POSIX platforms.
///
void add_messages_path(const std::string& path);
/// Remove all added paths
void clear_paths();
/// Remove all cached locales
void clear_cache();
/// Turn locale caching ON
void locale_cache_enabled(bool on);
/// Get locale cache option
bool locale_cache_enabled() const;
/// Check if by default ANSI encoding is selected or UTF-8 onces. The default is false.
bool use_ansi_encoding() const;
/// Select ANSI encodings as default system encoding rather then UTF-8 by default
/// under Windows.
///
/// The default is the most portable and most powerful encoding, UTF-8, but the user
/// can select "system" one if dealing with legacy applications
void use_ansi_encoding(bool enc);
/// Generate a locale with id \a id
std::locale generate(const std::string& id) const;
/// Generate a locale with id \a id. Use \a base as a locale to which all facets are added,
/// instead of std::locale::classic().
std::locale generate(const std::locale& base, const std::string& id) const;
/// Shortcut to generate(id)
std::locale operator()(const std::string& id) const { return generate(id); }
private:
void set_all_options(localization_backend& backend, const std::string& id) const;
generator(const generator&);
void operator=(const generator&);
struct data;
hold_ptr<data> d;
};
constexpr char_facet_t operator|(const char_facet_t lhs, const char_facet_t rhs)
{
return char_facet_t(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
}
constexpr char_facet_t operator^(const char_facet_t lhs, const char_facet_t rhs)
{
return char_facet_t(static_cast<uint32_t>(lhs) ^ static_cast<uint32_t>(rhs));
}
constexpr bool operator&(const char_facet_t lhs, const char_facet_t rhs)
{
return (static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs)) != 0u;
}
// Prefix increment: Return the next value
BOOST_CXX14_CONSTEXPR inline char_facet_t& operator++(char_facet_t& v)
{
return v = char_facet_t(static_cast<uint32_t>(v) ? static_cast<uint32_t>(v) << 1 : 1);
}
constexpr category_t operator|(const category_t lhs, const category_t rhs)
{
return category_t(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
}
constexpr category_t operator^(const category_t lhs, const category_t rhs)
{
return category_t(static_cast<uint32_t>(lhs) ^ static_cast<uint32_t>(rhs));
}
constexpr bool operator&(const category_t lhs, const category_t rhs)
{
return (static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs)) != 0u;
}
// Prefix increment: Return the next value
BOOST_CXX14_CONSTEXPR inline category_t& operator++(category_t& v)
{
return v = category_t(static_cast<uint32_t>(v) << 1);
}
} // namespace locale
} // namespace boost
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+472
View File
@@ -0,0 +1,472 @@
//
// Copyright (c) 2015 Artyom Beilis (Tonkikh)
// Copyright (c) 2021-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_GENERIC_CODECVT_HPP
#define BOOST_LOCALE_GENERIC_CODECVT_HPP
#include <boost/locale/utf.hpp>
#include <cstdint>
#include <locale>
namespace boost { namespace locale {
static_assert(sizeof(std::mbstate_t) >= 2, "std::mbstate_t is to small to store an UTF-16 codepoint");
namespace detail {
// Avoid including cstring for std::memcpy
inline void copy_uint16_t(void* dst, const void* src)
{
unsigned char* cdst = static_cast<unsigned char*>(dst);
const unsigned char* csrc = static_cast<const unsigned char*>(src);
cdst[0] = csrc[0];
cdst[1] = csrc[1];
}
inline uint16_t read_state(const std::mbstate_t& src)
{
uint16_t dst;
copy_uint16_t(&dst, &src);
return dst;
}
inline void write_state(std::mbstate_t& dst, const uint16_t src)
{
copy_uint16_t(&dst, &src);
}
} // namespace detail
/// \brief A base class that used to define constants for generic_codecvt
class generic_codecvt_base {
public:
/// Initial state for converting to or from Unicode code points, used by initial_state in derived classes
enum initial_convertion_state {
to_unicode_state, ///< The state would be used by to_unicode functions
from_unicode_state ///< The state would be used by from_unicode functions
};
};
/// \brief Generic codecvt facet for various stateless encodings to UTF-16 and UTF-32 using wchar_t, char32_t
/// and char16_t
///
/// Implementations should derive from this class defining itself as CodecvtImpl and provide following members
///
/// - `state_type` - a type of special object that allows to store intermediate cached data, for example `iconv_t`
/// descriptor
/// - `state_type initial_state(generic_codecvt_base::initial_convertion_state direction) const` - member function
/// that creates initial state
/// - `int max_encoding_length() const` - a maximal length that one Unicode code point is represented, for UTF-8 for
/// example it is 4 from ISO-8859-1 it is 1
/// - `utf::code_point to_unicode(state_type& state, const char*& begin, const char* end)` - extract first code
/// point from the text in range [begin,end), in case of success begin would point to the next character sequence to
/// be encoded to next code point, in case of incomplete sequence - utf::incomplete shell be returned, and in case
/// of invalid input sequence utf::illegal shell be returned and begin would remain unmodified
/// - `utf::len_or_error from_unicode(state_type &state, utf::code_point u, char* begin, const char* end)` - convert
/// a Unicode code point `u` into a character sequence at [begin,end). Return the length of the sequence in case of
/// success, utf::incomplete in case of not enough room to encode the code point, or utf::illegal in case conversion
/// can not be performed
///
///
/// For example implementation of codecvt for latin1/ISO-8859-1 character set
///
/// \code
///
/// template<typename CharType>
/// class latin1_codecvt: boost::locale::generic_codecvt<CharType,latin1_codecvt<CharType> >
/// {
/// public:
///
/// /* Standard codecvt constructor */
/// latin1_codecvt(size_t refs = 0): boost::locale::generic_codecvt<CharType,latin1_codecvt<CharType> >(refs)
/// {
/// }
///
/// /* State is unused but required by generic_codecvt */
/// struct state_type {};
///
/// state_type initial_state(generic_codecvt_base::initial_convertion_state /*unused*/) const
/// {
/// return state_type();
/// }
///
/// int max_encoding_length() const
/// {
/// return 1;
/// }
///
/// boost::locale::utf::code_point to_unicode(state_type&, const char*& begin, const char* end) const
/// {
/// if(begin == end)
/// return boost::locale::utf::incomplete;
/// return *begin++;
/// }
///
/// boost::locale::utf::len_or_error from_unicode(state_type&, boost::locale::utf::code_point u,
/// char* begin, const char* end) const
/// {
/// if(u >= 256)
/// return boost::locale::utf::illegal;
/// if(begin == end)
/// return boost::locale::utf::incomplete;
/// *begin = u;
/// return 1;
/// }
/// };
///
/// \endcode
///
/// When external tools used for encoding conversion, the `state_type` is useful to save objects used for
/// conversions. For example, icu::UConverter can be saved in such a state for an efficient use:
///
/// \code
/// template<typename CharType>
/// class icu_codecvt: boost::locale::generic_codecvt<CharType,icu_codecvt<CharType>>
/// {
/// public:
///
/// /* Standard codecvt constructor */
/// icu_codecvt(std::string const &name,refs = 0):
/// boost::locale::generic_codecvt<CharType,icu_codecvt<CharType>>(refs)
/// { ... }
///
/// using state_type = std::unique_ptr<UConverter,void (*)(UConverter*)>;
///
/// state_type initial_state(generic_codecvt_base::initial_convertion_state /*unused*/) const
/// {
/// UErrorCode err = U_ZERO_ERROR;
/// return state_type(ucnv_safeClone(converter_,0,0,&err),ucnv_close);
/// }
///
/// boost::locale::utf::code_point to_unicode(state_type &ptr,char const *&begin,char const *end) const
/// {
/// UErrorCode err = U_ZERO_ERROR;
/// boost::locale::utf::code_point cp = ucnv_getNextUChar(ptr.get(),&begin,end,&err);
/// ...
/// }
/// ...
/// };
/// \endcode
///
template<typename CharType, typename CodecvtImpl, int CharSize = sizeof(CharType)>
class generic_codecvt;
/// \brief UTF-16 to/from narrow char codecvt facet to use with char16_t or wchar_t on Windows
///
/// Note in order to fit the requirements of usability by std::wfstream it uses mbstate_t
/// to handle intermediate states in handling of variable length UTF-16 sequences
///
/// Its member functions implement standard virtual functions of basic codecvt
template<typename CharType, typename CodecvtImpl>
class generic_codecvt<CharType, CodecvtImpl, 2> : public std::codecvt<CharType, char, std::mbstate_t>,
public generic_codecvt_base {
public:
typedef CharType uchar;
generic_codecvt(size_t refs = 0) : std::codecvt<CharType, char, std::mbstate_t>(refs) {}
const CodecvtImpl& implementation() const { return *static_cast<const CodecvtImpl*>(this); }
protected:
std::codecvt_base::result do_unshift(std::mbstate_t& s, char* from, char* /*to*/, char*& next) const override
{
if(*reinterpret_cast<char*>(&s) != 0)
return std::codecvt_base::error;
next = from;
return std::codecvt_base::ok;
}
int do_encoding() const noexcept override { return 0; }
int do_max_length() const noexcept override { return implementation().max_encoding_length(); }
bool do_always_noconv() const noexcept override { return false; }
int do_length(std::mbstate_t& std_state, const char* from, const char* from_end, size_t max) const override
{
bool state = *reinterpret_cast<char*>(&std_state) != 0;
const char* save_from = from;
auto cvt_state = implementation().initial_state(to_unicode_state);
while(max > 0 && from < from_end) {
const char* prev_from = from;
const utf::code_point ch = implementation().to_unicode(cvt_state, from, from_end);
if(ch == boost::locale::utf::incomplete || ch == boost::locale::utf::illegal) {
from = prev_from;
break;
}
max--;
if(ch > 0xFFFF) {
if(!state)
from = prev_from;
state = !state;
}
}
*reinterpret_cast<char*>(&std_state) = state;
return static_cast<int>(from - save_from);
}
std::codecvt_base::result do_in(std::mbstate_t& std_state,
const char* from,
const char* from_end,
const char*& from_next,
uchar* to,
uchar* to_end,
uchar*& to_next) const override
{
std::codecvt_base::result r = std::codecvt_base::ok;
// mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT())
// according to standard. We use it to keep a flag 0/1 for surrogate pair writing
//
// if 0/false no codepoint above >0xFFFF observed, else a codepoint above 0xFFFF was observed
// and first pair is written, but no input consumed
bool state = *reinterpret_cast<char*>(&std_state) != 0;
auto cvt_state = implementation().initial_state(to_unicode_state);
while(to < to_end && from < from_end) {
const char* from_saved = from;
utf::code_point ch = implementation().to_unicode(cvt_state, from, from_end);
if(ch == boost::locale::utf::illegal) {
from = from_saved;
r = std::codecvt_base::error;
break;
}
if(ch == boost::locale::utf::incomplete) {
from = from_saved;
r = std::codecvt_base::partial;
break;
}
// Normal codepoints go directly to stream
if(ch <= 0xFFFF)
*to++ = static_cast<uchar>(ch);
else {
// For other codepoints we do the following
//
// 1. We can't consume our input as we may find ourselves
// in state where all input consumed but not all output written,i.e. only
// 1st pair is written
// 2. We only write first pair and mark this in the state, we also revert back
// the from pointer in order to make sure this codepoint would be read
// once again and then we would consume our input together with writing
// second surrogate pair
ch -= 0x10000;
std::uint16_t w1 = static_cast<std::uint16_t>(0xD800 | (ch >> 10));
std::uint16_t w2 = static_cast<std::uint16_t>(0xDC00 | (ch & 0x3FF));
if(!state) {
from = from_saved;
*to++ = w1;
} else
*to++ = w2;
state = !state;
}
}
from_next = from;
to_next = to;
if(r == std::codecvt_base::ok && (from != from_end || state))
r = std::codecvt_base::partial;
*reinterpret_cast<char*>(&std_state) = state;
return r;
}
std::codecvt_base::result do_out(std::mbstate_t& std_state,
const uchar* from,
const uchar* from_end,
const uchar*& from_next,
char* to,
char* to_end,
char*& to_next) const override
{
std::codecvt_base::result r = std::codecvt_base::ok;
// mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT())
// according to standard. We assume that sizeof(mbstate_t) >=2 in order
// to be able to store first observed surrogate pair
//
// State: state!=0 - a first surrogate pair was observed (state = first pair),
// we expect the second one to come and then zero the state
std::uint16_t state = detail::read_state(std_state);
auto cvt_state = implementation().initial_state(from_unicode_state);
while(to < to_end && from < from_end) {
utf::code_point ch = 0;
if(state != 0) {
// if the state indicates that 1st surrogate pair was written
// we should make sure that the second one that comes is actually
// second surrogate
std::uint16_t w1 = state;
std::uint16_t w2 = *from;
// we don't forward from as writing may fail to incomplete or
// partial conversion
if(0xDC00 <= w2 && w2 <= 0xDFFF) {
std::uint16_t vh = w1 - 0xD800;
std::uint16_t vl = w2 - 0xDC00;
ch = ((uint32_t(vh) << 10) | vl) + 0x10000;
} else {
// Invalid surrogate
r = std::codecvt_base::error;
break;
}
} else {
ch = *from;
if(0xD800 <= ch && ch <= 0xDBFF) {
// if this is a first surrogate pair we put
// it into the state and consume it, note we don't
// go forward as it should be illegal so we increase
// the from pointer manually
state = static_cast<uint16_t>(ch);
from++;
continue;
} else if(0xDC00 <= ch && ch <= 0xDFFF) {
// if we observe second surrogate pair and
// first only may be expected we should break from the loop with error
// as it is illegal input
r = std::codecvt_base::error;
break;
}
}
if(!boost::locale::utf::is_valid_codepoint(ch)) {
r = std::codecvt_base::error;
break;
}
const utf::code_point len = implementation().from_unicode(cvt_state, ch, to, to_end);
if(len == boost::locale::utf::incomplete) {
r = std::codecvt_base::partial;
break;
} else if(len == boost::locale::utf::illegal) {
r = std::codecvt_base::error;
break;
} else
to += len;
state = 0;
from++;
}
from_next = from;
to_next = to;
if(r == std::codecvt_base::ok && (from != from_end || state != 0))
r = std::codecvt_base::partial;
detail::write_state(std_state, state);
return r;
}
};
/// \brief UTF-32 to/from narrow char codecvt facet to use with char32_t or wchar_t on POSIX platforms
///
/// Its member functions implement standard virtual functions of basic codecvt.
/// mbstate_t is not used for UTF-32 handling due to fixed length encoding
template<typename CharType, typename CodecvtImpl>
class generic_codecvt<CharType, CodecvtImpl, 4> : public std::codecvt<CharType, char, std::mbstate_t>,
public generic_codecvt_base {
public:
typedef CharType uchar;
generic_codecvt(size_t refs = 0) : std::codecvt<CharType, char, std::mbstate_t>(refs) {}
const CodecvtImpl& implementation() const { return *static_cast<const CodecvtImpl*>(this); }
protected:
std::codecvt_base::result
do_unshift(std::mbstate_t& /*s*/, char* from, char* /*to*/, char*& next) const override
{
next = from;
return std::codecvt_base::ok;
}
int do_encoding() const noexcept override { return 0; }
int do_max_length() const noexcept override { return implementation().max_encoding_length(); }
bool do_always_noconv() const noexcept override { return false; }
int do_length(std::mbstate_t& /*state*/, const char* from, const char* from_end, size_t max) const override
{
const char* start_from = from;
auto cvt_state = implementation().initial_state(to_unicode_state);
while(max > 0 && from < from_end) {
const char* save_from = from;
const utf::code_point ch = implementation().to_unicode(cvt_state, from, from_end);
if(ch == boost::locale::utf::incomplete || ch == boost::locale::utf::illegal) {
from = save_from;
break;
}
max--;
}
return static_cast<int>(from - start_from);
}
std::codecvt_base::result do_in(std::mbstate_t& /*state*/,
const char* from,
const char* from_end,
const char*& from_next,
uchar* to,
uchar* to_end,
uchar*& to_next) const override
{
std::codecvt_base::result r = std::codecvt_base::ok;
auto cvt_state = implementation().initial_state(to_unicode_state);
while(to < to_end && from < from_end) {
const char* from_saved = from;
const utf::code_point ch = implementation().to_unicode(cvt_state, from, from_end);
if(ch == boost::locale::utf::illegal) {
r = std::codecvt_base::error;
from = from_saved;
break;
}
if(ch == boost::locale::utf::incomplete) {
r = std::codecvt_base::partial;
from = from_saved;
break;
}
*to++ = ch;
}
from_next = from;
to_next = to;
if(r == std::codecvt_base::ok && from != from_end)
r = std::codecvt_base::partial;
return r;
}
std::codecvt_base::result do_out(std::mbstate_t& /*std_state*/,
const uchar* from,
const uchar* from_end,
const uchar*& from_next,
char* to,
char* to_end,
char*& to_next) const override
{
std::codecvt_base::result r = std::codecvt_base::ok;
auto cvt_state = implementation().initial_state(from_unicode_state);
while(to < to_end && from < from_end) {
const std::uint32_t ch = *from;
if(!boost::locale::utf::is_valid_codepoint(ch)) {
r = std::codecvt_base::error;
break;
}
const utf::code_point len = implementation().from_unicode(cvt_state, ch, to, to_end);
if(len == boost::locale::utf::incomplete) {
r = std::codecvt_base::partial;
break;
} else if(len == boost::locale::utf::illegal) {
r = std::codecvt_base::error;
break;
}
to += len;
from++;
}
from_next = from;
to_next = to;
if(r == std::codecvt_base::ok && from != from_end)
r = std::codecvt_base::partial;
return r;
}
};
template<typename CodecvtImpl>
class generic_codecvt<char, CodecvtImpl, 1> : public std::codecvt<char, char, std::mbstate_t>,
public generic_codecvt_base {
public:
typedef char uchar;
const CodecvtImpl& implementation() const { return *static_cast<const CodecvtImpl*>(this); }
generic_codecvt(size_t refs = 0) : std::codecvt<char, char, std::mbstate_t>(refs) {}
};
}} // namespace boost::locale
#endif
+124
View File
@@ -0,0 +1,124 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCLAE_GNU_GETTEXT_HPP
#define BOOST_LOCLAE_GNU_GETTEXT_HPP
#include <boost/locale/detail/is_supported_char.hpp>
#include <boost/locale/message.hpp>
#include <functional>
#include <stdexcept>
#include <type_traits>
#include <vector>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4251) // "identifier" : class "type" needs to have dll-interface...
#endif
namespace boost { namespace locale {
/// \addtogroup message
/// @{
/// \brief This namespace holds classes that provide GNU Gettext message catalogs support.
namespace gnu_gettext {
/// \brief This structure holds all information required for creating gnu-gettext message catalogs,
///
/// The user is expected to set its parameters to load these catalogs correctly. This structure
/// also allows providing functions for charset conversion. Note, you need to provide them,
/// so this structure is not useful for wide characters without subclassing and it will also
/// ignore gettext catalogs that use a charset different from \a encoding.
struct BOOST_LOCALE_DECL messages_info {
messages_info() : language("C"), locale_category("LC_MESSAGES") {}
std::string language; ///< The language we load the catalog for, like "ru", "en", "de"
std::string country; ///< The country we load the catalog for, like "US", "IL"
std::string variant; ///< Language variant, like "euro" so it would look for catalog like de_DE\@euro
std::string encoding; ///< Required target charset encoding. Ignored for wide characters.
///< For narrow, should specify the correct encoding required for this catalog
std::string locale_category; ///< Locale category, is set by default to LC_MESSAGES, but may be changed
///
/// \brief This type represents GNU Gettext domain name for the messages.
///
/// It consists of two parameters:
///
/// - name - the name of the domain - used for opening the file name
/// - encoding - the encoding of the keys in the sources, default - UTF-8
///
struct domain {
std::string name; ///< The name of the domain
std::string encoding; ///< The character encoding for the domain
domain() = default;
/// Create a domain object from the name that can hold an encoding after symbol "/"
/// such that if n is "hello/cp1255" then the name would be "hello" and "encoding" would
/// be "cp1255" and if n is "hello" then the name would be the same but encoding would be
/// "UTF-8"
domain(const std::string& n)
{
const size_t pos = n.find('/');
if(pos == std::string::npos) {
name = n;
encoding = "UTF-8";
} else {
name = n.substr(0, pos);
encoding = n.substr(pos + 1);
}
}
/// Check whether two objects are equivalent, only names are compared, encoding is ignored
bool operator==(const domain& other) const { return name == other.name; }
/// Check whether two objects are distinct, only names are compared, encoding is ignored
bool operator!=(const domain& other) const { return !(*this == other); }
};
typedef std::vector<domain> domains_type; ///< Type that defines a list of domains that are loaded
///< The first one is the default one
domains_type domains; ///< Message domains - application name, like my_app. So files named my_app.mo
///< would be loaded
std::vector<std::string> paths; ///< Paths to search files in. Under MS Windows it uses encoding
///< parameter to convert them to wide OS specific paths.
/// The callback for custom file system support. This callback should read the file named \a file_name
/// encoded in \a encoding character set into std::vector<char> and return it.
///
/// - If the file does not exist, it should return an empty vector.
/// - If an error occurs during file read it should throw an exception.
///
/// \note The user should support only the encodings the locales are created for. So if the user
/// uses only one encoding or the file system is encoding agnostic, he may ignore the \a encoding parameter.
typedef std::function<std::vector<char>(const std::string& file_name, const std::string& encoding)>
callback_type;
/// The callback for handling custom file systems, if it is empty, the real OS file-system
/// is being used.
callback_type callback;
/// Get paths to folders which may contain catalog files
std::vector<std::string> get_catalog_paths() const;
private:
/// Get a list of folder names for the language, country and variant
std::vector<std::string> get_lang_folders() const;
};
/// Create a message_format facet using GNU Gettext catalogs. It uses \a info structure to get
/// information about where to read them from and uses it for character set conversion (if needed)
template<typename CharType, class = boost::locale::detail::enable_if_is_supported_char<CharType>>
BOOST_LOCALE_DECL message_format<CharType>* create_messages_facet(const messages_info& info);
} // namespace gnu_gettext
/// @}
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+77
View File
@@ -0,0 +1,77 @@
//
// Copyright (c) 2010 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_HOLD_PTR_H
#define BOOST_LOCALE_HOLD_PTR_H
#include <boost/locale/config.hpp>
#include <boost/core/exchange.hpp>
namespace boost { namespace locale {
/// \brief a smart pointer similar to std::unique_ptr but the
/// underlying object has the same constness as the pointer itself (unlike an ordinary pointer).
template<typename T>
class hold_ptr {
public:
/// Create new empty pointer
hold_ptr() : ptr_(nullptr) {}
/// Create a pointer that holds \a v, ownership is transferred to smart pointer
explicit hold_ptr(T* v) : ptr_(v) {}
/// Destroy smart pointer and the object it owns.
~hold_ptr() { delete ptr_; }
// Non-copyable
hold_ptr(const hold_ptr&) = delete;
hold_ptr& operator=(const hold_ptr&) = delete;
// Movable
hold_ptr(hold_ptr&& other) noexcept : ptr_(exchange(other.ptr_, nullptr)) {}
hold_ptr& operator=(hold_ptr&& other) noexcept
{
swap(other);
return *this;
}
/// Get a const pointer to the object
T const* get() const { return ptr_; }
/// Get a mutable pointer to the object
T* get() { return ptr_; }
/// Explicitly convertible to bool. Returns: get() != nullptr
explicit operator bool() const { return ptr_ != nullptr; }
/// Get a const reference to the object
T const& operator*() const { return *ptr_; }
/// Get a mutable reference to the object
T& operator*() { return *ptr_; }
/// Get a const pointer to the object
T const* operator->() const { return ptr_; }
/// Get a mutable pointer to the object
T* operator->() { return ptr_; }
/// Transfer ownership of the pointer to user
T* release() { return exchange(ptr_, nullptr); }
/// Set new value to pointer, previous object is destroyed, ownership of new object is transferred
void reset(T* p = nullptr)
{
if(ptr_)
delete ptr_;
ptr_ = p;
}
/// Swap two pointers
void swap(hold_ptr& other) noexcept { ptr_ = exchange(other.ptr_, ptr_); }
private:
T* ptr_;
};
}} // namespace boost::locale
#endif
+72
View File
@@ -0,0 +1,72 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2022-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_INFO_HPP_INCLUDED
#define BOOST_LOCALE_INFO_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <boost/locale/detail/facet_id.hpp>
#include <locale>
#include <string>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \brief a facet that holds general information about locale
///
/// This facet should be always created in order to make all Boost.Locale functions work
class BOOST_SYMBOL_VISIBLE info : public std::locale::facet, public detail::facet_id<info> {
public:
/// String information about the locale
enum string_property {
language_property, ///< ISO 639 language id
country_property, ///< ISO 3166 country id
variant_property, ///< Variant for locale
encoding_property, ///< encoding name
name_property ///< locale name
};
/// Integer information about locale
enum integer_property {
utf8_property ///< Non zero value if uses UTF-8 encoding
};
/// Standard facet's constructor
info(size_t refs = 0) : std::locale::facet(refs) {}
/// Get language name
std::string language() const { return get_string_property(language_property); }
/// Get country name
std::string country() const { return get_string_property(country_property); }
/// Get locale variant
std::string variant() const { return get_string_property(variant_property); }
/// Get encoding
std::string encoding() const { return get_string_property(encoding_property); }
/// Get the name of the locale, like en_US.UTF-8
std::string name() const { return get_string_property(name_property); }
/// True if the underlying encoding is UTF-8 (for char streams and strings)
bool utf8() const { return get_integer_property(utf8_property) != 0; }
protected:
/// Get string property by its id \a v
virtual std::string get_string_property(string_property v) const = 0;
/// Get integer property by its id \a v
virtual int get_integer_property(integer_property v) const = 0;
};
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+129
View File
@@ -0,0 +1,129 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_LOCALIZATION_BACKEND_HPP
#define BOOST_LOCALE_LOCALIZATION_BACKEND_HPP
#include <boost/locale/generator.hpp>
#include <boost/locale/hold_ptr.hpp>
#include <locale>
#include <memory>
#include <string>
#include <vector>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \brief this class represents a localization backend that can be used for localizing your application.
///
/// Backends are usually registered inside the localization backends manager and allow transparent support
/// of different backends, so a user can switch the backend by simply linking the application to the correct one.
///
/// Backends may support different tuning options, but these are the default options available to the user
/// for all of them
///
/// -# \c locale - the name of the locale in POSIX format like en_US.UTF-8
/// -# \c use_ansi_encoding - select system locale using ANSI codepages rather then UTF-8 under Windows
/// by default
/// -# \c message_path - path to the location of message catalogs (vector of strings)
/// -# \c message_application - the name of applications that use message catalogs (vector of strings)
///
/// Each backend can be installed with a different default priority so when you work with two different backends,
/// you can specify priority so this backend will be chosen according to their priority.
class BOOST_LOCALE_DECL localization_backend {
protected:
localization_backend(const localization_backend&) = default;
localization_backend& operator=(const localization_backend&) = default;
public:
localization_backend() = default;
virtual ~localization_backend();
/// Make a polymorphic copy of the backend
virtual localization_backend* clone() const = 0;
/// Set option for backend, for example "locale" or "encoding"
virtual void set_option(const std::string& name, const std::string& value) = 0;
/// Clear all options
virtual void clear_options() = 0;
/// Create a facet for category \a category and character type \a type
virtual std::locale install(const std::locale& base, category_t category, char_facet_t type) = 0;
}; // localization_backend
/// \brief Localization backend manager is a class that holds various backend and allows creation
/// of their combination or selection
class BOOST_LOCALE_DECL localization_backend_manager {
public:
/// New empty localization_backend_manager
localization_backend_manager();
/// Copy localization_backend_manager
localization_backend_manager(const localization_backend_manager&);
/// Assign localization_backend_manager
localization_backend_manager& operator=(const localization_backend_manager&);
/// Move construct localization_backend_manager
localization_backend_manager(localization_backend_manager&&) noexcept;
/// Move assign localization_backend_manager
localization_backend_manager& operator=(localization_backend_manager&&) noexcept;
/// Destructor
~localization_backend_manager();
/// Create new localization backend according to current settings. Ownership is passed to caller
std::unique_ptr<localization_backend> create() const;
BOOST_DEPRECATED("This function is deprecated, use 'create()' instead")
std::unique_ptr<localization_backend> get() const { return create(); } // LCOV_EXCL_LINE
BOOST_DEPRECATED("This function is deprecated, use 'create()' instead")
std::unique_ptr<localization_backend> get_unique_ptr() const { return create(); } // LCOV_EXCL_LINE
/// Add new backend to the manager, each backend should be uniquely defined by its name.
///
/// This library provides: "icu", "posix", "winapi" and "std" backends.
void add_backend(const std::string& name, std::unique_ptr<localization_backend> backend);
// clang-format off
BOOST_DEPRECATED("This function is deprecated, use 'add_backend' instead")
void adopt_backend(const std::string& name, localization_backend* backend) { add_backend(name, std::unique_ptr<localization_backend>(backend)); } // LCOV_EXCL_LINE
// clang-format on
/// Clear backend
void remove_all_backends();
/// Get list of all available backends
std::vector<std::string> get_all_backends() const;
/// Select specific backend by name for a category \a category. It allows combining different
/// backends for user preferences.
void select(const std::string& backend_name, category_t category = all_categories);
/// Set new global backend manager, the old one is returned.
///
/// This function is thread safe
static localization_backend_manager global(const localization_backend_manager&);
/// Get global backend manager
///
/// This function is thread safe
static localization_backend_manager global();
private:
class impl;
hold_ptr<impl> pimpl_;
};
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+550
View File
@@ -0,0 +1,550 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2021-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_MESSAGE_HPP_INCLUDED
#define BOOST_LOCALE_MESSAGE_HPP_INCLUDED
#include <boost/locale/detail/facet_id.hpp>
#include <boost/locale/detail/is_supported_char.hpp>
#include <boost/locale/formatting.hpp>
#include <boost/locale/util/string.hpp>
#include <locale>
#include <memory>
#include <set>
#include <string>
#include <type_traits>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
// glibc < 2.3.4 declares those as macros if compiled with optimization turned on
#ifdef gettext
# undef gettext
# undef ngettext
# undef dgettext
# undef dngettext
#endif
namespace boost { namespace locale {
///
/// \defgroup message Message Formatting (translation)
///
/// This module provides message translation functionality, i.e. allow your application to speak native language
///
/// @{
///
/// Type used for the count/n argument to the translation functions choosing between singular and plural forms
using count_type = long long;
/// \brief This facet provides message formatting abilities
template<typename CharType>
class BOOST_SYMBOL_VISIBLE message_format : public std::locale::facet,
public detail::facet_id<message_format<CharType>> {
BOOST_LOCALE_ASSERT_IS_SUPPORTED(CharType);
public:
/// Character type
typedef CharType char_type;
/// String type
typedef std::basic_string<CharType> string_type;
/// Standard constructor
message_format(size_t refs = 0) : std::locale::facet(refs) {}
/// This function returns a pointer to the string for a message defined by a \a context
/// and identification string \a id. Both create a single key for message lookup in
/// a domain defined by \a domain_id.
///
/// If \a context is NULL it is not considered to be a part of the key
///
/// If a translated string is found, it is returned, otherwise NULL is returned
virtual const char_type* get(int domain_id, const char_type* context, const char_type* id) const = 0;
/// This function returns a pointer to the string for a plural message defined by a \a context
/// and identification string \a single_id.
///
/// If \a context is NULL it is not considered to be a part of the key
///
/// Both create a single key for message lookup in
/// a domain defined \a domain_id. \a n is used to pick the correct translation string for a specific
/// number.
///
/// If a translated string is found, it is returned, otherwise NULL is returned
virtual const char_type*
get(int domain_id, const char_type* context, const char_type* single_id, count_type n) const = 0;
/// Convert a string that defines \a domain to the integer id used by \a get functions
virtual int domain(const std::string& domain) const = 0;
/// Convert the string \a msg to target locale's encoding. If \a msg is already
/// in target encoding it would be returned otherwise the converted
/// string is stored in temporary \a buffer and buffer.c_str() is returned.
///
/// Note: for char_type that is char16_t, char32_t and wchar_t it is no-op, returns
/// msg
virtual const char_type* convert(const char_type* msg, string_type& buffer) const = 0;
};
/// \cond INTERNAL
namespace detail {
inline bool is_us_ascii_char(char c)
{
// works for null terminated strings regardless char "signedness"
return 0 < c && c < 0x7F;
}
inline bool is_us_ascii_string(const char* msg)
{
while(*msg) {
if(!is_us_ascii_char(*msg++))
return false;
}
return true;
}
template<typename CharType>
struct string_cast_traits {
static const CharType* cast(const CharType* msg, std::basic_string<CharType>& /*unused*/) { return msg; }
};
template<>
struct string_cast_traits<char> {
static const char* cast(const char* msg, std::string& buffer)
{
if(is_us_ascii_string(msg))
return msg;
buffer.reserve(strlen(msg));
char c;
while((c = *msg++) != 0) {
if(is_us_ascii_char(c))
buffer += c;
}
return buffer.c_str();
}
};
} // namespace detail
/// \endcond
/// \brief This class represents a message that can be converted to a specific locale message
///
/// It holds the original ASCII string that is queried in the dictionary when converting to the output string.
/// The created string may be UTF-8, UTF-16, UTF-32 or other 8-bit encoded string according to the target
/// character type and locale encoding.
template<typename CharType>
class basic_message {
public:
typedef CharType char_type; ///< The character this message object is used with
typedef std::basic_string<char_type> string_type; ///< The string type this object can be used with
typedef message_format<char_type> facet_type; ///< The type of the facet the messages are fetched with
/// Create default empty message
basic_message() : n_(0), c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr) {}
/// Create a simple message from 0 terminated string. The string should exist
/// until the message is destroyed. Generally useful with static constant strings
explicit basic_message(const char_type* id) : n_(0), c_id_(id), c_context_(nullptr), c_plural_(nullptr) {}
/// Create a simple plural form message from 0 terminated strings. The strings should exist
/// until the message is destroyed. Generally useful with static constant strings.
///
/// \a n is the number, \a single and \a plural are singular and plural forms of the message
explicit basic_message(const char_type* single, const char_type* plural, count_type n) :
n_(n), c_id_(single), c_context_(nullptr), c_plural_(plural)
{}
/// Create a simple message from 0 terminated strings, with context
/// information. The string should exist
/// until the message is destroyed. Generally useful with static constant strings
explicit basic_message(const char_type* context, const char_type* id) :
n_(0), c_id_(id), c_context_(context), c_plural_(nullptr)
{}
/// Create a simple plural form message from 0 terminated strings, with context. The strings should exist
/// until the message is destroyed. Generally useful with static constant strings.
///
/// \a n is the number, \a single and \a plural are singular and plural forms of the message
explicit basic_message(const char_type* context,
const char_type* single,
const char_type* plural,
count_type n) :
n_(n),
c_id_(single), c_context_(context), c_plural_(plural)
{}
/// Create a simple message from a string.
explicit basic_message(const string_type& id) :
n_(0), c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr), id_(id)
{}
/// Create a simple plural form message from strings.
///
/// \a n is the number, \a single and \a plural are single and plural forms of the message
explicit basic_message(const string_type& single, const string_type& plural, count_type number) :
n_(number), c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr), id_(single), plural_(plural)
{}
/// Create a simple message from a string with context.
explicit basic_message(const string_type& context, const string_type& id) :
n_(0), c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr), id_(id), context_(context)
{}
/// Create a simple plural form message from strings.
///
/// \a n is the number, \a single and \a plural are single and plural forms of the message
explicit basic_message(const string_type& context,
const string_type& single,
const string_type& plural,
count_type number) :
n_(number),
c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr), id_(single), context_(context), plural_(plural)
{}
/// Copy an object
basic_message(const basic_message&) = default;
basic_message(basic_message&&) noexcept = default;
/// Assign other message object to this one
basic_message& operator=(const basic_message&) = default;
basic_message&
operator=(basic_message&&) noexcept(std::is_nothrow_move_assignable<string_type>::value) = default;
/// Swap two message objects
void
swap(basic_message& other) noexcept(noexcept(std::declval<string_type&>().swap(std::declval<string_type&>())))
{
using std::swap;
swap(n_, other.n_);
swap(c_id_, other.c_id_);
swap(c_context_, other.c_context_);
swap(c_plural_, other.c_plural_);
swap(id_, other.id_);
swap(context_, other.context_);
swap(plural_, other.plural_);
}
friend void swap(basic_message& x, basic_message& y) noexcept(noexcept(x.swap(y))) { x.swap(y); }
/// Message class can be explicitly converted to string class
operator string_type() const { return str(); }
/// Translate message to a string in the default global locale, using default domain
string_type str() const { return str(std::locale()); }
/// Translate message to a string in the locale \a locale, using default domain
string_type str(const std::locale& locale) const { return str(locale, 0); }
/// Translate message to a string using locale \a locale and message domain \a domain_id
string_type str(const std::locale& locale, const std::string& domain_id) const
{
int id = 0;
if(std::has_facet<facet_type>(locale))
id = std::use_facet<facet_type>(locale).domain(domain_id);
return str(locale, id);
}
/// Translate message to a string using the default locale and message domain \a domain_id
string_type str(const std::string& domain_id) const { return str(std::locale(), domain_id); }
/// Translate message to a string using locale \a loc and message domain index \a id
string_type str(const std::locale& loc, int id) const
{
string_type buffer;
const char_type* ptr = write(loc, id, buffer);
if(ptr != buffer.c_str())
buffer = ptr;
return buffer;
}
/// Translate message and write to stream \a out, using imbued locale and domain set to the
/// stream
void write(std::basic_ostream<char_type>& out) const
{
const std::locale& loc = out.getloc();
int id = ios_info::get(out).domain_id();
string_type buffer;
out << write(loc, id, buffer);
}
private:
const char_type* plural() const
{
if(c_plural_)
return c_plural_;
if(plural_.empty())
return nullptr;
return plural_.c_str();
}
const char_type* context() const
{
if(c_context_)
return c_context_;
if(context_.empty())
return nullptr;
return context_.c_str();
}
const char_type* id() const { return c_id_ ? c_id_ : id_.c_str(); }
const char_type* write(const std::locale& loc, int domain_id, string_type& buffer) const
{
static const char_type empty_string[1] = {0};
const char_type* id = this->id();
const char_type* context = this->context();
const char_type* plural = this->plural();
if(*id == 0)
return empty_string;
const facet_type* facet = nullptr;
if(std::has_facet<facet_type>(loc))
facet = &std::use_facet<facet_type>(loc);
const char_type* translated = nullptr;
if(facet) {
if(!plural)
translated = facet->get(domain_id, context, id);
else
translated = facet->get(domain_id, context, id, n_);
}
if(!translated) {
const char_type* msg = plural ? (n_ == 1 ? id : plural) : id;
if(facet)
translated = facet->convert(msg, buffer);
else
translated = detail::string_cast_traits<char_type>::cast(msg, buffer);
}
return translated;
}
/// members
count_type n_;
const char_type* c_id_;
const char_type* c_context_;
const char_type* c_plural_;
string_type id_;
string_type context_;
string_type plural_;
};
/// Convenience typedef for char
typedef basic_message<char> message;
/// Convenience typedef for wchar_t
typedef basic_message<wchar_t> wmessage;
#ifndef BOOST_LOCALE_NO_CXX20_STRING8
/// Convenience typedef for char8_t
typedef basic_message<char8_t> u8message;
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
/// Convenience typedef for char16_t
typedef basic_message<char16_t> u16message;
#endif
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
/// Convenience typedef for char32_t
typedef basic_message<char32_t> u32message;
#endif
/// Translate message \a msg and write it to stream
template<typename CharType>
std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const basic_message<CharType>& msg)
{
msg.write(out);
return out;
}
/// \anchor boost_locale_translate_family \name Indirect message translation function family
/// @{
/// \brief Translate a message, \a msg is not copied
template<typename CharType>
inline basic_message<CharType> translate(const CharType* msg)
{
return basic_message<CharType>(msg);
}
/// \brief Translate a message in context, \a msg and \a context are not copied
template<typename CharType>
inline basic_message<CharType> translate(const CharType* context, const CharType* msg)
{
return basic_message<CharType>(context, msg);
}
/// \brief Translate a plural message form, \a single and \a plural are not copied
template<typename CharType>
inline basic_message<CharType> translate(const CharType* single, const CharType* plural, count_type n)
{
return basic_message<CharType>(single, plural, n);
}
/// \brief Translate a plural message from in context, \a context, \a single and \a plural are not copied
template<typename CharType>
inline basic_message<CharType>
translate(const CharType* context, const CharType* single, const CharType* plural, count_type n)
{
return basic_message<CharType>(context, single, plural, n);
}
/// \brief Translate a message, \a msg is copied
template<typename CharType>
inline basic_message<CharType> translate(const std::basic_string<CharType>& msg)
{
return basic_message<CharType>(msg);
}
/// \brief Translate a message in context,\a context and \a msg is copied
template<typename CharType>
inline basic_message<CharType> translate(const std::basic_string<CharType>& context,
const std::basic_string<CharType>& msg)
{
return basic_message<CharType>(context, msg);
}
/// \brief Translate a plural message form in context, \a context, \a single and \a plural are copied
template<typename CharType>
inline basic_message<CharType> translate(const std::basic_string<CharType>& context,
const std::basic_string<CharType>& single,
const std::basic_string<CharType>& plural,
count_type n)
{
return basic_message<CharType>(context, single, plural, n);
}
/// \brief Translate a plural message form, \a single and \a plural are copied
template<typename CharType>
inline basic_message<CharType>
translate(const std::basic_string<CharType>& single, const std::basic_string<CharType>& plural, count_type n)
{
return basic_message<CharType>(single, plural, n);
}
/// @}
/// \anchor boost_locale_gettext_family \name Direct message translation functions family
/// Translate message \a id according to locale \a loc
template<typename CharType>
std::basic_string<CharType> gettext(const CharType* id, const std::locale& loc = std::locale())
{
return basic_message<CharType>(id).str(loc);
}
/// Translate plural form according to locale \a loc
template<typename CharType>
std::basic_string<CharType>
ngettext(const CharType* s, const CharType* p, count_type n, const std::locale& loc = std::locale())
{
return basic_message<CharType>(s, p, n).str(loc);
}
/// Translate message \a id according to locale \a loc in domain \a domain
template<typename CharType>
std::basic_string<CharType> dgettext(const char* domain, const CharType* id, const std::locale& loc = std::locale())
{
return basic_message<CharType>(id).str(loc, domain);
}
/// Translate plural form according to locale \a loc in domain \a domain
template<typename CharType>
std::basic_string<CharType> dngettext(const char* domain,
const CharType* s,
const CharType* p,
count_type n,
const std::locale& loc = std::locale())
{
return basic_message<CharType>(s, p, n).str(loc, domain);
}
/// Translate message \a id according to locale \a loc in context \a context
template<typename CharType>
std::basic_string<CharType>
pgettext(const CharType* context, const CharType* id, const std::locale& loc = std::locale())
{
return basic_message<CharType>(context, id).str(loc);
}
/// Translate plural form according to locale \a loc in context \a context
template<typename CharType>
std::basic_string<CharType> npgettext(const CharType* context,
const CharType* s,
const CharType* p,
count_type n,
const std::locale& loc = std::locale())
{
return basic_message<CharType>(context, s, p, n).str(loc);
}
/// Translate message \a id according to locale \a loc in domain \a domain in context \a context
template<typename CharType>
std::basic_string<CharType>
dpgettext(const char* domain, const CharType* context, const CharType* id, const std::locale& loc = std::locale())
{
return basic_message<CharType>(context, id).str(loc, domain);
}
/// Translate plural form according to locale \a loc in domain \a domain in context \a context
template<typename CharType>
std::basic_string<CharType> dnpgettext(const char* domain,
const CharType* context,
const CharType* s,
const CharType* p,
count_type n,
const std::locale& loc = std::locale())
{
return basic_message<CharType>(context, s, p, n).str(loc, domain);
}
/// @}
namespace as {
/// \cond INTERNAL
namespace detail {
struct set_domain {
std::string domain_id;
};
template<typename CharType>
std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const set_domain& dom)
{
int id = std::use_facet<message_format<CharType>>(out.getloc()).domain(dom.domain_id);
ios_info::get(out).domain_id(id);
return out;
}
} // namespace detail
/// \endcond
/// \addtogroup manipulators
///
/// @{
/// Manipulator for switching message domain in ostream,
///
/// \note The returned object throws std::bad_cast if the I/O stream does not have \ref message_format facet
/// installed
inline
#ifdef BOOST_LOCALE_DOXYGEN
unspecified_type
#else
detail::set_domain
#endif
domain(const std::string& id)
{
detail::set_domain tmp = {id};
return tmp;
}
/// @}
} // namespace as
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+40
View File
@@ -0,0 +1,40 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_TIME_ZONE_HPP_INCLUDED
#define BOOST_LOCALE_TIME_ZONE_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <string>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4275 4251 4231 4660)
#endif
namespace boost { namespace locale {
/// \addtogroup date_time
///
/// @{
/// \brief namespace that holds functions for operating with global
/// time zone
namespace time_zone {
/// Get global time zone identifier. If empty, system time zone is used
BOOST_LOCALE_DECL std::string global();
/// Set global time zone identifier returning previous one. If empty, system time zone is used
BOOST_LOCALE_DECL std::string global(const std::string& new_tz);
} // namespace time_zone
/// @}
}} // namespace boost::locale
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+371
View File
@@ -0,0 +1,371 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_UTF_HPP_INCLUDED
#define BOOST_LOCALE_UTF_HPP_INCLUDED
#include <boost/locale/config.hpp>
#include <cstdint>
namespace boost { namespace locale {
/// \brief Namespace that holds basic operations on UTF encoded sequences
///
/// All functions defined in this namespace do not require linking with Boost.Locale library
namespace utf {
/// \brief The integral type that can hold a Unicode code point
using code_point = uint32_t;
/// \brief Special constant that defines illegal code point
constexpr code_point illegal = 0xFFFFFFFFu;
/// \brief Special constant that defines incomplete code point
constexpr code_point incomplete = 0xFFFFFFFEu;
/// Either a length/size or an error (illegal/incomplete)
using len_or_error = code_point;
/// \brief the function checks if \a v is a valid code point
inline bool is_valid_codepoint(code_point v)
{
if(v > 0x10FFFF)
return false;
if(0xD800 <= v && v <= 0xDFFF) // surrogates
return false;
return true;
}
#ifdef BOOST_LOCALE_DOXYGEN
/// \brief UTF Traits class - functions to convert UTF sequences to and from Unicode code points
template<typename CharType, int size = sizeof(CharType)>
struct utf_traits {
/// The type of the character
typedef CharType char_type;
/// Read one code point from the range [p,e) and return it.
///
/// - If the sequence that was read is incomplete sequence returns \ref incomplete,
/// - If illegal sequence detected returns \ref illegal
///
/// Requirements
///
/// - Iterator is valid input iterator
///
/// Postconditions
///
/// - p points to the last consumed character
template<typename Iterator>
static code_point decode(Iterator& p, Iterator e);
/// Maximal width of valid sequence in the code units:
///
/// - UTF-8 - 4
/// - UTF-16 - 2
/// - UTF-32 - 1
static constexpr int max_width;
/// The width of specific code point in the code units.
///
/// Requirement: value is a valid Unicode code point
/// Returns value in range [1..max_width]
static int width(code_point value);
/// Get the size of the trail part of variable length encoded sequence.
///
/// Returns -1 if C is not valid lead character
static int trail_length(char_type c);
/// Returns true if c is trail code unit, always false for UTF-32
static bool is_trail(char_type c);
/// Returns true if c is lead code unit, always true of UTF-32
static bool is_lead(char_type c);
/// Convert valid Unicode code point \a value to the UTF sequence.
///
/// Requirements:
///
/// - \a value is valid code point
/// - \a out is an output iterator should be able to accept at least width(value) units
///
/// Returns the iterator past the last written code unit.
template<typename Iterator>
static Iterator encode(code_point value, Iterator out);
/// Decodes valid UTF sequence that is pointed by p into code point.
///
/// If the sequence is invalid or points to end the behavior is undefined
template<typename Iterator>
static code_point decode_valid(Iterator& p);
};
#else
template<typename CharType, int size = sizeof(CharType)>
struct utf_traits;
template<typename CharType>
struct utf_traits<CharType, 1> {
typedef CharType char_type;
static int trail_length(char_type ci)
{
unsigned char c = ci;
if(c < 128)
return 0;
if(BOOST_UNLIKELY(c < 194))
return -1;
if(c < 224)
return 1;
if(c < 240)
return 2;
if(BOOST_LIKELY(c <= 244))
return 3;
return -1;
}
static constexpr int max_width = 4;
static int width(code_point value)
{
if(value <= 0x7F)
return 1;
else if(value <= 0x7FF)
return 2;
else if(BOOST_LIKELY(value <= 0xFFFF))
return 3;
else
return 4;
}
static bool is_trail(char_type ci)
{
unsigned char c = ci;
return (c & 0xC0) == 0x80;
}
static bool is_lead(char_type ci) { return !is_trail(ci); }
template<typename Iterator>
static code_point decode(Iterator& p, Iterator e)
{
if(BOOST_UNLIKELY(p == e))
return incomplete;
unsigned char lead = *p++;
// First byte is fully validated here
int trail_size = trail_length(lead);
if(BOOST_UNLIKELY(trail_size < 0))
return illegal;
// Ok as only ASCII may be of size = 0
// also optimize for ASCII text
if(trail_size == 0)
return lead;
code_point c = lead & ((1 << (6 - trail_size)) - 1);
// Read the rest
unsigned char tmp;
switch(trail_size) {
case 3:
if(BOOST_UNLIKELY(p == e))
return incomplete;
tmp = *p++;
if(!is_trail(tmp))
return illegal;
c = (c << 6) | (tmp & 0x3F);
BOOST_FALLTHROUGH;
case 2:
if(BOOST_UNLIKELY(p == e))
return incomplete;
tmp = *p++;
if(!is_trail(tmp))
return illegal;
c = (c << 6) | (tmp & 0x3F);
BOOST_FALLTHROUGH;
case 1:
if(BOOST_UNLIKELY(p == e))
return incomplete;
tmp = *p++;
if(!is_trail(tmp))
return illegal;
c = (c << 6) | (tmp & 0x3F);
}
// Check code point validity: no surrogates and
// valid range
if(BOOST_UNLIKELY(!is_valid_codepoint(c)))
return illegal;
// make sure it is the most compact representation
if(BOOST_UNLIKELY(width(c) != trail_size + 1))
return illegal;
return c;
}
template<typename Iterator>
static code_point decode_valid(Iterator& p)
{
unsigned char lead = *p++;
if(lead < 192)
return lead;
int trail_size;
if(lead < 224)
trail_size = 1;
else if(BOOST_LIKELY(lead < 240)) // non-BMP rare
trail_size = 2;
else
trail_size = 3;
code_point c = lead & ((1 << (6 - trail_size)) - 1);
switch(trail_size) {
case 3: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F); BOOST_FALLTHROUGH;
case 2: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F); BOOST_FALLTHROUGH;
case 1: c = (c << 6) | (static_cast<unsigned char>(*p++) & 0x3F);
}
return c;
}
template<typename Iterator>
static Iterator encode(code_point value, Iterator out)
{
if(value <= 0x7F)
*out++ = static_cast<char_type>(value);
else if(value <= 0x7FF) {
*out++ = static_cast<char_type>((value >> 6) | 0xC0);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
} else if(BOOST_LIKELY(value <= 0xFFFF)) {
*out++ = static_cast<char_type>((value >> 12) | 0xE0);
*out++ = static_cast<char_type>(((value >> 6) & 0x3F) | 0x80);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
} else {
*out++ = static_cast<char_type>((value >> 18) | 0xF0);
*out++ = static_cast<char_type>(((value >> 12) & 0x3F) | 0x80);
*out++ = static_cast<char_type>(((value >> 6) & 0x3F) | 0x80);
*out++ = static_cast<char_type>((value & 0x3F) | 0x80);
}
return out;
}
}; // utf8
template<typename CharType>
struct utf_traits<CharType, 2> {
typedef CharType char_type;
// See RFC 2781
static bool is_first_surrogate(uint16_t x) { return 0xD800 <= x && x <= 0xDBFF; }
static bool is_second_surrogate(uint16_t x) { return 0xDC00 <= x && x <= 0xDFFF; }
static code_point combine_surrogate(uint16_t w1, uint16_t w2)
{
return ((code_point(w1 & 0x3FF) << 10) | (w2 & 0x3FF)) + 0x10000;
}
static int trail_length(char_type c)
{
if(is_first_surrogate(c))
return 1;
if(is_second_surrogate(c))
return -1;
return 0;
}
/// Returns true if c is trail code unit, always false for UTF-32
static bool is_trail(char_type c) { return is_second_surrogate(c); }
/// Returns true if c is lead code unit, always true of UTF-32
static bool is_lead(char_type c) { return !is_second_surrogate(c); }
template<typename It>
static code_point decode(It& current, It last)
{
if(BOOST_UNLIKELY(current == last))
return incomplete;
uint16_t w1 = *current++;
if(BOOST_LIKELY(w1 < 0xD800 || 0xDFFF < w1))
return w1;
if(w1 > 0xDBFF)
return illegal;
if(current == last)
return incomplete;
uint16_t w2 = *current++;
if(w2 < 0xDC00 || 0xDFFF < w2)
return illegal;
return combine_surrogate(w1, w2);
}
template<typename It>
static code_point decode_valid(It& current)
{
uint16_t w1 = *current++;
if(BOOST_LIKELY(w1 < 0xD800 || 0xDFFF < w1))
return w1;
uint16_t w2 = *current++;
return combine_surrogate(w1, w2);
}
static constexpr int max_width = 2;
static int width(code_point u) { return u >= 0x10000 ? 2 : 1; }
template<typename It>
static It encode(code_point u, It out)
{
if(BOOST_LIKELY(u <= 0xFFFF))
*out++ = static_cast<char_type>(u);
else {
u -= 0x10000;
*out++ = static_cast<char_type>(0xD800 | (u >> 10));
*out++ = static_cast<char_type>(0xDC00 | (u & 0x3FF));
}
return out;
}
}; // utf16;
template<typename CharType>
struct utf_traits<CharType, 4> {
typedef CharType char_type;
static int trail_length(char_type c)
{
if(is_valid_codepoint(c))
return 0;
return -1;
}
static bool is_trail(char_type /*c*/) { return false; }
static bool is_lead(char_type /*c*/) { return true; }
template<typename It>
static code_point decode_valid(It& current)
{
return *current++;
}
template<typename It>
static code_point decode(It& current, It last)
{
if(BOOST_UNLIKELY(current == last))
return boost::locale::utf::incomplete;
code_point c = *current++;
if(BOOST_UNLIKELY(!is_valid_codepoint(c)))
return boost::locale::utf::illegal;
return c;
}
static constexpr int max_width = 1;
static int width(code_point /*u*/) { return 1; }
template<typename It>
static It encode(code_point u, It out)
{
*out++ = static_cast<char_type>(u);
return out;
}
}; // utf32
#endif
} // namespace utf
}} // namespace boost::locale
#endif
+56
View File
@@ -0,0 +1,56 @@
//
// Copyright (c) 2015 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_UTF8_CODECVT_HPP
#define BOOST_LOCALE_UTF8_CODECVT_HPP
#include <boost/locale/generic_codecvt.hpp>
#include <boost/locale/utf.hpp>
#include <boost/assert.hpp>
#include <cstdint>
#include <locale>
namespace boost { namespace locale {
/// \brief Generic utf8 codecvt facet, it allows to convert UTF-8 strings to UTF-16 and UTF-32 using wchar_t,
/// char32_t and char16_t
template<typename CharType>
class utf8_codecvt : public generic_codecvt<CharType, utf8_codecvt<CharType>> {
public:
struct state_type {};
utf8_codecvt(size_t refs = 0) : generic_codecvt<CharType, utf8_codecvt<CharType>>(refs) {}
static int max_encoding_length() { return 4; }
static state_type initial_state(generic_codecvt_base::initial_convertion_state /* unused */)
{
return state_type();
}
static utf::code_point to_unicode(state_type&, const char*& begin, const char* end)
{
const char* p = begin;
utf::code_point c = utf::utf_traits<char>::decode(p, end);
if(c != utf::illegal && c != utf::incomplete)
begin = p;
return c;
}
static utf::len_or_error from_unicode(state_type&, utf::code_point u, char* begin, const char* end)
{
BOOST_ASSERT(utf::is_valid_codepoint(u));
const auto width = utf::utf_traits<char>::width(u);
if(width > end - begin)
return utf::incomplete;
utf::utf_traits<char>::encode(u, begin);
return width;
}
};
}} // namespace boost::locale
#endif
+210
View File
@@ -0,0 +1,210 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2022-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_UTIL_HPP
#define BOOST_LOCALE_UTIL_HPP
#include <boost/locale/generator.hpp>
#include <boost/locale/utf.hpp>
#include <boost/assert.hpp>
#include <cstdint>
#include <locale>
#include <memory>
#include <typeinfo>
namespace boost { namespace locale {
/// \brief This namespace provides various utility function useful for Boost.Locale's backends
/// implementations
namespace util {
/// \brief Return default system locale name in POSIX format.
///
/// This function tries to detect the locale using LC_ALL, LC_CTYPE and LANG environment
/// variables in this order and if all of them are unset, on POSIX platforms it returns "C".
/// On Windows additionally to the above environment variables, this function
/// tries to create the locale name from ISO-639 and ISO-3166 country codes defined
/// for the users default locale.
/// If \a use_utf8_on_windows is true it sets the encoding to UTF-8,
/// otherwise, if the system locale supports ANSI codepages it defines the ANSI encoding, e.g. windows-1252,
/// otherwise (if ANSI codepage is not available) it uses UTF-8 encoding.
BOOST_LOCALE_DECL
std::string get_system_locale(bool use_utf8_on_windows = false);
/// \brief Installs information facet to locale \a in based on locale name \a name
///
/// This function installs boost::locale::info facet into the locale \a in and returns
/// newly created locale.
///
/// Note: all information is based only on parsing of string \a name;
///
/// The name has following format: language[_COUNTRY][.encoding][\@variant]
/// Where language is ISO-639 language code like "en" or "ru", COUNTRY is ISO-3166
/// country identifier like "US" or "RU". the Encoding is a character set name
/// like UTF-8 or ISO-8859-1. Variant is backend specific variant like \c euro or
/// calendar=hebrew.
///
/// If some parameters are missing they are specified as blanks, default encoding
/// is assumed to be US-ASCII and missing language is assumed to be "C"
BOOST_LOCALE_DECL
std::locale create_info(const std::locale& in, const std::string& name);
/// \brief This class represent a simple stateless converter from UCS-4 and to UCS-4 for
/// each single code point
///
/// This class is used for creation of std::codecvt facet for converting utf-16/utf-32 encoding
/// to encoding supported by this converter
///
/// Please note, this converter should be fully stateless. Fully stateless means it should
/// never assume that it is called in any specific order on the text. Even if the
/// encoding itself seems to be stateless like windows-1255 or shift-jis, some
/// encoders (most notably iconv) can actually compose several code-point into one or
/// decompose them in case composite characters are found. So be very careful when implementing
/// these converters for certain character set.
class BOOST_LOCALE_DECL base_converter {
public:
/// This value should be returned when an illegal input sequence or code-point is observed:
/// For example if a UCS-32 code-point is in the range reserved for UTF-16 surrogates
/// or an invalid UTF-8 sequence is found
static constexpr utf::code_point illegal = utf::illegal;
/// This value is returned in following cases: An incomplete input sequence was found or
/// insufficient output buffer was provided so complete output could not be written.
static constexpr utf::code_point incomplete = utf::incomplete;
virtual ~base_converter();
/// Return the maximal length that one Unicode code-point can be converted to, for example
/// for UTF-8 it is 4, for Shift-JIS it is 2 and ISO-8859-1 is 1
virtual int max_len() const { return 1; }
/// Returns true if calling the functions from_unicode, to_unicode, and max_len is thread safe.
///
/// Rule of thumb: if this class' implementation uses simple tables that are unchanged
/// or is purely algorithmic like UTF-8 - so it does not share any mutable bit for
/// independent to_unicode, from_unicode calls, you may set it to true, otherwise,
/// for example if you use iconv_t descriptor or UConverter as conversion object return false,
/// and this object will be cloned for each use.
virtual bool is_thread_safe() const { return false; }
/// Create a polymorphic copy of this object, usually called only if is_thread_safe() return false
virtual base_converter* clone() const
{
BOOST_ASSERT(typeid(*this) == typeid(base_converter));
return new base_converter();
}
/// Convert a single character starting at begin and ending at most at end to Unicode code-point.
///
/// if valid input sequence found in [\a begin,\a code_point_end) such as \a begin < \a code_point_end && \a
/// code_point_end <= \a end it is converted to its Unicode code point equivalent, \a begin is set to \a
/// code_point_end
///
/// if incomplete input sequence found in [\a begin,\a end), i.e. there my be such \a code_point_end that \a
/// code_point_end > \a end and [\a begin, \a code_point_end) would be valid input sequence, then \a
/// incomplete is returned begin stays unchanged, for example for UTF-8 conversion a *begin = 0xc2, \a begin
/// +1 = \a end is such situation.
///
/// if invalid input sequence found, i.e. there is a sequence [\a begin, \a code_point_end) such as \a
/// code_point_end <= \a end that is illegal for this encoding, \a illegal is returned and begin stays
/// unchanged. For example if *begin = 0xFF and begin < end for UTF-8, then \a illegal is returned.
virtual utf::code_point to_unicode(const char*& begin, const char* end)
{
if(begin == end)
return incomplete; // LCOV_EXCL_LINE
unsigned char cp = *begin;
if(cp <= 0x7F) {
begin++;
return cp;
}
return illegal;
}
/// Convert a single code-point \a u into encoding and store it in [begin,end) range.
///
/// If u is invalid Unicode code-point, or it can not be mapped correctly to represented character set,
/// \a illegal should be returned
///
/// If u can be converted to a sequence of bytes c1, ... , cN (1<= N <= max_len() ) then
///
/// -# If end - begin >= N, c1, ... cN are written starting at begin and N is returned
/// -# If end - begin < N, incomplete is returned, it is unspecified what would be
/// stored in bytes in range [begin,end)
virtual utf::len_or_error from_unicode(utf::code_point u, char* begin, const char* end)
{
if(begin == end)
return incomplete; // LCOV_EXCL_LINE
if(u >= 0x80)
return illegal;
*begin = static_cast<char>(u);
return 1;
}
};
/// This function creates a \a base_converter that can be used for conversion between UTF-8 and
/// Unicode code points
BOOST_LOCALE_DECL std::unique_ptr<base_converter> create_utf8_converter();
BOOST_DEPRECATED("This function is deprecated, use 'create_utf8_converter()'")
inline std::unique_ptr<base_converter> create_utf8_converter_unique_ptr()
{
return create_utf8_converter();
}
/// This function creates a \a base_converter that can be used for conversion between single byte
/// character encodings like ISO-8859-1, koi8-r, windows-1255 and Unicode code points,
///
/// If \a encoding is not supported, empty pointer is returned.
/// So you should check whether the returned pointer is valid/non-NULL
BOOST_LOCALE_DECL std::unique_ptr<base_converter> create_simple_converter(const std::string& encoding);
BOOST_DEPRECATED("This function is deprecated, use 'create_simple_converter()'")
inline std::unique_ptr<base_converter> create_simple_converter_unique_ptr(const std::string& encoding)
{
return create_simple_converter(encoding);
}
/// Install codecvt facet into locale \a in and return new locale that is based on \a in and uses new
/// facet.
///
/// codecvt facet would convert between narrow and wide/char16_t/char32_t encodings using \a cvt converter.
/// If \a cvt is null pointer, always failure conversion would be used that fails on every first input or
/// output.
///
/// Note: the codecvt facet handles both UTF-16 and UTF-32 wide encodings, it knows to break and join
/// Unicode code-points above 0xFFFF to and from surrogate pairs correctly. \a cvt should be unaware
/// of wide encoding type
BOOST_LOCALE_DECL
std::locale create_codecvt(const std::locale& in, std::unique_ptr<base_converter> cvt, char_facet_t type);
BOOST_DEPRECATED("This function is deprecated, use 'create_codecvt()'")
inline std::locale create_codecvt_from_pointer(const std::locale& in, base_converter* cvt, char_facet_t type)
{
return create_codecvt(in, std::unique_ptr<base_converter>(cvt), type);
}
BOOST_DEPRECATED("This function is deprecated, use 'create_utf8_converter()'")
BOOST_LOCALE_DECL base_converter* create_utf8_converter_new_ptr();
BOOST_DEPRECATED("This function is deprecated, use 'create_simple_converter()'")
BOOST_LOCALE_DECL base_converter* create_simple_converter_new_ptr(const std::string& encoding);
/// Install utf8 codecvt to UTF-16 or UTF-32 into locale \a in and return
/// new locale that is based on \a in and uses new facet.
BOOST_LOCALE_DECL
std::locale create_utf8_codecvt(const std::locale& in, char_facet_t type);
/// This function installs codecvt that can be used for conversion between single byte
/// character encodings like ISO-8859-1, koi8-r, windows-1255 and Unicode code points,
///
/// \throws boost::locale::conv::invalid_charset_error: Character set is not supported or isn't a single
/// byte character set
BOOST_LOCALE_DECL
std::locale create_simple_codecvt(const std::locale& in, const std::string& encoding, char_facet_t type);
} // namespace util
}} // namespace boost::locale
#endif
+78
View File
@@ -0,0 +1,78 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_UTIL_LOCALE_DATA_HPP
#define BOOST_LOCALE_UTIL_LOCALE_DATA_HPP
#include <boost/locale/config.hpp>
#include <string>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4251)
#endif
namespace boost { namespace locale { namespace util {
/// Holder and parser for locale names/identifiers
class BOOST_LOCALE_DECL locale_data {
std::string language_;
std::string country_;
std::string encoding_;
std::string variant_;
bool utf8_;
public:
/// Default to C locale with US-ASCII encoding
locale_data();
/// Construct from the parsed locale \see \ref parse
///
/// \throws std::invalid_argument: parsing failed
explicit locale_data(const std::string& locale_name);
/// Return language (usually 2 lowercase letters, i.e. ISO-639 or 'C')
const std::string& language() const { return language_; }
/// Return country (usually 2 uppercase letters, i.e. ISO-3166)
const std::string& country() const { return country_; }
/// Return encoding/codeset, e.g. ISO8859-1 or UTF-8
const std::string& encoding() const { return encoding_; }
/// Set encoding, will be made uppercase by default as-if it was parsed
/// Returns \c *this for chaining
locale_data& encoding(std::string new_encoding, bool uppercase = true);
/// Return variant/modifier, e.g. euro or stroke
const std::string& variant() const { return variant_; }
/// Return iff the encoding is UTF-8
bool is_utf8() const { return utf8_; }
/// Parse a locale identifier of the form `[language[_territory][.codeset][@modifier]]`
///
/// Allows a dash as the delimiter: `[language-territory]`
/// Return true if the identifier is valid:
/// - `language` is given and consists of ASCII letters
/// - `territory`, if given, consists of ASCII letters
/// - Any field started by a delimiter (`_`, `-`, `.`, `@`) is not empty
/// Otherwise parsing is aborted. Valid values already parsed stay set, other are defaulted.
bool parse(const std::string& locale_name);
/// Get a representation in the form `[language[_territory][.codeset][@modifier]]`
/// codeset is omitted if it is US-ASCII
std::string to_string() const;
private:
void reset();
bool parse_from_lang(const std::string& input);
bool parse_from_country(const std::string& input);
bool parse_from_encoding(const std::string& input);
bool parse_from_variant(const std::string& input);
};
}}} // namespace boost::locale::util
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif
+46
View File
@@ -0,0 +1,46 @@
//
// Copyright (c) 2022-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_LOCALE_UTIL_STRING_HPP
#define BOOST_LOCALE_UTIL_STRING_HPP
#include <boost/locale/config.hpp>
#include <limits>
namespace boost { namespace locale { namespace util {
/// Return the end of a C-string, i.e. the pointer to the trailing NULL byte
template<typename Char>
Char* str_end(Char* str)
{
while(*str)
++str;
return str;
}
inline constexpr bool is_upper_ascii(const char c)
{
return 'A' <= c && c <= 'Z';
}
inline constexpr bool is_lower_ascii(const char c)
{
return 'a' <= c && c <= 'z';
}
inline constexpr bool is_numeric_ascii(const char c)
{
return '0' <= c && c <= '9';
}
/// Cast an unsigned char to a (possibly signed) char avoiding implementation defined behavior
constexpr char to_char(unsigned char c)
{
return static_cast<char>((c - (std::numeric_limits<char>::min)()) + (std::numeric_limits<char>::min)());
}
}}} // namespace boost::locale::util
#endif