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.anystringview;
13 extern(C++):
14 
15 import qt.config;
16 import qt.core.bytearray;
17 import qt.core.global;
18 import qt.core.namespace;
19 import qt.core.qchar;
20 import qt.core.string;
21 import qt.core.stringview;
22 import qt.core.typeinfo;
23 import qt.core.utf8stringview;
24 import qt.helpers;
25 
26 /+ #ifdef __cpp_impl_three_way_comparison
27 #endif
28 
29 template <typename, typename> class QStringBuilder;
30 template <typename> struct QConcatenable; +/
31 
32 /// Binding for C++ class [QAnyStringView](https://doc.qt.io/qt-6/qanystringview.html).
33 @Q_PRIMITIVE_TYPE extern(C++, class) struct QAnyStringView
34 {
35 public:
36     alias difference_type = qptrdiff;
37     alias size_type = qsizetype;
38 private:
39     /+ template <typename Char> +/
40     /+ using if_compatible_char = std::enable_if_t<std::disjunction_v<
41         QtPrivate::IsCompatibleCharType<Char>,
42         QtPrivate::IsCompatibleChar8Type<Char>
43     >, bool>; +/
44 
45     /+ template <typename Pointer> +/
46     /+ using if_compatible_pointer = std::enable_if_t<std::disjunction_v<
47         QtPrivate::IsCompatiblePointer<Pointer>,
48         QtPrivate::IsCompatiblePointer8<Pointer>
49     >, bool>; +/
50 
51 
52     /+ template <typename T> +/
53     alias if_compatible_container(T) = /+ std:: +/enable_if_t!(bool, bool);
54 
55     // confirm we don't make an accidental copy constructor:
56 //    static assert(/+ QtPrivate:: +/qt.core.stringview.IsContainerCompatibleWithQStringView!(QAnyStringView).value == false);
57 //    static assert(/+ QtPrivate:: +/qt.core.utf8stringview.IsContainerCompatibleWithQUtf8StringView!(QAnyStringView).value == false);
58 
59     /+ template <typename Char> +/
60     static /+ std:: +/size_t encodeType(Char)(qsizetype sz)/+ noexcept+/
61     {
62         // only deals with Utf8 and Utf16 - there's only one way to create
63         // a Latin1 string, and that ctor deals with the tag itself
64         (mixin(Q_ASSERT(q{sz >= 0})));
65         (mixin(Q_ASSERT(q{sz <= qsizetype(SizeMask)})));
66         return /+ std:: +/size_t(sz) | uint(Char.sizeof == wchar.sizeof) * Tag.Utf16;
67     }
68 
69     /+ template <typename Char> +/
70     /+ static qsizetype lengthHelperPointer(const Char *str) noexcept
71     {
72 #if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) && !defined(Q_CC_INTEL)
73         if (__builtin_constant_p(*str)) {
74             qsizetype result = 0;
75             while (*str++ != '\0')
76                 ++result;
77             return result;
78         }
79 #endif
80         static if (sizeof(Char) == sizeof(char16_t))
81             return QtPrivate::qustrlen(reinterpret_cast<const char16_t*>(str));
82         else
83             return qsizetype(strlen(reinterpret_cast<const char*>(str)));
84     } +/
85 
86     /+ template <typename Container> +/
87     static qsizetype lengthHelperContainer(Container)(ref const(Container) c)/+ noexcept+/
88     {
89         return qsizetype(c.size());
90     }
91 
92     /+ template <typename Char, size_t N> +/
93     /+ static qsizetype lengthHelperContainer(Char,size_t N)(ref const(Char)[N] str)/+ noexcept+/
94     {
95         const it = /+ std:: +/char_traits!(Char).find(str.ptr, N, Char(0));
96         const end = it ? it : /+ std:: +/next(str, N);
97         return qsizetype(/+ std:: +/distance(str, end));
98     } +/
99 
100     static QChar toQChar(char ch)/+ noexcept+/ { return toQChar(QLatin1Char(ch)); } // we don't handle UTF-8 multibytes
101     static QChar toQChar(QChar ch)/+ noexcept+/ { return ch; }
102     static QChar toQChar(QLatin1Char ch)/+ noexcept+/ { return QChar(ch); }
103 
104 /+    /+ explicit +/ this(const(void)* d, qsizetype n, /+ std:: +/size_t sizeAndType)/+ noexcept+/
105     {
106         this.m_data = typeof(this.m_data)({d});
107         this.m_size = {/+ std:: +/size_t(n) | (sizeAndType & TypeMask)};
108     }+/
109 public:
110     @disable this();
111     /+this()/+ noexcept+/
112     {
113         this.m_data = typeof(this.m_data)({null});
114         this.m_size = {0};
115     }+/
116     this(typeof(null))/+ noexcept+/
117     {
118         //this();
119     }
120 
121     /+ template <typename Char, if_compatible_char<Char> = true> +/
122     this(Char)(const(Char)* str, qsizetype len) if(is(const(Char) == const(wchar)) || is(const(Char) == const(char)) || is(const(Char) == const(QChar)))
123     {
124         this.m_data = typeof(this.m_data)(str);
125         (mixin(Q_ASSERT(q{len >= 0})));
126         (mixin(Q_ASSERT(q{str || !len})));
127         this.m_size = encodeType!(Char)(len);
128     }
129 
130     /+ template <typename Char, if_compatible_char<Char> = true> +/
131     this(Char,)(const(Char)* f, const(Char)* l)
132     {
133         this(f, l - f);
134     }
135 
136 /+ #ifdef Q_CLANG_QDOC
137     template <typename Char, size_t N>
138     constexpr QAnyStringView(const Char (&array)[N]) noexcept;
139 
140     template <typename Char>
141     constexpr QAnyStringView(const Char *str) noexcept;
142 #else +/
143 
144     /+ template <typename Pointer, if_compatible_pointer<Pointer> = true> +/
145     this(Pointer,)(ref const(Pointer) str)/+ noexcept+/
146     {
147         this(QAnyStringView(str, str ? lengthHelperPointer(str) : 0));
148     }
149 /+ #endif +/
150 
151     // defined in qstring.h
152     /+pragma(inline, true) this(ref const(QByteArray) str)/+ noexcept+/
153     {
154         this(QAnyStringView(str.isNull() ? null : str.data(), str.size()));
155     }+/ // TODO: Should we have this at all? Remove?
156     pragma(inline, true) this(ref const(QString) str)/+ noexcept+/
157     {
158         this(str.isNull() ? null : str.data(), str.size());
159     }
160 /+    pragma(inline, true) this(QLatin1String str)/+ noexcept+/
161     {
162         this.m_data = typeof(this.m_data)({str.data()});
163         this.m_size = {size_t(str.size()) | Tag.Latin1};
164     }+/
165 
166     // defined in qstringbuilder.h
167     /+ template <typename A, typename B> +/
168     /+ inline QAnyStringView(const QStringBuilder<A, B> &expr,
169                           typename QConcatenable<QStringBuilder<A, B>>::ConvertTo &&capacity = {}); +/
170 
171     /+ template <typename Container, if_compatible_container<Container> = true> +/
172     this(Container,)(ref const(Container) c)/+ noexcept+/
173     {
174         this(/+ std:: +/data(c), lengthHelperContainer(c));
175     }
176 
177     /+ template <typename Char, if_compatible_char<Char> = true> +/
178     this(Char,)(ref const(Char) c)/+ noexcept+/
179     {
180         this(&c, 1);
181     }
182     this(ref const(QChar) c)/+ noexcept+/
183     {
184         this(&c, 1);
185     }
186 
187     /+ template <typename Char, typename Container = decltype(QChar::fromUcs4(U'x')),
188               std::enable_if_t<std::is_same_v<Char, char32_t>, bool> = true> +/
189     /+ constexpr QAnyStringView(Char c, Container &&capacity = {})
190         : QAnyStringView(capacity = QChar::fromUcs4(c)) {} +/
191 
192     this(QStringView v)/+ noexcept+/
193     {
194         this(v.data(), lengthHelperContainer(v));
195     }
196 
197     /+ template <bool UseChar8T> +/
198     this(bool UseChar8T)(QBasicUtf8StringView!(UseChar8T) v)/+ noexcept+/
199     {
200         this(v.data(), lengthHelperContainer(v));
201     }
202 
203     /+ template <typename Char, size_t Size, if_compatible_char<Char> = true> +/
204     /+ [[nodiscard]] +/ static QAnyStringView fromArray(Char,size_t Size,)(ref const(Char)[Size] string)/+ noexcept+/
205     { return QAnyStringView(string, Size); }
206 
207     // defined in qstring.h:
208     /+ template <typename Visitor> +/
209     /+ inline constexpr decltype(auto) visit(Visitor &&v) const; +/
210 
211     /+ [[nodiscard]] +/ pragma(inline, true) QString toString() const
212     {
213         import qt.core.stringalgorithms;
214         return /+ QtPrivate:: +/qt.core.stringalgorithms.convertToQString(this);
215     } // defined in qstring.h
216 
217     /+ [[nodiscard]] +/ qsizetype size() const/+ noexcept+/ { return qsizetype(m_size & SizeMask); }
218     /+ [[nodiscard]] +/ const(void)* data() const/+ noexcept+/ { return m_data; }
219 
220     /+ [[nodiscard]] +/ /+ Q_CORE_EXPORT +/ static int compare(QAnyStringView lhs, QAnyStringView rhs, /+ Qt:: +/qt.core.namespace.CaseSensitivity cs = /+ Qt:: +/qt.core.namespace.CaseSensitivity.CaseSensitive)/+ noexcept+/;
221     /+ [[nodiscard]] +/ /+ Q_CORE_EXPORT +/ static bool equal(QAnyStringView lhs, QAnyStringView rhs)/+ noexcept+/;
222 
223     //
224     // STL compatibility API:
225     //
226 /+    /+ [[nodiscard]] +/ QChar front() const
227     {
228         return visit([] (auto that) { return QAnyStringView.toQChar(that.front()); });
229     } // NOT noexcept!
230     /+ [[nodiscard]] +/ QChar back() const
231     {
232         return visit([] (auto that) { return QAnyStringView.toQChar(that.back()); });
233     } +/ // NOT noexcept!
234     /+ [[nodiscard]] +/ bool empty() const/+ noexcept+/ { return size() == 0; }
235     /+ [[nodiscard]] +/ qsizetype size_bytes() const/+ noexcept+/
236     { return size() * charSize(); }
237 
238     //
239     // Qt compatibility API:
240     //
241     /+ [[nodiscard]] +/ bool isNull() const/+ noexcept+/ { return !m_data; }
242     /+ [[nodiscard]] +/ bool isEmpty() const/+ noexcept+/ { return empty(); }
243     /+ [[nodiscard]] +/ qsizetype length() const/+ noexcept+/
244     { return size(); }
245 
246 private:
247     /+ [[nodiscard]] friend inline bool operator==(QAnyStringView lhs, QAnyStringView rhs) noexcept
248     { return QAnyStringView::equal(lhs, rhs); } +/
249     /+ [[nodiscard]] friend inline bool operator!=(QAnyStringView lhs, QAnyStringView rhs) noexcept
250     { return !QAnyStringView::equal(lhs, rhs); } +/
251 
252 /+ #if defined(__cpp_impl_three_way_comparison) && !defined(Q_QDOC)
253     [[nodiscard]] friend inline auto operator<=>(QAnyStringView lhs, QAnyStringView rhs) noexcept
254     { return QAnyStringView::compare(lhs, rhs) <=> 0; }
255 #else +/
256     /+ [[nodiscard]] friend inline bool operator<=(QAnyStringView lhs, QAnyStringView rhs) noexcept
257     { return QAnyStringView::compare(lhs, rhs) <= 0; } +/
258     /+ [[nodiscard]] friend inline bool operator>=(QAnyStringView lhs, QAnyStringView rhs) noexcept
259     { return QAnyStringView::compare(lhs, rhs) >= 0; } +/
260     /+ [[nodiscard]] friend inline bool operator<(QAnyStringView lhs, QAnyStringView rhs) noexcept
261     { return QAnyStringView::compare(lhs, rhs) < 0; } +/
262     /+ [[nodiscard]] friend inline bool operator>(QAnyStringView lhs, QAnyStringView rhs) noexcept
263     { return QAnyStringView::compare(lhs, rhs) > 0; } +/
264 /+ #endif +/
265 
266     // TODO: Optimize by inverting and storing the flags in the low bits and
267     //       the size in the high.
268     //static assert(/+ std:: +/is_same_v!(/+ std:: +/size_t, size_t));
269     static assert(size_t.sizeof == qsizetype.sizeof);
270     extern(D) static immutable size_t SizeMask = size_t.max / 4;
271     extern(D) static immutable size_t Latin1Flag = SizeMask + 1;
272     extern(D) static immutable size_t TwoByteCodePointFlag = Latin1Flag << 1;
273     extern(D) static immutable size_t TypeMask = size_t.max & ~SizeMask;
274     static assert(TypeMask == (Latin1Flag|TwoByteCodePointFlag));
275     // HI HI LO LO ...
276     //  0  0 SZ SZ  Utf8
277     //  0  1 SZ SZ  Latin1
278     //  1  0 SZ SZ  Utf16
279     //  1  1 SZ SZ  Unused
280     //  ^  ^ latin1
281     //  | sizeof code-point == 2
282     enum Tag : size_t {
283         Utf8     = 0,
284         Latin1   = Latin1Flag,
285         Utf16    = TwoByteCodePointFlag,
286         Unused   = TypeMask,
287     }
288     /+ [[nodiscard]] +/ Tag tag() const/+ noexcept+/ { return cast(Tag)(m_size & TypeMask); }
289     /+ [[nodiscard]] +/ bool isUtf16() const/+ noexcept+/ { return tag() == Tag.Utf16; }
290     /+ [[nodiscard]] +/ bool isUtf8() const/+ noexcept+/ { return tag() == Tag.Utf8; }
291     /+ [[nodiscard]] +/ bool isLatin1() const/+ noexcept+/ { return tag() == Tag.Latin1; }
292     /+ [[nodiscard]] +/ QStringView asStringView() const
293     { return (){ (mixin(Q_ASSERT(q{QAnyStringView.isUtf16()})));
294 return QStringView(m_data_utf16, size());
295 }(); }
296     /+ [[nodiscard]] +/ /+ q_no_char8_t:: +/QUtf8StringView asUtf8StringView() const
297     { return (){ (mixin(Q_ASSERT(q{QAnyStringView.isUtf8()})));
298 return /+ q_no_char8_t:: +/qt.core.utf8stringview.QUtf8StringView(m_data_utf8, size());
299 }(); }
300     /+ [[nodiscard]] +/ pragma(inline, true) QLatin1String asLatin1StringView() const
301     {
302         (mixin(Q_ASSERT(q{QAnyStringView.isLatin1()})));
303         return QLatin1String(m_data_utf8, cast(int)(size()));
304     }
305     /+ [[nodiscard]] +/ size_t charSize() const/+ noexcept+/ { return isUtf16() ? 2 : 1; }
306     /+ Q_ALWAYS_INLINE +/ pragma(inline, true) void verify(qsizetype pos, qsizetype n = 0) const
307     {
308         (mixin(Q_ASSERT(q{pos >= 0})));
309         (mixin(Q_ASSERT(q{pos <= QAnyStringView.size()})));
310         (mixin(Q_ASSERT(q{n >= 0})));
311         (mixin(Q_ASSERT(q{n <= QAnyStringView.size() - pos})));
312     }
313     union {
314         const(void)* m_data;
315         const(char)* m_data_utf8;
316         const(wchar)* m_data_utf16;
317     }
318     size_t m_size;
319     mixin(CREATE_CONVENIENCE_WRAPPERS);
320 }
321 /+ Q_DECLARE_TYPEINFO(QAnyStringView, Q_PRIMITIVE_TYPE); +/
322 
323 /+ [[nodiscard]] +/ pragma(inline, true) QAnyStringView qToAnyStringViewIgnoringNull(QStringLike, /+ std::enable_if_t<std::disjunction_v<
324         std::is_same<QStringLike, QString>,
325         std::is_same<QStringLike, QByteArray>
326     >, bool> +/ /+ = true +/)(ref const(QStringLike) s)/+ noexcept+/
327 { return QAnyStringView(s.data(), s.size()); }
328