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