1 /*
2  * DQt - D bindings for the Qt Toolkit
3  *
4  * GNU Lesser General Public License Usage
5  * This file may be used under the terms of the GNU Lesser
6  * General Public License version 3 as published by the Free Software
7  * Foundation and appearing in the file LICENSE.LGPL3 included in the
8  * packaging of this file. Please review the following information to
9  * ensure the GNU Lesser General Public License version 3 requirements
10  * will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
11  */
12 module qt.core.utf8stringview;
13 extern(C++):
14 
15 import qt.config;
16 import qt.core.bytearray;
17 import qt.core.global;
18 import qt.core.string;
19 import qt.helpers;
20 
21 
22 extern(C++, "QtPrivate") {
23 /+alias IsCompatibleChar8TypeHelper(Char) = /+ std:: +/
24     disjunction!(
25 /+ #ifdef __cpp_char8_t +/
26         /+ std:: +/is_same!(Char, char8_t),
27 /+ #endif +/
28         /+ std:: +/is_same!(Char, char),
29         /+ std:: +/is_same!(Char, uchar),
30         /+ std:: +/is_same!(Char, byte));
31 alias IsCompatibleChar8Type(Char)
32     = IsCompatibleChar8TypeHelper!(/+ std:: +/remove_cv_t!(/+ std:: +/remove_reference_t!(Char)));
33 
34 struct IsCompatiblePointer8Helper(Pointer) {
35     /+ std:: +/false_type base0;
36     alias base0 this;
37 }
38 /+ template <typename Char>
39 struct IsCompatiblePointer8Helper<Char*>
40     : IsCompatibleChar8Type<Char> {}; +/
41 alias IsCompatiblePointer8(Pointer)
42     = IsCompatiblePointer8Helper!(/+ std:: +/remove_cv_t!(/+ std:: +/remove_reference_t!(Pointer)));
43 +/
44 struct IsContainerCompatibleWithQUtf8StringView(T, Enable) {
45     /+ std:: +/false_type base0;
46     alias base0 this;
47 }
48 
49 /+ template <typename T>
50 struct IsContainerCompatibleWithQUtf8StringView<T, std::enable_if_t<std::conjunction_v<
51         // lacking concepts and ranges, we accept any T whose std::data yields a suitable pointer ...
52         IsCompatiblePointer8<decltype(std::data(std::declval<const T &>()))>,
53         // ... and that has a suitable size ...
54         std::is_convertible<
55             decltype(std::size(std::declval<const T &>())),
56             qsizetype
57         >,
58         // ... and it's a range as it defines an iterator-like API
59         IsCompatibleChar8Type<typename std::iterator_traits<
60             decltype(std::begin(std::declval<const T &>()))>::value_type
61         >,
62         std::is_convertible<
63             decltype( std::begin(std::declval<const T &>()) != std::end(std::declval<const T &>()) ),
64             bool
65         >,
66 
67         // This needs to be treated specially due to the empty vs null distinction
68         std::negation<std::is_same<std::decay_t<T>, QByteArray>>,
69 
70         // This has a compatible value_type, but explicitly a different encoding
71         std::negation<std::is_same<std::decay_t<T>, QLatin1String>>,
72 
73         // Don't make an accidental copy constructor
74         std::negation<std::disjunction<
75             std::is_same<std::decay_t<T>, QBasicUtf8StringView<true>>,
76             std::is_same<std::decay_t<T>, QBasicUtf8StringView<false>>
77         >>
78     >>> : std::true_type {}; +/
79 
80 struct hide_char8_t {
81 /+ #ifdef __cpp_char8_t +/
82     alias type = char;
83 /+ #endif +/
84 }
85 
86 struct wrap_char { alias type = char; }
87 
88 } // namespace QtPrivate
89 
90 /+ #ifdef Q_CLANG_QDOC +/
91 static if(false)
92 {
93 /+ #define QBasicUtf8StringView QUtf8StringView +/
94 }
95 /+ #else +/
96 /+ #endif +/
97 extern(C++, class) struct QBasicUtf8StringView(bool UseChar8T)
98 {
99 public:
100 /+ #ifndef Q_CLANG_QDOC +/
101 /+    alias storage_type = /+ typename std::conditional<UseChar8T,
102                 QtPrivate::hide_char8_t,
103                 QtPrivate::wrap_char
104             >::type::type +/UnknownType!q{};+/
105     alias storage_type = char;
106 /+ #else
107     using storage_type = typename QtPrivate::hide_char8_t;
108 #endif +/
109     alias value_type = const(storage_type);
110     alias difference_type = qptrdiff;
111     alias size_type = qsizetype;
112     /+ typedef value_type &reference; +/
113     /+ typedef value_type &const_reference; +/
114     alias pointer = value_type*;
115     alias const_pointer = value_type*;
116 
117     alias iterator = pointer;
118     alias const_iterator = const_pointer;
119     /+ typedef std::reverse_iterator<iterator> reverse_iterator; +/
120     /+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator; +/
121 
122 private:
123     /+ template <typename Char> +/
124     /+ using if_compatible_char = std::enable_if_t<QtPrivate::IsCompatibleChar8Type<Char>::value, bool>; +/
125 
126     /+ template <typename Pointer> +/
127     /+ using if_compatible_pointer = std::enable_if_t<QtPrivate::IsCompatiblePointer8<Pointer>::value, bool>; +/
128 
129     /+ template <typename T> +/
130     /+ using if_compatible_qstring_like = std::enable_if_t<std::is_same_v<T, QByteArray>, bool>; +/
131 
132     /+ template <typename T> +/
133     alias if_compatible_container(T) = /+ std:: +/enable_if_t!(/+ QtPrivate:: +/IsContainerCompatibleWithQUtf8StringView!(T).value, bool);
134 
135     /+ template <typename Container> +/
136     static qsizetype lengthHelperContainer(Container)(ref const(Container) c)/+ noexcept+/
137     {
138         return qsizetype(/+ std:: +/size(c));
139     }
140 
141     // Note: Do not replace with std::size(const Char (&)[N]), cause the result
142     // will be of by one.
143     /+ template <typename Char, size_t N> +/
144     static qsizetype lengthHelperContainer(Char,size_t N)(ref const(Char)[N] str)/+ noexcept+/
145     {
146         const it = /+ std:: +/char_traits!(Char).find(str.ptr, N, Char(0));
147         const end = it ? it : /+ std:: +/next(str, N);
148         return qsizetype(/+ std:: +/distance(str, end));
149     }
150 
151     /+ template <typename Char> +/
152     static const(storage_type)* castHelper(Char)(const(Char)* str)/+ noexcept+/
153     { return reinterpret_cast!(const(storage_type)*)(str); }
154     static const(storage_type)* castHelper(const(storage_type)* str)/+ noexcept+/
155     { return str; }
156 
157 public:
158     @disable this();
159     /+this()/+ noexcept+/
160     {
161         this.m_data = null;
162         this.m_size = 0;
163     }+/
164     this(typeof(null))/+ noexcept+/
165     {
166     }
167 
168     /+ template <typename Char, if_compatible_char<Char> = true> +/
169     this(Char,)(const(Char)* str, qsizetype len)
170     {
171         this.m_data = castHelper(str);
172         this.m_size = ((){(){ (mixin(Q_ASSERT(q{len >= 0})));
173         return /+ Q_ASSERT(str || !len) +/ mixin(Q_ASSERT(q{str || !len}));
174         }();
175         return len;
176         }());
177     }
178 
179     /+ template <typename Char, if_compatible_char<Char> = true> +/
180     this(Char,)(const(Char)* f, const(Char)* l)
181     {
182         this(f, l - f);
183     }
184 
185 /+ #ifdef Q_CLANG_QDOC
186     template <typename Char, size_t N>
187     constexpr QBasicUtf8StringView(const Char (&array)[N]) noexcept;
188 
189     template <typename Char>
190     constexpr QBasicUtf8StringView(const Char *str) noexcept;
191 #else +/
192     /+ template <typename Pointer, if_compatible_pointer<Pointer> = true> +/
193     this(Pointer,)(ref const(Pointer) str)/+ noexcept+/
194     {
195         this(str,
196                     str ? /+ std:: +/char_traits!(/+ std:: +/remove_cv_t!(/+ std:: +/remove_pointer_t!(Pointer))).length(str) : 0);
197     }
198 /+ #endif
199 
200 #ifdef Q_CLANG_QDOC
201     QBasicUtf8StringView(const QByteArray &str) noexcept;
202 #else +/
203     /+ template <typename String, if_compatible_qstring_like<String> = true> +/
204     this(String,)(ref const(String) str)/+ noexcept+/
205     {
206         this(str.isNull() ? null : str.data(), qsizetype(str.size()));
207     }
208 /+ #endif +/
209 
210     /+ template <typename Container, if_compatible_container<Container> = true> +/
211     this(Container,)(ref const(Container) c)/+ noexcept+/
212     {
213         this(/+ std:: +/data(c), lengthHelperContainer(c));
214     }
215 
216 /+ #ifdef __cpp_char8_t +/
217     this(QBasicUtf8StringView!(!UseChar8T) other)
218     {
219         this(other.data(), other.size());
220     }
221 /+ #endif +/
222 
223     /+ template <typename Char, size_t Size, if_compatible_char<Char> = true> +/
224     /+ [[nodiscard]] +/ static QBasicUtf8StringView fromArray(Char,size_t Size,)(ref const(Char)[Size] string)/+ noexcept+/
225     { return QBasicUtf8StringView(string.ptr, Size); }
226 
227     /+ [[nodiscard]] +/ pragma(inline, true) QString toString() const
228     {
229         (mixin(Q_ASSERT(q{QBasicUtf8StringView.size() == cast(int)(QBasicUtf8StringView.size())})));
230         return QString.fromUtf8(data(), cast(int)(size()));
231     } // defined in qstring.h
232 
233     /+ [[nodiscard]] +/ qsizetype size() const/+ noexcept+/ { return m_size; }
234     /+ [[nodiscard]] +/ const_pointer data() const/+ noexcept+/ { return reinterpret_cast!(const_pointer)(m_data); }
235 /+ #if defined(__cpp_char8_t) || defined(Q_CLANG_QDOC) +/
236     /+ [[nodiscard]] +/ const(char)* utf8() const/+ noexcept+/ { return reinterpret_cast!(const(char)*)(m_data); }
237 /+ #endif +/
238 
239     /+ [[nodiscard]] +/ storage_type opIndex(qsizetype n) const
240     { return (){(){ (mixin(Q_ASSERT(q{n >= 0})));
241 return /+ Q_ASSERT(n < size()) +/ mixin(Q_ASSERT(q{n < QBasicUtf8StringView.size()}));
242 }();
243 return m_data[n];
244 }(); }
245 
246     //
247     // QString API
248     //
249 
250     /+ [[nodiscard]] +/ storage_type at(qsizetype n) const { return (this)[n]; }
251 
252 /+    /+ [[nodiscard]] +/
253         QBasicUtf8StringView mid(qsizetype pos, qsizetype n = -1) const
254     {
255         //using namespace QtPrivate;
256         auto result = QContainerImplHelper.mid(size(), &pos, &n);
257         return result == QContainerImplHelper.Null ? QBasicUtf8StringView() : QBasicUtf8StringView(m_data + pos, n);
258     }+/
259     /+ [[nodiscard]] +/
260         QBasicUtf8StringView left(qsizetype n) const
261     {
262         if (size_t(n) >= size_t(size()))
263             n = size();
264         return QBasicUtf8StringView(m_data, n);
265     }
266     /+ [[nodiscard]] +/
267         QBasicUtf8StringView right(qsizetype n) const
268     {
269         if (size_t(n) >= size_t(size()))
270             n = size();
271         return QBasicUtf8StringView(m_data + m_size - n, n);
272     }
273 
274     /+ [[nodiscard]] +/ QBasicUtf8StringView sliced(qsizetype pos) const
275     { verify(pos); return QBasicUtf8StringView(m_data + pos, m_size - pos); }
276     /+ [[nodiscard]] +/ QBasicUtf8StringView sliced(qsizetype pos, qsizetype n) const
277     { verify(pos, n); return QBasicUtf8StringView(m_data + pos, n); }
278     /+ [[nodiscard]] +/ QBasicUtf8StringView first(qsizetype n) const
279     { verify(n); return QBasicUtf8StringView(m_data, n); }
280     /+ [[nodiscard]] +/ QBasicUtf8StringView last(qsizetype n) const
281     { verify(n); return QBasicUtf8StringView(m_data + m_size - n, n); }
282     /+ [[nodiscard]] +/ QBasicUtf8StringView chopped(qsizetype n) const
283     { verify(n); return QBasicUtf8StringView(m_data, m_size - n); }
284 
285     void truncate(qsizetype n)
286     { verify(n); m_size = n; }
287     void chop(qsizetype n)
288     { verify(n); m_size -= n; }
289 
290     //
291     // STL compatibility API:
292     //
293     /+ [[nodiscard]] +/ const_iterator begin()   const/+ noexcept+/ { return data(); }
294     /+ [[nodiscard]] +/ const_iterator end()     const/+ noexcept+/ { return data() + size(); }
295     /+ [[nodiscard]] +/ const_iterator cbegin()  const/+ noexcept+/ { return begin(); }
296     /+ [[nodiscard]] +/ const_iterator cend()    const/+ noexcept+/ { return end(); }
297     /+ [[nodiscard]] const_reverse_iterator rbegin()  const noexcept { return const_reverse_iterator(end()); } +/
298     /+ [[nodiscard]] const_reverse_iterator rend()    const noexcept { return const_reverse_iterator(begin()); } +/
299     /+ [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return rbegin(); } +/
300     /+ [[nodiscard]] const_reverse_iterator crend()   const noexcept { return rend(); } +/
301 
302     /+ [[nodiscard]] +/ bool empty() const/+ noexcept+/ { return size() == 0; }
303     /+ [[nodiscard]] +/ storage_type front() const { return (){ (mixin(Q_ASSERT(q{!QBasicUtf8StringView.empty()})));
304 return m_data[0];
305 }(); }
306     /+ [[nodiscard]] +/ storage_type back()  const { return (){ (mixin(Q_ASSERT(q{!QBasicUtf8StringView.empty()})));
307 return m_data[m_size - 1];
308 }(); }
309 
310     //
311     // Qt compatibility API:
312     //
313     /+ [[nodiscard]] +/ bool isNull() const/+ noexcept+/ { return !m_data; }
314     /+ [[nodiscard]] +/ bool isEmpty() const/+ noexcept+/ { return empty(); }
315     /+ [[nodiscard]] +/ qsizetype length() const/+ noexcept+/
316     { return size(); }
317 
318 private:
319     /+ /+ [[nodiscard]] +/ pragma(inline, true) static int compare(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs)/+ noexcept+/
320     {
321         import qt.core.stringalgorithms;
322 
323         return /+ QtPrivate:: +/qt.core.stringalgorithms.compareStrings(QBasicUtf8StringView!(false)(lhs.data(),lhs.size()),QBasicUtf8StringView!(false)(rhs.data(),rhs.size()));
324     }+/
325 
326     /+ [[nodiscard]] friend inline bool operator==(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
327     {
328         return lhs.size() == rhs.size()
329                && QtPrivate::equalStrings(QBasicUtf8StringView<false>(lhs.data(), lhs.size()),
330                                           QBasicUtf8StringView<false>(rhs.data(), rhs.size()));
331     } +/
332     /+ [[nodiscard]] friend inline bool operator!=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
333     { return !operator==(lhs, rhs); } +/
334 
335 /+ #ifdef __cpp_impl_three_way_comparison
336     [[nodiscard]] friend inline auto operator<=>(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
337     { return QBasicUtf8StringView::compare(lhs, rhs) <=> 0; }
338 #else +/
339     /+ [[nodiscard]] friend inline bool operator<=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
340     { return QBasicUtf8StringView::compare(lhs, rhs) <= 0; } +/
341     /+ [[nodiscard]] friend inline bool operator>=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
342     { return QBasicUtf8StringView::compare(lhs, rhs) >= 0; } +/
343     /+ [[nodiscard]] friend inline bool operator<(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
344     { return QBasicUtf8StringView::compare(lhs, rhs) < 0; } +/
345     /+ [[nodiscard]] friend inline bool operator>(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
346     { return QBasicUtf8StringView::compare(lhs, rhs) > 0; } +/
347 /+ #endif +/
348 
349     /+ Q_ALWAYS_INLINE +/ pragma(inline, true) void verify(qsizetype pos, qsizetype n = 0) const
350     {
351         (mixin(Q_ASSERT(q{pos >= 0})));
352         (mixin(Q_ASSERT(q{pos <= QBasicUtf8StringView.size()})));
353         (mixin(Q_ASSERT(q{n >= 0})));
354         (mixin(Q_ASSERT(q{n <= QBasicUtf8StringView.size() - pos})));
355     }
356     const(storage_type)* m_data = null;
357     qsizetype m_size = 0;
358 }
359 
360 /+ #ifdef Q_CLANG_QDOC
361 #undef QBasicUtf8StringView
362 #else +/
363 /+ inline +/ extern(C++,"q_no_char8_t"){
364 /+ template <bool UseChar8T>
365 Q_DECLARE_TYPEINFO_BODY(QBasicUtf8StringView<UseChar8T>, Q_PRIMITIVE_TYPE);
366 
367 // ### Qt 7: remove the non-char8_t version of QUtf8StringView
368 QT_BEGIN_NO_CHAR8_T_NAMESPACE +/
369 //alias QUtf8StringView = QBasicUtf8StringView!(false);
370 }
371 /+ QT_END_NO_CHAR8_T_NAMESPACE +/
372 extern(C++,"q_has_char8_t"){
373 
374 /+ QT_BEGIN_HAS_CHAR8_T_NAMESPACE +/
375 alias QUtf8StringView = QBasicUtf8StringView!(true);
376 }
377 /+ QT_END_HAS_CHAR8_T_NAMESPACE +/
378 /+ #endif +/ // Q_CLANG_QDOC
379 
380 /+ [[nodiscard]] +/ pragma(inline, true) /+ q_no_char8_t:: +/QUtf8StringView qToUtf8StringViewIgnoringNull(QStringLike, /+ std::enable_if_t<std::is_same_v<QStringLike, QByteArray>, bool> +/ /+ = true +/)(ref const(QStringLike) s)/+ noexcept+/
381 { return /+ q_no_char8_t:: +/QUtf8StringView(s.data(), s.size()); }
382