From 9ba83cf7a91c04c0213ecb7b5717c2d3e3ccaad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= Date: Sun, 3 Jun 2018 10:38:38 +0300 Subject: [PATCH] libcore: Started removal of Qt dependencies --- doomsday/apps/api/dd_version.h.in | 3 +- doomsday/cmake/FindCPlus.cmake | 31 + doomsday/libs/core/CMakeLists.txt | 4 +- doomsday/libs/core/include/de/List | 1 + doomsday/libs/core/include/de/RegExp | 1 + doomsday/libs/core/include/de/data/block.h | 25 +- doomsday/libs/core/include/de/data/list.h | 72 ++ doomsday/libs/core/include/de/data/regexp.h | 50 ++ doomsday/libs/core/include/de/data/string.h | 330 ++++++--- .../libs/core/include/de/data/stringlist.h | 62 ++ doomsday/libs/core/include/de/libcore.h | 125 ++-- doomsday/libs/core/src/core/logtextstyle.h | 18 +- doomsday/libs/core/src/data/block.cpp | 48 +- doomsday/libs/core/src/data/string.cpp | 648 +++++++++--------- doomsday/libs/core/src/error.cpp | 20 +- 15 files changed, 876 insertions(+), 562 deletions(-) create mode 100644 doomsday/cmake/FindCPlus.cmake create mode 100644 doomsday/libs/core/include/de/List create mode 100644 doomsday/libs/core/include/de/RegExp create mode 100644 doomsday/libs/core/include/de/data/list.h create mode 100644 doomsday/libs/core/include/de/data/regexp.h create mode 100644 doomsday/libs/core/include/de/data/stringlist.h diff --git a/doomsday/apps/api/dd_version.h.in b/doomsday/apps/api/dd_version.h.in index 3bf1876afd..9e29128009 100644 --- a/doomsday/apps/api/dd_version.h.in +++ b/doomsday/apps/api/dd_version.h.in @@ -32,8 +32,7 @@ extern "C" { #define DOOMSDAY_NICENAME "Doomsday Engine" #define DOOMSDAY_HOMEURL "http://dengine.net" -#define DOOMSDAY_MASTERURL "http://dengine.net/master.php" -#define DOOMSDAY_DOCSURL "http://dengine.net/dew" +#define DOOMSDAY_DOCSURL "https://manual.dengine.net" /** * Version number rules: (major).(minor).(revision)-(release name) diff --git a/doomsday/cmake/FindCPlus.cmake b/doomsday/cmake/FindCPlus.cmake new file mode 100644 index 0000000000..2ad306dfcd --- /dev/null +++ b/doomsday/cmake/FindCPlus.cmake @@ -0,0 +1,31 @@ +# c_Plus provides a number of low-level C features. + +set (CPLUS_DIR "" CACHE PATH "Location of the c_Plus library") + +set (_oldPath ${CPLUS_DEFS_H}) +find_file (CPLUS_DEFS_H include/c_plus/defs.h + PATHS "${CPLUS_DIR}" + HINTS ENV DENG_DEPEND_PATH + PATH_SUFFIXES cplus c_plus + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +mark_as_advanced (CPLUS_DEFS_H) + +if (NOT _oldPath STREQUAL CPLUS_DEFS_H) + if (CPLUS_DEFS_H) + message (STATUS "Looking for c_Plus - found") + else () + message (STATUS "Looking for c_Plus - not found (set the CPLUS_DIR variable)") + endif () +endif () + +if (CPLUS_DEFS_H AND NOT TARGET cplus) + get_filename_component (cplusInc "${CPLUS_DEFS_H}" DIRECTORY) + get_filename_component (cplusInc "${cplusInc}" DIRECTORY) + + add_library (cplus INTERFACE) + target_include_directories (cplus INTERFACE ${cplusInc}) + # target_link_libraries (cplus INTERFACE xxx) +endif () + diff --git a/doomsday/libs/core/CMakeLists.txt b/doomsday/libs/core/CMakeLists.txt index 17de7ffb91..a9fecc8058 100644 --- a/doomsday/libs/core/CMakeLists.txt +++ b/doomsday/libs/core/CMakeLists.txt @@ -5,6 +5,7 @@ project (DE_LIBCORE) include (../../cmake/Config.cmake) # Dependencies. +find_package (CPlus) include (ZLIB) find_package (Git QUIET) @@ -58,8 +59,9 @@ deng_add_package (net.dengine.stdlib) deng_add_library (libcore ${SOURCES} ${HEADERS}) target_include_directories (libcore PRIVATE ${ZLIB_INCLUDE_DIR}) -deng_target_link_qt (libcore PUBLIC Core Network) +#deng_target_link_qt (libcore PUBLIC Core Network) target_link_libraries (libcore PUBLIC ${ZLIB_LIBRARIES}) +target_link_libraries (libcore PRIVATE cplus) deng_deploy_library (libcore DengCore) deng_cotire (libcore src/precompiled.h) diff --git a/doomsday/libs/core/include/de/List b/doomsday/libs/core/include/de/List new file mode 100644 index 0000000000..8c1c990c06 --- /dev/null +++ b/doomsday/libs/core/include/de/List @@ -0,0 +1 @@ +#include "data/list.h" diff --git a/doomsday/libs/core/include/de/RegExp b/doomsday/libs/core/include/de/RegExp new file mode 100644 index 0000000000..3958e0dc57 --- /dev/null +++ b/doomsday/libs/core/include/de/RegExp @@ -0,0 +1 @@ +#include "data/regexp.h" diff --git a/doomsday/libs/core/include/de/data/block.h b/doomsday/libs/core/include/de/data/block.h index 6995b0b457..b54dd16523 100644 --- a/doomsday/libs/core/include/de/data/block.h +++ b/doomsday/libs/core/include/de/data/block.h @@ -23,9 +23,10 @@ #include "../IByteArray" #include "../IBlock" #include "../ISerializable" +#include "../List" #include "../Writer" -#include +#include #include namespace de { @@ -43,15 +44,17 @@ class IIStream; * * @ingroup data */ -class DE_PUBLIC Block : public QByteArray, public IByteArray, public IBlock, - public ISerializable +class DE_PUBLIC Block + : public IByteArray + , public IBlock + , public ISerializable { public: Block(Size initialSize = 0); + Block(const iBlock *); Block(IByteArray const &array); Block(Block const &other); Block(Block &&moved); - Block(QByteArray const &byteArray); Block(char const *nullTerminatedCStr); Block(void const *data, Size length); @@ -83,16 +86,21 @@ class DE_PUBLIC Block : public QByteArray, public IByteArray, public IBlock, */ Block(IByteArray const &array, Offset at, Size count); + virtual ~Block(); + Byte *data(); Byte const *dataConst() const; + inline Byte const *cdata() const { return dataConst(); } + inline Byte const *constData() const { return dataConst(); } Block &append(Byte b); Block &append(char const *str, int len); - inline explicit operator bool() const { return !isEmpty(); } + operator const iBlock *() const { return &_block; } + inline explicit operator bool() const { return !isEmpty_Block(&_block); } Block &operator += (char const *nullTerminatedCStr); - Block &operator += (QByteArray const &bytes); +// Block &operator += (QByteArray const &bytes); /// Appends a block after this one. Block &operator += (Block const &other); @@ -180,7 +188,10 @@ class DE_PUBLIC Block : public QByteArray, public IByteArray, public IBlock, void operator << (Reader &from); public: - static Block join(QList const &blocks, Block const &sep = Block()); + static Block join(List const &blocks, Block const &sep = Block()); + +private: + iBlock _block; }; template diff --git a/doomsday/libs/core/include/de/data/list.h b/doomsday/libs/core/include/de/data/list.h new file mode 100644 index 0000000000..67301808fa --- /dev/null +++ b/doomsday/libs/core/include/de/data/list.h @@ -0,0 +1,72 @@ +/** @file + * + * @authors Copyright (c) 2018 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBCORE_LIST_H +#define LIBCORE_LIST_H + +#include + +namespace de { + +/** + * Array of elements. + * @ingroup data + */ +template +class List : public std::vector +{ + using Base = std::vector; + +public: + List() {} + List(const List &); + List(List &&); + List(const std::initializer_list &init) { + for (const auto &i : init) { + push_back(i); + } + } + + using iterator = typename Base::iterator; + using const_iterator = typename Base::const_iterator; + using reverse_iterator = typename Base::reverse_iterator; + using const_reverse_iterator = typename Base::const_reverse_iterator; + + // Qt style methods: + + int size() const { return int(Base::size()); } + void clear() { Base::clear(); } + bool isEmpty() const { return Base::size() == 0; } + void append(const T &s) { Base::push_back(s); } + void prepend(const T &s) { Base::push_front(s); } + void insert(int pos, const T &value) { Base::insert(Base::begin() + pos, value); } + const T &operator[](int pos) const { return Base::at(pos); } + T & operator[](int pos) { return Base::operator[](pos); } + const T &at(int pos) const { return Base::at(pos); } + const T &first() const { return Base::front(); } + const T &last() const { return Base::back(); } + T takeFirst() { T v = first(); Base::pop_front(); return v; } + T takeLast() { T v = last(); Base::pop_back(); return v; } + void removeFirst() { Base::erase(Base::begin()); } + void removeLast() { Base::erase(Base::begin() + size() - 1); } + void removeAt(int pos) { Base::erase(Base::begin() + pos); } +}; + +} // namespace de + +#endif // LIBCORE_LIST_H diff --git a/doomsday/libs/core/include/de/data/regexp.h b/doomsday/libs/core/include/de/data/regexp.h new file mode 100644 index 0000000000..fdc042d140 --- /dev/null +++ b/doomsday/libs/core/include/de/data/regexp.h @@ -0,0 +1,50 @@ +/** @file regexp.h Perl-compatible regular expressions. + * + * @authors Copyright (c) 2018 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBCORE_REGEXP_H +#define LIBCORE_REGEXP_H + +#include "../String" +#include + +namespace de { + +class RegExpMatch +{ +public: + RegExpMatch(); + +private: + iRegExpMatch _match; +}; + +/** + * Perl-compatible regular expression. + */ +class RegExp +{ +public: + RegExp(const String &expression = {}, String::CaseSensitivity cs = String::CaseSensitive); + +private: + iRegExp *_d; +}; + +} // namespace de + +#endif // LIBCORE_REGEXP_H diff --git a/doomsday/libs/core/include/de/data/string.h b/doomsday/libs/core/include/de/data/string.h index 5e0e1a388e..c88484ab19 100644 --- a/doomsday/libs/core/include/de/data/string.h +++ b/doomsday/libs/core/include/de/data/string.h @@ -20,12 +20,13 @@ #ifndef LIBCORE_STRING_H #define LIBCORE_STRING_H -#include -#include - #include "../libcore.h" #include "../Block" #include "../Range" +#include "../List" +#include "../RegExp" + +#include namespace de { @@ -34,11 +35,11 @@ namespace de { * other convenience methods. * * The default codec when converting strings from C strings is UTF-8 (e.g., - * constructor that takes a char const * as the only argument). + * constructor that takes a const char * as the only argument). * * @ingroup types */ -class DE_PUBLIC String : public QString +class DE_PUBLIC String { public: /// Error related to String operations (note: shadows de::Error). @ingroup errors @@ -73,90 +74,132 @@ class DE_PUBLIC String : public QString virtual ddouble asNumber() const = 0; }; - typedef QList PatternArgs; - - typedef dint size_type; - + typedef List PatternArgs; enum CaseSensitivity { CaseInsensitive, CaseSensitive }; public: + using size_type = dsize; static size_type const npos; + using Char2 = char[2]; public: String(); - String(String const &other); + String(const String &other); String(String &&moved); - String(QString const &text); - String(char const *nullTerminatedCStr); - String(wchar_t const *nullTerminatedWideStr); - String(char const *cStr, size_type length); - String(char const *cStr, dsize length); - String(QChar const *nullTerminatedStr); - String(QChar const *str, size_type length); - String(size_type length, QChar ch); - String(QString const &str, size_type index, size_type length); - String(const_iterator start, const_iterator end); - String(iterator start, iterator end); - - inline String &operator = (String const &other) { - QString::operator = (other); + String(const iString *other); + String(const std::string &text); + String(const char *nullTerminatedCStr); + String(const wchar_t *nullTerminatedWideStr); + String(const char *cStr, int length); + String(const char *cStr, dsize length); +// String(const QChar *nullTerminatedStr); +// String(const QChar *str, size_type length); + String(dsize length, char ch); + String(dsize length, Char ch); + String(const char *start, const char *end); + String(const std::string &str, dsize index, dsize length); + + template + String(Iterator start, Iterator end) + { + for (Iterator i = start; i != end; ++i) + { + push_back(*i); + } + } + + ~String(); + + inline String &operator=(const String &other) + { + set_String(&_str, &other._str); return *this; } - inline String &operator = (String &&moved) { - QString::operator = (moved); + inline String &operator=(String &&moved) + { + deinit_String(&_str); + _str = moved._str; + zap(moved._str); return *this; } inline explicit operator bool() const { return size() > 0; } - /// Conversion to a character pointer. - operator QChar const *() const { - return data(); - } + inline dsize size() const { return size_String(&_str); } /// Determines whether the string is empty. - bool empty() const { return size() == 0; } + inline bool empty() const { return size() == 0; } + + inline bool isEmpty() const { return empty(); } + + const char *c_str() const { return cstr_String(&_str); } + + /// Conversion to a character pointer. + operator const char *() const { return c_str(); } + + operator std::string() const { return toStdString(); } + + std::string toStdString() const { return {constBegin_String(&_str), constEnd_String(&_str)}; } + + std::wstring toWideString() const; + + void clear(); /// Returns the first character of the string. - QChar first() const; + Char first() const; /// Returns the last character of the string. - QChar last() const; + Char last() const; - bool beginsWith(QString const &s, CaseSensitivity cs = CaseSensitive) const { - return startsWith(s, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); - } - bool beginsWith(QChar c, CaseSensitivity cs = CaseSensitive) const { - return startsWith(c, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); + bool beginsWith(const String &s, CaseSensitivity cs = CaseSensitive) const + { + return startsWithSc_String(&_str, s, cs == CaseSensitive? &iCaseSensitive : &iCaseInsensitive); } - bool beginsWith(QLatin1String ls, CaseSensitivity cs = CaseSensitive) const { - return startsWith(ls, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); + // bool beginsWith(Qconst String &s, CaseSensitivity cs = CaseSensitive) const { + // return startsWith(s, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); + // } + bool beginsWith(char ch, CaseSensitivity cs = CaseSensitive) const + { + return beginsWith(Char2{ch, 0}, cs); } - bool beginsWith(char const *ls, CaseSensitivity cs = CaseSensitive) const { - return beginsWith(QLatin1String(ls), cs); + // bool beginsWith(QLatin1String ls, CaseSensitivity cs = CaseSensitive) const { + // return startsWith(ls, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); + // } + bool beginsWith(const char *cstr, CaseSensitivity cs = CaseSensitive) const + { + return startsWithSc_String(&_str, cstr, cs == CaseSensitive? &iCaseSensitive : &iCaseInsensitive); } - bool endsWith(QString const &s, CaseSensitivity cs = CaseSensitive) const { - return QString::endsWith(s, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); - } - bool endsWith(QChar c, CaseSensitivity cs = CaseSensitive) const { - return QString::endsWith(c, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); - } - bool endsWith(QLatin1String ls, CaseSensitivity cs = CaseSensitive) const { - return QString::endsWith(ls, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); +// bool endsWith(Qconst String &s, CaseSensitivity cs = CaseSensitive) const { +// return QString::endsWith(s, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); +// } + bool endsWith(char ch, CaseSensitivity cs = CaseSensitive) const + { + return endsWith(Char2{ch, 0}, cs); + // return QString::endsWith(c, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); } - bool endsWith(char const *ls, CaseSensitivity cs = CaseSensitive) const { - return endsWith(QLatin1String(ls), cs); +// bool endsWith(QLatin1String ls, CaseSensitivity cs = CaseSensitive) const { +// return QString::endsWith(ls, cs == CaseSensitive? Qt::CaseSensitive : Qt::CaseInsensitive); +// } + bool endsWith(const char *cstr, CaseSensitivity cs = CaseSensitive) const + { + return endsWithSc_String( + &_str, cstr, cs == CaseSensitive ? &iCaseSensitive : &iCaseInsensitive); } - String substr(int position, int n = -1) const { - return mid(position, n); - } + inline String mid(int position, int n = -1) const { return substr(position, n); } + String substr(size_type pos, size_type n = iInvalidPos) const; + String substr(int position, int n = -1) const; + String substr(const Rangei &range) const { return mid(range.start, range.size()); } - String substr(Rangei const &range) const { - return mid(range.start, range.size()); - } + String operator+(const char *) const; + inline String operator+(const String &other) const { return *this + (const char *) other; } + + String &operator+=(char ch); + String &operator+=(Char ch); + String &operator+=(const char *); + String &operator+=(const String &); /** * Does a path concatenation on this string and the argument. Note that if @@ -166,9 +209,9 @@ class DE_PUBLIC String : public QString * @param path Path to concatenate. * @param dirChar Directory/folder separator character. */ - String concatenatePath(String const &path, QChar dirChar = '/') const; + String concatenatePath(const String &path, Char dirChar = '/') const; - String concatenateRelativePath(String const &path, QChar dirChar = '/') const; + String concatenateRelativePath(const String &path, Char dirChar = '/') const; /** * Does a path concatenation on this string and the argument. Note that if @@ -177,7 +220,7 @@ class DE_PUBLIC String : public QString * * @param path Path to concatenate. */ - String operator / (String const &path) const; + String operator/(const String &path) const; /** * Applies pattern formatting using the string as a format string. @@ -187,7 +230,7 @@ class DE_PUBLIC String : public QString * @return String with placeholders replaced using the arguments. * @see patternFormat() */ - String operator % (PatternArgs args) const; + String operator%(const PatternArgs &args) const; /** * Does a record member concatenation on a variable name. Record members @@ -195,7 +238,7 @@ class DE_PUBLIC String : public QString * * @param member Identifier to append to this string. */ - String concatenateMember(String const &member) const; + String concatenateMember(const String &member) const; /// Removes whitespace from the beginning and end of the string. /// @return Copy of the string without whitespace. @@ -213,7 +256,7 @@ class DE_PUBLIC String : public QString String normalizeWhitespace() const; /// Returns a copy of the string with matches removed. - String removed(QRegularExpression const &expr) const; + String removed(const RegExp &expr) const; /// Returns a lower-case version of the string. String lower() const; @@ -225,7 +268,7 @@ class DE_PUBLIC String : public QString String upperFirstChar() const; /// Extracts the base name from the string (includes extension). - String fileName(QChar dirChar = '/') const; + String fileName(Char dirChar = '/') const; /// Extracts the base name from the string (does not include extension). String fileNameWithoutExtension() const; @@ -243,12 +286,29 @@ class DE_PUBLIC String : public QString String fileNameExtension() const; /// Extracts the path of the string. - String fileNamePath(QChar dirChar = '/') const; + String fileNamePath(Char dirChar = '/') const; /// Extracts everything but the extension from string. - String fileNameAndPathWithoutExtension(QChar dirChar = '/') const; + String fileNameAndPathWithoutExtension(char dirChar = '/') const; + + int indexOf(char ch) const { size_t pos = indexOf_String(&_str, ch); return pos == iInvalidPos ? -1 : pos; } + int indexOf(Char ch) const { size_t pos = indexOf_String(&_str, ch); return pos == iInvalidPos ? -1 : pos; } + int indexOf(const char *cstr) const { size_t pos = indexOfCStr_String(&_str, cstr); return pos == iInvalidPos ? -1 : pos; } + int lastIndexOf(char ch) const { size_t pos = lastIndexOf_String(&_str, ch); return pos == iInvalidPos ? -1 : pos; } + int lastIndexOf(Char ch) const { size_t pos = lastIndexOf_String(&_str, ch); return pos == iInvalidPos ? -1 : pos; } + int lastIndexOf(const char *cstr) const { size_t pos = lastIndexOfCStr_String(&_str, cstr); return pos == iInvalidPos ? -1 : pos;} + + bool containsWord(const String &word) const; + + inline bool operator==(const char *cstr) const { return compare(cstr) == 0; } + inline bool operator!=(const char *cstr) const { return compare(cstr) != 0; } + inline bool operator< (const char *cstr) const { return compare(cstr) < 0; } + inline bool operator> (const char *cstr) const { return compare(cstr) > 0; } + inline bool operator<=(const char *cstr) const { return compare(cstr) <= 0; } + inline bool operator>=(const char *cstr) const { return compare(cstr) >= 0; } - bool containsWord(String const &word) const; + inline int compare(const char *cstr) const { return cmp_String(&_str, cstr); } + inline int compare(const String &s) const { return cmpString_String(&_str, &s); } /** * Compare two strings (case sensitive). @@ -256,7 +316,7 @@ class DE_PUBLIC String : public QString * @return 0, if @a a and @a b are identical. Positive, if @a a > @a b. * Negative, if @a a < @a b. */ - dint compareWithCase(String const &str) const; + dint compareWithCase(const String &str) const; /** * Compare two strings (case insensitive). @@ -264,7 +324,7 @@ class DE_PUBLIC String : public QString * @return 0, if @a a and @a b are identical. Positive, if @a a > @a b. * Negative, if @a a < @a b. */ - dint compareWithoutCase(String const &str) const; + dint compareWithoutCase(const String &str) const; /** * Compare two strings (case insensitive), but only up to @a n characters. @@ -273,7 +333,7 @@ class DE_PUBLIC String : public QString * @return 0, if @a a and @a b are identical. Positive, if @a a > @a b. * Negative, if @a a < @a b. */ - dint compareWithoutCase(String const &str, int n) const; + dint compareWithoutCase(const String &str, int n) const; /** * Compares two strings to see how many characters they have in common @@ -284,7 +344,7 @@ class DE_PUBLIC String : public QString * * @return Number of characters the two strings have in common from the left. */ - int commonPrefixLength(String const &str, CaseSensitivity sensitivity = CaseSensitive) const; + int commonPrefixLength(const String &str, CaseSensitivity sensitivity = CaseSensitive) const; /** * Converts the string to UTF-8 and returns it as a byte block. @@ -304,7 +364,7 @@ class DE_PUBLIC String : public QString AllowOnlyWhitespace = 0x0, AllowSuffix = 0x1 }; - Q_DECLARE_FLAGS(IntConversionFlags, IntConversionFlag) +// Q_DECLARE_FLAGS(IntConversionFlags, IntConversionFlag) /** * Converts the string to an integer. The default behavior is identical to @@ -326,7 +386,7 @@ class DE_PUBLIC String : public QString * @return Integer parsed from the string (@c *ok set to true). @c 0 if * the conversion fails (@c *ok set to @c false). */ - dint toInt(bool *ok = 0, int base = 10, IntConversionFlags flags = AllowOnlyWhitespace) const; + dint toInt(bool *ok = nullptr, int base = 10, duint flags = AllowOnlyWhitespace) const; /** * Converts the string to a 32-bit unsigned integer. The behavior is the same as @@ -340,7 +400,7 @@ class DE_PUBLIC String : public QString * @return 32-bit unsigned integer parsed from the string (@c *ok set to true). * @c 0 if the conversion fails (@c *ok set to @c false). */ - duint32 toUInt32(bool *ok = 0, int base = 0) const; + duint32 toUInt32(bool *ok = nullptr, int base = 0) const; /** * Adds a prefix to each line in the text. @@ -349,33 +409,74 @@ class DE_PUBLIC String : public QString * * @return Prefixed text. */ - String addLinePrefix(String const &prefix) const; + String addLinePrefix(const String &prefix) const; /** * Prefixes double quotes and backslashes with a backslash. */ String escaped() const; - String truncateWithEllipsis(int maxLength) const; + String truncateWithEllipsis(dsize maxLength) const; Block toPercentEncoding() const; +public: + // Iterators: + + struct const_iterator + { + iStringConstIterator iter; + + operator Char() const { return iter.value; } + Char operator*() const { return iter.value; } + void operator++() { next_StringConstIterator(&iter); } + void operator++(int) { next_StringConstIterator(&iter); } + bool operator==(const const_iterator &other) const { + return iter.str == other.str && iter.pos == other.pos; + } + bool operator!=(const const_iterator &other) const { return !(*this == other); } + }; + + struct const_reverse_iterator + { + iStringReverseConstIterator iter; + + operator Char() const { return iter.value; } + Char operator*() const { return iter.value; } + void operator++() { next_StringReverseConstIterator(&iter); } + void operator++(int) { next_StringReverseConstIterator(&iter); } + bool operator==(const const_reverse_iterator &other) const { + return iter.str == other.str && iter.pos == other.pos; + } + bool operator!=(const const_reverse_iterator &other) const { return !(*this == other); } + }; + + const_iterator begin() const; + const_iterator cbegin() const { return begin(); } + const_reverse_iterator rbegin() const; + const_reverse_iterator crbegin() const { return rbegin(); } + + const_iterator end() const; + const_iterator cend() const { return end(); } + const_reverse_iterator rend() const; + const_reverse_iterator crend() const { return rend(); } + public: /** * Builds a String out of an array of bytes that contains a UTF-8 string. */ - static String fromUtf8(IByteArray const &byteArray); + static String fromUtf8(const IByteArray &byteArray); - static String fromUtf8(QByteArray const &byteArray); +// static String fromUtf8(const QByteArray &byteArray); - static String fromUtf8(Block const &block); + static String fromUtf8(const Block &block); - static String fromUtf8(char const *nullTerminatedCStr); + static String fromUtf8(const char *nullTerminatedCStr); /** * Builds a String out of an array of bytes that contains a Latin1 string. */ - static String fromLatin1(IByteArray const &byteArray); + static String fromLatin1(const IByteArray &byteArray); /** * Builds a String out of an array of bytes using the IBM PC character set @@ -385,11 +486,11 @@ class DE_PUBLIC String : public QString * * @return Converted string. */ - static String fromCP437(IByteArray const &byteArray); + static String fromCP437(const IByteArray &byteArray); - static String fromPercentEncoding(Block const &percentEncoded); + static String fromPercentEncoding(const Block &percentEncoded); - static dint compareWithCase(QChar const *a, QChar const *b, dsize count); +// static dint compareWithCase(const QChar *a, const QChar *b, dsize count); /** * Checks if two strings are the same (case sensitive), up to @a count characters. @@ -405,7 +506,7 @@ class DE_PUBLIC String : public QString * @return @c true, if the strings are equal (excluding the part that is longer * than @a count). */ - static bool equals(QChar const *a, QChar const *b, dsize count); +// static bool equals(const QChar *a, const QChar *b, dsize count); /** * Advances the iterator until a nonspace character is encountered. @@ -413,21 +514,25 @@ class DE_PUBLIC String : public QString * @param i Iterator to advance. * @param end End of the string. Will not advance past this. */ - static void skipSpace(String::const_iterator &i, String::const_iterator const &end); + static void skipSpace(const_iterator &i, const const_iterator &end); /** - * Formats a string using standard printf() formatting. Uses UTF-8 encoding. + * Formats a string using standard printf() formatting. * * @param format Format string. * - * @return Formatted output.. + * @return Formatted output. */ - static String format(String format, ...); + static String format(const char *format, ...); /** * Formats data according to formatting instructions. Outputs a * string containing the formatted data. * + * Note that the behavior is different than the standard C printf() formatting. + * For example, "%b" will format the number argument as boolean "True" or "False". + * Use String::format() for normal printf() formatting. + * * @param formatIter Will be moved past the formatting instructions. * After the call, will remain past the end of * the formatting characters. @@ -436,36 +541,47 @@ class DE_PUBLIC String : public QString * * @return Formatted argument as a string. */ - static String patternFormat(String::const_iterator &formatIter, - String::const_iterator const &formatEnd, - IPatternArg const &arg); + static String patternFormat(const_iterator &formatIter, + const const_iterator &formatEnd, + const IPatternArg &arg); - static void advanceFormat(String::const_iterator &i, - String::const_iterator const &end); + static void advanceFormat(const_iterator &i, + const const_iterator &end); - static String join(QList const &stringList, String const &sep = {}); + static String join(const List &stringList, const String &sep = {}); + +private: + iString _str; }; +inline String operator+(Char ch, const String &s) { + return String(1, ch) + s; +} + +inline String operator+(const char *cstr, const String &s) { + return String(cstr) + s; +} + +using StringList = List; + /** * Support routine for determining the length of a null-terminated QChar* * string. Later versions have a QString constructor that needs no length * when constructing a string from QChar*. */ -size_t qchar_strlen(QChar const *str); +//size_t qchar_strlen(const QChar *str); -typedef QList StringList; +//inline StringList toStringList(const QStringList &qstr) { +// return compose(qstr.begin(), qstr.end()); +//} -inline StringList toStringList(const QStringList &qstr) { - return compose(qstr.begin(), qstr.end()); +inline String operator / (const char *cstr, const String &str) { + return String(cstr) / str; } -inline String operator / (char const *utf8CStr, String const &str) { - return String(utf8CStr) / str; -} - -inline String operator / (QString const &qs, String const &str) { - return String(qs) / str; -} +//inline String operator / (Qconst String &qs, const String &str) { +// return String(qs) / str; +//} } // namespace de diff --git a/doomsday/libs/core/include/de/data/stringlist.h b/doomsday/libs/core/include/de/data/stringlist.h new file mode 100644 index 0000000000..ee242de71a --- /dev/null +++ b/doomsday/libs/core/include/de/data/stringlist.h @@ -0,0 +1,62 @@ +/** @file stringlist.h List of strings. + * + * @authors Copyright (c) 2018 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBCORE_STRINGLIST_H +#define LIBCORE_STRINGLIST_H + +#include "../String" + +namespace de { + +/** + * List of strings. + */ +class StringList +{ +public: + StringList(); + + template + StringList(const std::initializer_list &init) { + for (const auto &v : init) { + append(v); + } + } + + inline bool isEmpty() const { return size() == 0; } + inline bool empty() const { return isEmpty(); } + + void append(const String &); + void prepend(const String &); + void insert(int pos, const String &); + + const String &first() const; + const String &back() const; + + const String &front() const; + const String &back() const; + inline void push_back(const String &s) { append(s); } + inline void pop_back() { removeLast(); } + +private: + DE_PRIVATE(d) +}; + +} // namespace de + +#endif // LIBCORE_STRINGLIST_H diff --git a/doomsday/libs/core/include/de/libcore.h b/doomsday/libs/core/include/de/libcore.h index 915975579c..61d97610d8 100644 --- a/doomsday/libs/core/include/de/libcore.h +++ b/doomsday/libs/core/include/de/libcore.h @@ -86,45 +86,6 @@ # define DE_64BIT #endif -#ifdef DE_USE_QT -# include -# include -# include -# include - -// Qt versioning helper. Qt 4.8 is the oldest we support. -# if (QT_VERSION < QT_VERSION_CHECK(4, 8, 0)) -# error "Unsupported version of Qt" -# endif -# define DE_QT_4_6_OR_NEWER -# define DE_QT_4_7_OR_NEWER -# define DE_QT_4_8_OR_NEWER -# if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) -# define DE_QT_5_0_OR_NEWER -# endif -# if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)) -# define DE_QT_5_1_OR_NEWER -# endif -# if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) -# define DE_QT_5_2_OR_NEWER -# endif -# if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)) -# define DE_QT_5_3_OR_NEWER -# endif -# if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) -# define DE_QT_5_4_OR_NEWER -# endif -# if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) -# define DE_QT_5_5_OR_NEWER -# endif -# if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) -# define DE_QT_5_6_OR_NEWER -# endif -# if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)) -# define DE_QT_5_7_OR_NEWER -# endif -#endif - #ifndef _MSC_VER # include // Not MSVC so use C99 standard header #endif @@ -179,11 +140,7 @@ #ifndef NDEBUG # define DE_DEBUG DE_EXTERN_C DE_PUBLIC void LogBuffer_Flush(void); -# ifdef DE_USE_QT -# define DE_ASSERT(x) {if (!(x)) {LogBuffer_Flush(); Q_ASSERT(x);}} -# else -# define DE_ASSERT(x) {if (!(x)) {LogBuffer_Flush(); assert(x);}} -# endif +# define DE_ASSERT(x) {if (!(x)) {LogBuffer_Flush(); assert(x);}} # define DE_DEBUG_ONLY(x) x #else # define DE_NO_DEBUG @@ -193,28 +150,27 @@ #define DE_ASSERT_FAIL(msgCStr) DE_ASSERT(msgCStr == nullptr) -#ifdef DE_USE_QT -# ifdef UNIX -# include +#ifdef UNIX +# include /** * @macro DE_PRINT_BACKTRACE * Debug utility for dumping the current backtrace using qDebug. */ -# define DE_PRINT_BACKTRACE() { \ +# define DE_PRINT_BACKTRACE() { \ void *callstack[128]; \ int i, frames = backtrace(callstack, 128); \ char** strs = backtrace_symbols(callstack, frames); \ - for (i = 0; i < frames; ++i) qDebug("%s", strs[i]); \ + for (i = 0; i < frames; ++i) printf(stderr, "%s", strs[i]); \ free(strs); } -# define DE_BACKTRACE(n, out) { \ +# define DE_BACKTRACE(n, outStr) { \ void *callstack[n]; \ int i, frames = backtrace(callstack, n); \ char** strs = backtrace_symbols(callstack, frames); \ out = ""; \ - for (i = 0; i < frames; ++i) { out.append(strs[i]); out.append('\n'); } \ + for (i = 0; i < frames; ++i) { outStr.append(strs[i]); outStr.append('\n'); } \ free(strs); } # else -# define DENG2_PRINT_BACKTRACE() +# define DE_PRINT_BACKTRACE() # endif #endif @@ -373,6 +329,15 @@ #if defined(__cplusplus) && !defined(DE_C_API_ONLY) namespace de { +/** + * Formats a string using the standard C printf() syntax. + * + * @param format Format string. + * + * @return Formatted string. + */ +DE_PUBLIC std::string stringf(const char *format, ...); + /** * @defgroup errors Exceptions * @@ -385,13 +350,13 @@ namespace de { class DE_PUBLIC Error : public std::runtime_error { public: - Error(QString const &where, QString const &message); + Error(const std::string &where, const std::string &message); - QString name() const; - virtual QString asText() const; + std::string name() const; + virtual std::string asText() const; protected: - void setName(QString const &name); + void setName(const std::string &name); private: std::string _name; @@ -406,9 +371,9 @@ class DE_PUBLIC Error : public std::runtime_error #define DE_SUB_ERROR(Parent, Name) \ class Name : public Parent { \ public: \ - Name(QString const &message) \ + Name(const std::string &message) \ : Parent("-", message) { Parent::setName(#Name); } \ - Name(QString const &where, QString const &message) \ + Name(const std::string &where, const std::string &message) \ : Parent(where, message) { Parent::setName(#Name); } \ virtual void raise() const { throw *this; } \ } /**< @note One must put a semicolon after the macro invocation. */ @@ -470,14 +435,14 @@ template inline X_ &expectedAs(T_ *ptr) { if (auto *t = maybeAs(ptr)) return *t; DE_ASSERT(false); - throw CastError(QString("Cannot cast %1 to %2").arg(DE_TYPE_NAME(T_)).arg(DE_TYPE_NAME(X_))); + throw CastError(stringf("Cannot cast %s to %s", DE_TYPE_NAME(T_), DE_TYPE_NAME(X_))); } template inline X_ const &expectedAs(T_ const *ptr) { if (auto const *t = maybeAs(ptr)) return *t; DE_ASSERT(false); - throw CastError(QString("Cannot cast %1 to %2").arg(DE_TYPE_NAME(T_)).arg(DE_TYPE_NAME(X_))); + throw CastError(stringf("Cannot cast %s to %s", DE_TYPE_NAME(T_), DE_TYPE_NAME(X_))); } /** @@ -785,25 +750,27 @@ enum ProtocolVersion { //@{ /// @ingroup types -typedef qint8 dchar; ///< 8-bit signed integer. -typedef quint8 dbyte; ///< 8-bit unsigned integer. -typedef quint8 duchar; ///< 8-bit unsigned integer. -typedef dchar dint8; ///< 8-bit signed integer. -typedef dbyte duint8; ///< 8-bit unsigned integer. -typedef qint16 dint16; ///< 16-bit signed integer. -typedef quint16 duint16; ///< 16-bit unsigned integer. -typedef dint16 dshort; ///< 16-bit signed integer. -typedef duint16 dushort; ///< 16-bit unsigned integer. -typedef qint32 dint32; ///< 32-bit signed integer. -typedef quint32 duint32; ///< 32-bit unsigned integer. -typedef dint32 dint; ///< 32-bit signed integer. -typedef duint32 duint; ///< 32-bit unsigned integer. -typedef qint64 dint64; ///< 64-bit signed integer. -typedef quint64 duint64; ///< 64-bit unsigned integer. -typedef float dfloat; ///< 32-bit floating point number. -typedef double ddouble; ///< 64-bit floating point number. -typedef size_t dsize; // Likely unsigned long. -typedef long dlong; +typedef int8_t dchar; ///< 8-bit signed integer. +typedef uint8_t dbyte; ///< 8-bit unsigned integer. +typedef uint8_t duchar; ///< 8-bit unsigned integer. +typedef dchar dint8; ///< 8-bit signed integer. +typedef dbyte duint8; ///< 8-bit unsigned integer. +typedef int16_t dint16; ///< 16-bit signed integer. +typedef uint16_t duint16; ///< 16-bit unsigned integer. +typedef dint16 dshort; ///< 16-bit signed integer. +typedef duint16 dushort; ///< 16-bit unsigned integer. +typedef int32_t dint32; ///< 32-bit signed integer. +typedef uint32_t duint32; ///< 32-bit unsigned integer. +typedef dint32 dint; ///< 32-bit signed integer. +typedef duint32 duint; ///< 32-bit unsigned integer. +typedef int64_t dint64; ///< 64-bit signed integer. +typedef uint64_t duint64; ///< 64-bit unsigned integer. +typedef float dfloat; ///< 32-bit floating point number. +typedef double ddouble; ///< 64-bit floating point number. +typedef size_t dsize; // Likely unsigned long. +typedef long dlong; + +typedef wchar_t Char; // Pointer-integer conversion (used for legacy code). #ifdef DE_64BIT diff --git a/doomsday/libs/core/src/core/logtextstyle.h b/doomsday/libs/core/src/core/logtextstyle.h index 01b3e08c16..83b2b4df46 100644 --- a/doomsday/libs/core/src/core/logtextstyle.h +++ b/doomsday/libs/core/src/core/logtextstyle.h @@ -29,15 +29,15 @@ namespace de { -extern char const *TEXT_STYLE_NORMAL; -extern char const *TEXT_STYLE_MESSAGE; -extern char const *TEXT_STYLE_MAJOR_MESSAGE; -extern char const *TEXT_STYLE_MINOR_MESSAGE; -extern char const *TEXT_STYLE_SECTION; -extern char const *TEXT_STYLE_MINOR_SECTION; -extern char const *TEXT_STYLE_MAJOR_SECTION; -extern char const *TEXT_STYLE_LOG_TIME; -extern char const *TEXT_MARK_INDENT; +extern const char *TEXT_STYLE_NORMAL; +extern const char *TEXT_STYLE_MESSAGE; +extern const char *TEXT_STYLE_MAJOR_MESSAGE; +extern const char *TEXT_STYLE_MINOR_MESSAGE; +extern const char *TEXT_STYLE_SECTION; +extern const char *TEXT_STYLE_MINOR_SECTION; +extern const char *TEXT_STYLE_MAJOR_SECTION; +extern const char *TEXT_STYLE_LOG_TIME; +extern const char *TEXT_MARK_INDENT; } // namespace de diff --git a/doomsday/libs/core/src/data/block.cpp b/doomsday/libs/core/src/data/block.cpp index 725e31fb77..a981fb4c9d 100644 --- a/doomsday/libs/core/src/data/block.cpp +++ b/doomsday/libs/core/src/data/block.cpp @@ -20,42 +20,47 @@ #include "de/Block" #include "de/File" -#include #include namespace de { Block::Block(Size initialSize) { - resize(initialSize); + init_Block(&_block, initialSize); +} + +Block::Block(const iBlock *other) +{ + initCopy_Block(&_block, other); } Block::Block(IByteArray const &other) { + init_Block(&_block, other.size()); // Read the other's data directly into our data buffer. - resize(other.size()); - other.get(0, (dbyte *) data(), other.size()); + other.get(0, data(), other.size()); } Block::Block(Block const &other) - : QByteArray(other), IByteArray(), IBlock() -{} +{ + initCopy_Block(&_block, &other._block); +} Block::Block(Block &&moved) - : QByteArray(moved) -{} - -Block::Block(QByteArray const &byteArray) - : QByteArray(byteArray) -{} +{ + _block = moved._block; + iZap(moved._block); +} Block::Block(char const *nullTerminatedCStr) - : QByteArray(nullTerminatedCStr) -{} +{ + initCStr_Block(&_block, nullTerminatedCStr); +} Block::Block(void const *data, Size length) - : QByteArray(reinterpret_cast(data), int(length)), IByteArray(), IBlock() -{} +{ + initData_Block(&_block, data, length); +} Block::Block(IIStream &stream) { @@ -72,9 +77,14 @@ Block::Block(IByteArray const &other, Offset at, Size count) : IByteArray() copyFrom(other, at, count); } +Block::~Block() +{ + deinit_Block(&_block); +} + Block::Size Block::size() const { - return QByteArray::size(); + return size_Block(&_block); } void Block::get(Offset atPos, Byte *values, Size count) const @@ -83,7 +93,7 @@ void Block::get(Offset atPos, Byte *values, Size count) const { /// @throw OffsetError The accessed region of the block was out of range. throw OffsetError("Block::get", "Out of range " + - String("(%1[+%2] > %3)").arg(atPos).arg(count).arg(size())); + String::format("(%zu[+%zu] > %zu)", atPos, count, size())); } std::memcpy(values, constData() + atPos, count); @@ -108,7 +118,7 @@ void Block::copyFrom(IByteArray const &array, Offset at, Size count) void Block::resize(Size size) { - QByteArray::resize(size); + resize_Block(&_block, size); } IByteArray::Byte const *Block::dataConst() const diff --git a/doomsday/libs/core/src/data/string.cpp b/doomsday/libs/core/src/data/string.cpp index d101378e81..5fcacb6af3 100644 --- a/doomsday/libs/core/src/data/string.cpp +++ b/doomsday/libs/core/src/data/string.cpp @@ -22,106 +22,179 @@ #include "de/Block" #include "de/charsymbols.h" -#include -#include -#include -#include // percent encoding +#include #include #include +#include namespace de { -String::size_type const String::npos = -1; +const String::size_type String::npos = String::size_type(-1); String::String() {} -String::String(String const &other) : QString(other) -{} - -String::String(String &&moved) : QString(std::move(moved)) -{} +String::String(const String &other) +{ + initCopy_String(&_str, &other._str); +} -String::String(QString const &text) : QString(text) -{} +String::String(String &&moved) +{ + _str = moved._str; + iZap(moved._str); +} -#ifdef DE_QT_4_7_OR_NEWER -String::String(QChar const *nullTerminatedStr) - : QString(nullTerminatedStr) -{} -#else -String::String(QChar const *nullTerminatedStr) - : QString(nullTerminatedStr, qchar_strlen(nullTerminatedStr)) -{} -#endif +String::String(const iString *other) +{ + initCopy_String(&_str, other); +} -String::String(QChar const *str, size_type length) : QString(str, length) -{} +String::String(const std::string &text) +{ + initCStrN_String(&_str, text.data(), text.size()); +} -String::String(char const *nullTerminatedCStr) - : QString(QString::fromUtf8(nullTerminatedCStr)) -{} +String::String(const char *nullTerminatedCStr) +{ + initCStr_String(&_str, nullTerminatedCStr); +} -String::String(wchar_t const *nullTerminatedWideStr) - : QString(QString::fromWCharArray(nullTerminatedWideStr)) -{} +String::String(const Char *nullTerminatedWideStr) +{ + initWide_String(&_str, nullTerminatedWideStr); +} -String::String(char const *cStr, size_type length) - : QString(QString::fromUtf8(cStr, length)) -{} +String::String(char const *cStr, int length) +{ + initCStrN_String(&_str, cStr, length); +} String::String(char const *cStr, dsize length) - : QString(QString::fromUtf8(cStr, int(length))) -{} +{ + initCStrN_String(&_str, cStr, length); +} -String::String(size_type length, QChar ch) : QString(length, ch) -{} +String::String(dsize length, char ch) +{ + init_Block(&_str.chars, length); + fill_Block(&_str.chars, ch); +} -String::String(QString const &str, size_type index, size_type length) - : QString(str.mid(index, length)) -{} +String::String(const char *start, const char *end) +{ + initCStrN_String(&_str, start, end - start); +} -String::String(const_iterator start, const_iterator end) +String::String(dsize length, iChar ch) { - for (const_iterator i = start; i < end; ++i) + init_String(&_str); + + iMultibyteChar mb; + init_MultibyteChar(&mb, ch); + if (strlen(mb.bytes) == 1) { - append(*i); + resize_Block(&_str.chars, length); + fill_Block(&_str.chars, mb.bytes[0]); + } + else + { + while (length-- > 0) + { + appendCStr_String(&_str, mb.bytes); + } } } -String::String(iterator start, iterator end) +//String::String(Qconst String &str, size_type index, size_type length) +// : QString(str.mid(index, length)) +//{} + +String::~String() { - for (iterator i = start; i < end; ++i) + deinit_String(&_str); +} + +std::wstring String::toWideString() const +{ + std::wstring ws; + for (auto i : *this) + { + ws.push_back(i); + } + return ws; +} + +String String::substr(int position, int n) const +{ + iString *sub = mid_String(&_str, position, n); + String s(sub); + delete_String(sub); + return s; +} + +String String::operator+(const char *cStr) const + { + String cat(*this); + appendCStr_String(&cat._str, cStr); + return cat; + } + +String &String::operator+=(char ch) +{ + appendData_Block(&_str.chars, &ch, 1); + return *this; +} + +String &String::operator+=(iChar ch) +{ + appendChar_String(&_str, ch); + return*this; +} + +String &String::operator+=(const char *cStr) { - append(*i); + appendCStr_String(&_str, cStr); + return *this; } + +String &String::operator+=(const String &other) +{ + append_String(&_str, &other._str); + return *this; +} + +String String::substr(String::size_type pos, String::size_type n) const +{ + iString *sub = mid_String(&_str, pos, n); + String s(sub); + delete_String(sub); + return s; } -QChar String::first() const +iChar String::first() const { - if (empty()) return 0; - return at(0); + return empty() ? 0 : *begin(); } -QChar String::last() const +iChar String::last() const { - if (empty()) return 0; - return at(size() - 1); + return empty() ? 0 : *end(); } -String String::operator / (String const &path) const +String String::operator/(const String &path) const { return concatenatePath(path); } -String String::operator % (PatternArgs args) const +String String::operator%(const PatternArgs &args) const { String result; - QTextStream output(&result); +// QTextStream output(&result); PatternArgs::const_iterator arg = args.begin(); - DE_FOR_EACH_CONST(String, i, *this) + for (auto i = begin(); i != end(); ++i) { if (*i == '%') { @@ -130,7 +203,7 @@ String String::operator % (PatternArgs args) const if (*next == '%') { // Escaped. - output << *next; + result += *next; ++i; continue; } @@ -138,30 +211,30 @@ String String::operator % (PatternArgs args) const if (arg == args.end()) { // Out of args. - throw IllegalPatternError("String::operator %", "Ran out of arguments"); + throw IllegalPatternError("String::operator%", "Ran out of arguments"); } - output << patternFormat(i, end(), **arg); + result += patternFormat(i, end(), **arg); ++arg; } else { - output << *i; + result += *i; } } // Just append the rest of the arguments without special instructions. for (; arg != args.end(); ++arg) { - output << (*arg)->asText(); + result += (*arg)->asText(); } return result; } -String String::concatenatePath(String const &other, QChar dirChar) const +String String::concatenatePath(const String &other, iChar dirChar) const { - if ((dirChar == '/' || dirChar == '\\') && QDir::isAbsolutePath(other)) + if ((dirChar == '/' || dirChar == '\\') && isAbsolute_Path(&other._str)) { // The other path is absolute - use as is. return other; @@ -169,14 +242,14 @@ String String::concatenatePath(String const &other, QChar dirChar) const return concatenateRelativePath(other, dirChar); } -String String::concatenateRelativePath(const String &other, QChar dirChar) const +String String::concatenateRelativePath(const String &other, iChar dirChar) const { if (other.isEmpty()) return *this; int const startPos = (other.first() == dirChar? 1 : 0); // Do a path combination. Check for a slash. - String result = *this; + String result(*this); if (!empty() && last() != dirChar) { result += dirChar; @@ -185,7 +258,7 @@ String String::concatenateRelativePath(const String &other, QChar dirChar) const return result; } -String String::concatenateMember(String const &member) const +String String::concatenateMember(const String &member) const { if (member.isEmpty()) return *this; if (member.first() == '.') @@ -197,195 +270,66 @@ String String::concatenateMember(String const &member) const String String::strip() const { - return trimmed(); + String ts(*this); + trim_String(&ts._str); + return ts; } String String::leftStrip() const { - int endOfSpace = 0; - while (endOfSpace < size() && at(endOfSpace).isSpace()) - { - ++endOfSpace; + String ts(*this); + trimStart_String(&ts._str); + return ts; } - return mid(endOfSpace); -} String String::rightStrip() const { - int beginOfSpace = size() - 1; - while (beginOfSpace >= 0 && at(beginOfSpace).isSpace()) - { - --beginOfSpace; - } - return left(beginOfSpace + 1); + String ts(*this); + trimEnd_String(&ts._str); + return ts; } String String::normalizeWhitespace() const { - static QRegularExpression const reg("\\s+"); + static const RegExp reg("\\s+"); String s = *this; s.replace(reg, " "); return s.strip(); } -String String::removed(const QRegularExpression &expr) const +String String::removed(const RegExp &expr) const { String s = *this; - s.remove(expr); + s.replace(expr, ""); return s; } String String::lower() const { - return toLower(); + iString *low = toLower_String(&_str); + String str(low); + delete_String(low); + return str; } String String::upper() const { - return toUpper(); + iString *upr = toUpper_String(&_str); + String str(upr); + delete_String(upr); + return str; } String String::upperFirstChar() const { if (isEmpty()) return ""; if (size() == 1) return upper(); - return at(0).toUpper() + substr(1); + return towupper(first()) + substr(1); } -/* -std::wstring String::stringToWide(String const &str) +String String::fileName(Char dirChar) const { - duint inputSize = str.size(); - dbyte const *input = reinterpret_cast(str.c_str()); - dbyte const *inputEnd = input + inputSize; - std::vector output; - duint c; - duint d; - duint trailing; - - while (input < inputEnd) - { - d = *input++; - - if (d < 0x80) - { - c = d; - trailing = 0; - } - else if (d < 0xc0) - { - throw ConversionError("String::stringToWide", "Trailing byte in leading position"); - } - else if (d < 0xe0) - { - c = d & 0x1f; - trailing = 1; - } - else if (d < 0xf0) - { - c = d & 0x0f; - trailing = 2; - } - else if (d < 0xf8) - { - c = d & 0x07; - trailing = 3; - } - else - { - throw ConversionError("String::stringToWide", "Bad input"); - } - - for (; trailing; trailing--) - { - if ((input >= inputEnd) || (((d = *input++) & 0xc0) != 0x80)) - { - throw ConversionError("String::stringToWide", "Bad input"); - } - c <<= 6; - c |= d & 0x3f; - } - - if (c < 0x10000) - { - output.push_back(wchar_t(c)); - } - else if (c < 0x110000) - { - c -= 0x10000; - output.push_back(wchar_t(0xd800 | (c >> 10))); - output.push_back(0xdc00 | (c & 0x03ff)); - } - else - { - throw ConversionError("String::stringToWide", "Bad input"); - } - } - - output.push_back(0); - return std::wstring(&output[0]); -} - -String String::wideToString(std::wstring const &str) -{ - const wchar_t *input = &str[0]; - const wchar_t *inputEnd = input + str.size(); - String output; - duint c; - duint d; - dint bits; - - while (input < inputEnd) - { - c = *input++; - if ((c & 0xfc00) == 0xd800) - { - if ((input < inputEnd) && (((d = *input++) & 0xfc00) == 0xdc00)) - { - c &= 0x3ff; - c <<= 10; - c |= d & 0x3ff; - c += 0x10000; - } - else - { - throw ConversionError("String::wideToString", "Bad input"); - } - } - - if (c < 0x80) - { - output += dchar(c); - bits = -6; - } - else if (c < 0x800) - { - output += dchar((c >> 6) | 0xc0); - bits = 0; - } - else if (c < 0x10000) - { - output += dchar((c >> 12) | 0xe0); - bits = 6; - } - else - { - output += dchar((c >> 18) | 0xf0); - bits = 12; - } - - for (; bits > 0; bits -= 6) - { - output += dchar((c >> bits) & 0x3f); - } - } - return output; -} -*/ - -String String::fileName(QChar dirChar) const -{ - size_type pos = lastIndexOf(dirChar); + int pos = lastIndexOf(dirChar); if (pos >= 0) { return mid(pos + 1); @@ -396,7 +340,7 @@ String String::fileName(QChar dirChar) const String String::fileNameWithoutExtension() const { String name = fileName(); - size_type pos = name.lastIndexOf('.'); + int pos = name.lastIndexOf('.'); if (pos > 0) { return name.mid(0, pos); @@ -406,8 +350,8 @@ String String::fileNameWithoutExtension() const String String::fileNameExtension() const { - size_type pos = lastIndexOf('.'); - size_type slashPos = lastIndexOf('/'); + int pos = lastIndexOf('.'); + int slashPos = lastIndexOf('/'); if (pos > 0) { // If there is a directory included, make sure there it at least @@ -422,7 +366,7 @@ String String::fileNameExtension() const String String::fileNamePath(QChar dirChar) const { - size_type pos = lastIndexOf(dirChar); + int pos = lastIndexOf(dirChar); if (pos >= 0) { return mid(0, pos); @@ -435,34 +379,34 @@ String String::fileNameAndPathWithoutExtension(QChar dirChar) const return fileNamePath(dirChar) / fileNameWithoutExtension(); } -bool String::containsWord(String const &word) const +bool String::containsWord(const String &word) const { if (word.isEmpty()) { return false; } - return QRegularExpression(QString("\\b%1\\b").arg(word)).match(*this).hasMatch(); + return RegExp(stringf("\\b%s\\b", word.c_str())).match(*this).hasMatch(); } -dint String::compareWithCase(String const &str) const +dint String::compareWithCase(const String &other) const { - return compare(str, Qt::CaseSensitive); + return cmpSc_String(&_str, other, &iCaseSensitive); } -dint String::compareWithoutCase(String const &str) const +dint String::compareWithoutCase(const String &other) const { - return compare(str, Qt::CaseInsensitive); + return cmpSc_String(&_str, other, &iCaseInsensitive); } -dint String::compareWithoutCase(String const &str, int n) const +dint String::compareWithoutCase(const String &other, int n) const { - return leftRef(n).compare(str.leftRef(n), Qt::CaseInsensitive); + return cmpNSc_String(&_str, other, n, &iCaseInsensitive); } -int String::commonPrefixLength(String const &str, CaseSensitivity sensitivity) const +int String::commonPrefixLength(const String &str, CaseSensitivity sensitivity) const { int count = 0; - int len = qMin(str.size(), size()); + size_t len = std::min(str.size(), size()); for (int i = 0; i < len; ++i, ++count) { if (sensitivity == CaseSensitive) @@ -477,43 +421,75 @@ int String::commonPrefixLength(String const &str, CaseSensitivity sensitivity) c return count; } -dint String::compareWithCase(QChar const *a, QChar const *b, dsize count) // static +String::const_iterator String::begin() const { - return QString(a).leftRef(count).compare(QString(b).leftRef(count), Qt::CaseSensitive); + const_iterator i; + init_StringConstIterator(&i.iter, &_str); + return i; } -bool String::equals(QChar const *a, QChar const *b, dsize count) // static +String::const_iterator String::end() const { - while (count--) + const_iterator i; + init_StringConstIterator(&i.iter, &_str); + i.iter.pos = i.iter.next = constEnd_String(&_str); + i.iter.remaining = 0; + return i; +} + +String::const_reverse_iterator String::rbegin() const { - // Both strings the same length? - if (a->isNull() && b->isNull()) break; - // Mismatch? - if (*a != *b) return false; - // Advance. - a++; - b++; + const_reverse_iterator i; + init_StringReverseConstIterator(&i.iter, &_str); + return i; } - return true; + +String::const_reverse_iterator String::rend() const +{ + const_reverse_iterator i; + init_StringReverseConstIterator(&i.iter, &_str); + i.iter.pos = i.iter.next = constBegin_String(&_str); + i.iter.remaining = 0; + return i; } -void String::skipSpace(String::const_iterator &i, String::const_iterator const &end) +//dint String::compareWithCase(QChar const *a, QChar const *b, dsize count) // static +//{ +// return QString(a).leftRef(count).compare(QString(b).leftRef(count), Qt::CaseSensitive); +//} + +//bool String::equals(QChar const *a, QChar const *b, dsize count) // static +//{ +// while (count--) +// { +// // Both strings the same length? +// if (a->isNull() && b->isNull()) break; +// // Mismatch? +// if (*a != *b) return false; +// // Advance. +// a++; +// b++; +// } +// return true; +//} + +void String::skipSpace(const_iterator &i, const const_iterator &end) { - while (i != end && (*i).isSpace()) ++i; + while (i != end && iswspace(*i)) ++i; } -String String::format(String format, ...) +String String::format(const char *format, ...) { va_list args; Block buffer; - int neededSize = 1024; + int neededSize = 512; - forever + for (;;) { buffer.resize(neededSize); va_start(args, format); - int count = vsnprintf((char *) buffer.data(), buffer.size(), format.toUtf8(), args); + int count = vsnprintf(reinterpret_cast(buffer.data()), buffer.size(), format, args); va_end(args); if (count >= 0 && count < neededSize) @@ -534,45 +510,43 @@ String String::format(String format, ...) return fromUtf8(buffer); } -// Seems like an ommission on the part of QChar... -static inline bool isSign(QChar const &ch) +static inline bool isSign(Char ch) { return ch == '-' || ch == '+'; } -dint String::toInt(bool *ok, int base, IntConversionFlags flags) const +dint String::toInt(bool *ok, int base, duint flags) const { - String token = leftStrip(); - - if (flags & AllowSuffix) + char *endp; + auto value = std::strtol(*this, &endp, base); + if (ok) *ok = (errno != ERANGE); + if (!(flags & AllowSuffix) && !(*endp == 0 && isspace(*endp))) { - // Truncate at the first non-numeric, non-notation or sign character. - int endOfNumber = 0; - while (endOfNumber < token.size() && - (token.at(endOfNumber).isDigit() || (endOfNumber == 0 && isSign(token.at(endOfNumber))) || - ((base == 0 || base == 16) && endOfNumber <= 1 && - (token.at(endOfNumber) == QChar('x') || token.at(endOfNumber) == QChar('X'))))) - { - ++endOfNumber; + // Suffix not allowed; consider this a failure. + if (ok) *ok = false; + value = 0; } - token.truncate(endOfNumber); - } - - return token.QString::toInt(ok, base); + return dint(value); } duint32 String::toUInt32(bool *ok, int base) const { - return QString::toUInt(ok, base); + char *endp; + const auto value = std::strtoul(*this, nullptr, base); + if (ok) *ok = (errno != ERANGE); + return duint32(value); } -String String::addLinePrefix(String const &prefix) const +String String::addLinePrefix(const String &prefix) const { String result; - for (auto const &str : QString::split(QChar('\n'))) + iRangecc str{constBegin_String(&_str), constEnd_String(&_str)}; + iRangecc range{}; + while (nextSplit_Rangecc(&str, "\n", &range)) { - if (!result.isEmpty()) result += "\n"; - result += prefix + str; + if (!result.empty()) result += '\n'; + result += prefix; + result += String(range.start, range.end); } return result; } @@ -590,16 +564,16 @@ Block String::toPercentEncoding() const return QUrl::toPercentEncoding(*this); } -String String::truncateWithEllipsis(int maxLength) const +String String::truncateWithEllipsis(dsize maxLength) const { if (size() <= maxLength) { return *this; } - return left(maxLength/2 - 1) + "..." + right(maxLength/2 - 1); + return mid(0, maxLength/2 - 1) + "..." + right(maxLength/2 - 1); } -void String::advanceFormat(String::const_iterator &i, String::const_iterator const &end) +void String::advanceFormat(const_iterator &i, const const_iterator &end) { ++i; if (i == end) @@ -609,33 +583,32 @@ void String::advanceFormat(String::const_iterator &i, String::const_iterator con } } -String String::join(StringList const &stringList, String const &sep) +String String::join(const StringList &stringList, const String &sep) { if (stringList.isEmpty()) return {}; - String joined; - QTextStream os(&joined); - os << stringList.at(0); + String joined = stringList.at(0); for (int i = 1; i < stringList.size(); ++i) { - os << sep << stringList.at(i); + joined += sep; + joined += stringList.at(i); } return joined; } -String String::patternFormat(String::const_iterator &formatIter, - String::const_iterator const &formatEnd, - IPatternArg const &arg) +String String::patternFormat(const_iterator & formatIter, + const const_iterator &formatEnd, + const IPatternArg & arg) { advanceFormat(formatIter, formatEnd); - QString result; - QTextStream output(&result); + String result; + //QTextStream output(&result); // An argument comes here. bool rightAlign = true; - dint maxWidth = 0; - dint minWidth = 0; + dsize maxWidth = 0; + dsize minWidth = 0; DE_ASSERT(*formatIter != '%'); @@ -646,7 +619,7 @@ String String::patternFormat(String::const_iterator &formatIter, advanceFormat(formatIter, formatEnd); } String::const_iterator k = formatIter; - while ((*formatIter).isDigit()) + while (iswdigit(*formatIter)) { advanceFormat(formatIter, formatEnd); } @@ -660,7 +633,7 @@ String String::patternFormat(String::const_iterator &formatIter, advanceFormat(formatIter, formatEnd); k = formatIter; // There's also a maxWidth. - while ((*formatIter).isDigit()) + while (iswdigit(*formatIter)) { advanceFormat(formatIter, formatEnd); } @@ -668,42 +641,44 @@ String String::patternFormat(String::const_iterator &formatIter, } // Finally, the type formatting. - switch ((*formatIter).toLatin1()) + switch (*formatIter) { case 's': - output << arg.asText(); + result += arg.asText(); break; case 'b': - output << (int(arg.asNumber())? "true" : "false"); + result += (int(arg.asNumber())? "True" : "False"); break; case 'c': - output << QChar(ushort(arg.asNumber())); + result += Char(arg.asNumber()); break; case 'i': case 'd': - output << dint64(arg.asNumber()); + result += format("%lli", dint64(arg.asNumber())); break; case 'u': - output << duint64(arg.asNumber()); + result += format("%llu", duint64(arg.asNumber())); break; case 'X': - output << uppercasedigits; + result += format("%llX", dint64(arg.asNumber())); + break; + case 'x': - output << "0x" << hex << dint64(arg.asNumber()) << dec << lowercasedigits; + result += format("%llx", dint64(arg.asNumber())); break; case 'p': - output << "0x" << hex << dintptr(arg.asNumber()) << dec; + result += format("%p", dintptr(arg.asNumber())); break; case 'f': // Max width is interpreted as the number of decimal places. - output << fixed << qSetRealNumberPrecision(maxWidth? maxWidth : 3) << arg.asNumber(); + result += format(stringf("%%.%df", maxWidth? maxWidth : 3).c_str(), arg.asNumber()); maxWidth = 0; break; @@ -712,13 +687,11 @@ String String::patternFormat(String::const_iterator &formatIter, "Unknown format character '" + String(1, *formatIter) + "'"); } - output.flush(); - // Align and fit. if (maxWidth && result.size() > maxWidth) { // Cut it. - result = result.mid(!rightAlign? 0 : result.size() - maxWidth, maxWidth); + result = result.substr(!rightAlign ? 0 : (result.size() - maxWidth), maxWidth); } if (result.size() < minWidth) { @@ -738,64 +711,83 @@ String String::patternFormat(String::const_iterator &formatIter, Block String::toUtf8() const { - return QString::toUtf8(); + return Block(&_str.chars); } Block String::toLatin1() const { - return QString::toLatin1(); -} - -String String::fromUtf8(IByteArray const &byteArray) + // Non-8-bit characters are simply filtered out. + Block latin; + for (iChar ch : *this) { - Block const bytes(byteArray); - return QString::fromUtf8(reinterpret_cast(bytes.data()), int(bytes.size())); + if (ch < 256) latin.append(Block::Byte(ch)); +} + return latin; } -String String::fromUtf8(QByteArray const &byteArray) +String String::fromUtf8(const IByteArray &byteArray) { - return QString::fromUtf8(byteArray); + String s; + setBlock_String(&s._str, Block(byteArray)); + return s; } -String String::fromUtf8(Block const &block) +String String::fromUtf8(const Block &block) { - return QString::fromUtf8(block); + String s; + setBlock_String(&s._str, block); + return s; } String String::fromUtf8(char const *nullTerminatedCStr) { - return QString::fromUtf8(nullTerminatedCStr); + return String(nullTerminatedCStr); } -String String::fromLatin1(IByteArray const &byteArray) +String String::fromLatin1(const IByteArray &byteArray) { - return QString::fromLatin1(reinterpret_cast(Block(byteArray).data())); + const Block bytes(byteArray); + return String(reinterpret_cast(bytes.data()), bytes.size()); } -String String::fromCP437(IByteArray const &byteArray) +String String::fromCP437(const IByteArray &byteArray) { - Block const chars(byteArray); + const Block chars(byteArray); String conv; - conv.reserve(byteArray.size()); for (dbyte ch : chars) { - conv.append(codePage437ToUnicode(ch)); + conv += Char(codePage437ToUnicode(ch)); } return conv; } -String String::fromPercentEncoding(Block const &percentEncoded) // static +String String::fromPercentEncoding(const Block &percentEncoded) // static { return QUrl::fromPercentEncoding(percentEncoded); } -size_t qchar_strlen(QChar const *str) -{ - if (!str) return 0; - - size_t len = 0; - while (str->unicode() != 0) { ++str; ++len; } - return len; +//size_t qchar_strlen(Char const *str) +//{ +// if (!str) return 0; + +// size_t len = 0; +// while (str->unicode() != 0) { ++str; ++len; } +// return len; +//} + +std::string stringf(const char *format, ...) +{ + // First determine the length of the result. + va_list args1, args2; + va_start(args1, format); + va_copy(args2, args1); + const int requiredLength = vsprintf(nullptr, format, args1); + va_end(args1); + // Format the output to a new string. + std::string str(requiredLength); + vsprintf(str.data(), format, args2); + va_end(args2); + return str; } } // namespace de diff --git a/doomsday/libs/core/src/error.cpp b/doomsday/libs/core/src/error.cpp index c2753b9190..01b522ef36 100644 --- a/doomsday/libs/core/src/error.cpp +++ b/doomsday/libs/core/src/error.cpp @@ -20,10 +20,11 @@ #include "de/error.h" #include "core/logtextstyle.h" +#include "de/String" namespace de { -Error::Error(QString const &where, QString const &message) +Error::Error(const std::string &where, const std::string &message) : std::runtime_error(QString("%1(in " _E(m) "%2" _E(.) ")" _E(.) " %3") .arg(TEXT_STYLE_SECTION) .arg(where) @@ -32,24 +33,23 @@ Error::Error(QString const &where, QString const &message) , _name("") {} -QString Error::name() const +std::string Error::name() const { if (!_name.size()) return "Error"; - return QString::fromStdString(_name); + return _name; } -QString Error::asText() const +std::string Error::asText() const { - return QString("%1[%2]" _E(.) " %4") - .arg(TEXT_STYLE_SECTION) - .arg(name()) - .arg(std::runtime_error::what()); + return String::format("%s[%s] " _E(.) " %s", TEXT_STYLE_SECTION, + _name.c_str(), + std::runtime_error::what()); } -void Error::setName(QString const &name) +void Error::setName(const std::string &name) { if (_name.size()) _name += "_"; - _name += name.toStdString(); + _name += name; } } // namespace de