dius 0.1.0
Loading...
Searching...
No Matches
grapheme_cluster.h
Go to the documentation of this file.
1#pragma once
2
3#include "di/container/concepts/prelude.h"
4#include "di/container/meta/prelude.h"
5#include "di/container/view/view_interface.h"
6#include "di/types/prelude.h"
7
8namespace dius::unicode {
10public:
19 auto is_boundary(c32 code_point) -> bool;
20
21private:
22 // Opaque internal state.
23 u8 m_state { 0 };
24};
25
27template<di::concepts::InputContainer View>
28requires(di::concepts::View<View> && di::concepts::ContainerOf<View, c32>)
30private:
31 friend struct Sentinel;
32 friend struct Iterator;
33
34 using Value = di::meta::Reconstructed<View, di::meta::ContainerIterator<View>, di::meta::ContainerIterator<View>>;
35
36 struct Iterator
37 : public di::container::IteratorBase<Iterator, di::ForwardIteratorTag, Value,
38 di::meta::ContainerSSizeType<View>> {
39 public:
40 Iterator() = default;
41
42 constexpr Iterator(GraphemeClusterView& parent, di::meta::ContainerIterator<View> base, Value next)
43 : m_parent(di::addressof(parent)), m_base(di::move(base)), m_next(di::move(next)) {}
44
45 constexpr auto base() const { return m_base; }
46
47 constexpr auto operator*() const -> Value {
48 return di::reconstruct(di::in_place_type<View>, m_base, di::end(m_next));
49 }
50
51 constexpr void advance_one() {
52 m_base = di::begin(m_next);
53 if (m_base != di::end(m_parent->m_base)) {
54 m_base = di::end(m_next);
55 if (m_base == di::end(m_parent->m_base)) {
56 m_next = di::reconstruct(di::in_place_type<View>, m_base, m_base);
57 } else {
58 m_next = m_parent->find_next(m_base);
59 }
60 }
61 }
62
63 private:
64 friend struct Sentinel;
65
66 constexpr friend auto operator==(Iterator const& a, Iterator const& b) -> bool { return a.m_base == b.m_base; }
67
68 GraphemeClusterView* m_parent { nullptr };
69 di::meta::ContainerIterator<View> m_base;
70 Value m_next;
71 };
72
73 struct Sentinel {
74 public:
75 Sentinel() = default;
76
77 constexpr explicit Sentinel(GraphemeClusterView& parent) : m_base(di::end(parent.m_base)) {}
78
79 private:
80 constexpr friend auto operator==(Iterator const& a, Sentinel const& b) -> bool { return a.m_base == b.m_base; }
81
82 di::meta::ContainerSentinel<View> m_base;
83 };
84
85public:
87 requires(di::concepts::DefaultInitializable<View>)
88 = default;
89
90 constexpr GraphemeClusterView(View base) : m_base(di::move(base)) {}
91
92 constexpr auto base() const& -> View
93 requires(di::concepts::CopyConstructible<View>)
94 {
95 return m_base;
96 }
97 constexpr auto base() && -> View { return di::move(m_base); }
98
99 constexpr auto begin() -> Iterator {
100 // We need a cache to ensure begin() is O(1). Grapheme clusters
101 // can be arbitrarily long.
102 if (!m_cached_begin) {
103 m_cached_begin = this->find_next(di::begin(m_base));
104 }
105 return { *this, di::begin(m_base), *m_cached_begin };
106 }
107
108 constexpr auto end() {
109 if constexpr (di::concepts::CommonContainer<View>) {
110 return Iterator { *this, di::end(m_base),
111 di::reconstruct(di::in_place_type<View>, di::end(m_base), di::end(m_base)) };
112 } else {
113 return Sentinel { *this };
114 }
115 }
116
117private:
118 constexpr auto find_next(di::meta::ContainerIterator<View> it) -> Value {
119 auto start = it;
120 auto last = it;
121 auto base_end = di::end(m_base);
122 if (last != base_end) {
123 // Prime the grapheme clusterer with the first code point, which
124 // should always be a boundary.
125 auto clusterer = GraphemeClusterer {};
126 clusterer.is_boundary(*last);
127 ++last;
128 while (last != base_end && !clusterer.is_boundary(*last)) {
129 ++last;
130 }
131 }
132 return di::reconstruct(di::in_place_type<View>, di::move(start), di::move(last));
133 }
134
135 View m_base;
136 di::Optional<Value> m_cached_begin;
137};
138
139template<typename Con>
141
142namespace detail {
143 struct GraphemeClusterViewFunction : di::function::pipeline::EnablePipeline {
144 template<typename Con>
145 requires(requires(Con&& con) { GraphemeClusterView(di::forward<Con>(con)); })
146 constexpr static auto operator()(Con&& con) {
147 return GraphemeClusterView(di::forward<Con>(con));
148 ;
149 }
150 };
151}
152
154}
View adaptor to segment a Unicode string into grapheme clusters.
Definition grapheme_cluster.h:29
constexpr auto base() &&-> View
Definition grapheme_cluster.h:97
constexpr auto base() const &-> View requires(di::concepts::CopyConstructible< View >)
Definition grapheme_cluster.h:92
constexpr auto begin() -> Iterator
Definition grapheme_cluster.h:99
constexpr auto end()
Definition grapheme_cluster.h:108
constexpr GraphemeClusterView(View base)
Definition grapheme_cluster.h:90
Definition grapheme_cluster.h:9
auto is_boundary(c32 code_point) -> bool
Determine if there is a grapheme boundary between the previous character.
Definition error.h:7
Definition default_ignorable_code_point.h:19
Definition default_ignorable_code_point.h:8
GraphemeClusterView(Con &&) -> GraphemeClusterView< di::meta::AsView< Con > >
constexpr auto grapheme_clusters
Definition grapheme_cluster.h:153