| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Linguist of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "proitems.h" |
| |
| #include <qfileinfo.h> |
| #include <qset.h> |
| #include <qstringlist.h> |
| #include <qtextstream.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| // from qhash.cpp |
| uint ProString::hash(const QChar *p, int n) |
| { |
| uint h = 0; |
| |
| while (n--) { |
| h = (h << 4) + (*p++).unicode(); |
| h ^= (h & 0xf0000000) >> 23; |
| h &= 0x0fffffff; |
| } |
| return h; |
| } |
| |
| ProString::ProString() : |
| m_offset(0), m_length(0), m_file(0), m_hash(0x80000000) |
| { |
| } |
| |
| ProString::ProString(const ProString &other) : |
| m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(other.m_hash) |
| { |
| } |
| |
| ProString::ProString(const ProString &other, OmitPreHashing) : |
| m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(0x80000000) |
| { |
| } |
| |
| ProString::ProString(const QString &str, DoPreHashing) : |
| m_string(str), m_offset(0), m_length(str.length()), m_file(0) |
| { |
| updatedHash(); |
| } |
| |
| ProString::ProString(const QString &str) : |
| m_string(str), m_offset(0), m_length(str.length()), m_file(0), m_hash(0x80000000) |
| { |
| } |
| |
| ProString::ProString(const QStringRef &str) : |
| m_string(*str.string()), m_offset(str.position()), m_length(str.size()), m_file(0), m_hash(0x80000000) |
| { |
| } |
| |
| ProString::ProString(const char *str, DoPreHashing) : |
| m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0) |
| { |
| updatedHash(); |
| } |
| |
| ProString::ProString(const char *str) : |
| m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0), m_hash(0x80000000) |
| { |
| } |
| |
| ProString::ProString(const QString &str, int offset, int length, DoPreHashing) : |
| m_string(str), m_offset(offset), m_length(length), m_file(0) |
| { |
| updatedHash(); |
| } |
| |
| ProString::ProString(const QString &str, int offset, int length, uint hash) : |
| m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(hash) |
| { |
| } |
| |
| ProString::ProString(const QString &str, int offset, int length) : |
| m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(0x80000000) |
| { |
| } |
| |
| void ProString::setValue(const QString &str) |
| { |
| m_string = str, m_offset = 0, m_length = str.length(), m_hash = 0x80000000; |
| } |
| |
| uint ProString::updatedHash() const |
| { |
| return (m_hash = hash(m_string.constData() + m_offset, m_length)); |
| } |
| |
| uint qHash(const ProString &str) |
| { |
| if (!(str.m_hash & 0x80000000)) |
| return str.m_hash; |
| return str.updatedHash(); |
| } |
| |
| ProKey::ProKey(const QString &str) : |
| ProString(str, DoHash) |
| { |
| } |
| |
| ProKey::ProKey(const char *str) : |
| ProString(str, DoHash) |
| { |
| } |
| |
| ProKey::ProKey(const QString &str, int off, int len) : |
| ProString(str, off, len, DoHash) |
| { |
| } |
| |
| ProKey::ProKey(const QString &str, int off, int len, uint hash) : |
| ProString(str, off, len, hash) |
| { |
| } |
| |
| void ProKey::setValue(const QString &str) |
| { |
| m_string = str, m_offset = 0, m_length = str.length(); |
| updatedHash(); |
| } |
| |
| QString ProString::toQString() const |
| { |
| return m_string.mid(m_offset, m_length); |
| } |
| |
| QString &ProString::toQString(QString &tmp) const |
| { |
| return tmp.setRawData(m_string.constData() + m_offset, m_length); |
| } |
| |
| /*! |
| * \brief ProString::prepareExtend |
| * \param extraLen number of new characters to be added |
| * \param thisTarget offset to which current contents should be moved |
| * \param extraTarget offset at which new characters will be added |
| * \return pointer to storage location for new characters |
| * |
| * Prepares the string for adding new characters. |
| * If the string is detached and has enough space, it will be changed in place. |
| * Otherwise, it will be replaced with a new string object, thus detaching. |
| * In either case, the hash will be reset. |
| */ |
| QChar *ProString::prepareExtend(int extraLen, int thisTarget, int extraTarget) |
| { |
| if (m_string.isDetached() && m_length + extraLen <= m_string.capacity()) { |
| m_string.reserve(0); // Prevent the resize() below from reallocating |
| QChar *ptr = (QChar *)m_string.constData(); |
| if (m_offset != thisTarget) |
| memmove(ptr + thisTarget, ptr + m_offset, m_length * 2); |
| ptr += extraTarget; |
| m_offset = 0; |
| m_length += extraLen; |
| m_string.resize(m_length); |
| m_hash = 0x80000000; |
| return ptr; |
| } else { |
| QString neu(m_length + extraLen, Qt::Uninitialized); |
| QChar *ptr = (QChar *)neu.constData(); |
| memcpy(ptr + thisTarget, m_string.constData() + m_offset, m_length * 2); |
| ptr += extraTarget; |
| *this = ProString(neu); |
| return ptr; |
| } |
| } |
| |
| ProString &ProString::prepend(const ProString &other) |
| { |
| if (other.m_length) { |
| if (!m_length) { |
| *this = other; |
| } else { |
| QChar *ptr = prepareExtend(other.m_length, other.m_length, 0); |
| memcpy(ptr, other.constData(), other.m_length * 2); |
| if (!m_file) |
| m_file = other.m_file; |
| } |
| } |
| return *this; |
| } |
| |
| ProString &ProString::append(const QLatin1String other) |
| { |
| const char *latin1 = other.latin1(); |
| int size = other.size(); |
| if (size) { |
| QChar *ptr = prepareExtend(size, 0, m_length); |
| for (int i = 0; i < size; i++) |
| *ptr++ = QLatin1Char(latin1[i]); |
| } |
| return *this; |
| } |
| |
| ProString &ProString::append(QChar other) |
| { |
| QChar *ptr = prepareExtend(1, 0, m_length); |
| *ptr = other; |
| return *this; |
| } |
| |
| // If pending != 0, prefix with space if appending to non-empty non-pending |
| ProString &ProString::append(const ProString &other, bool *pending) |
| { |
| if (other.m_length) { |
| if (!m_length) { |
| *this = other; |
| } else { |
| QChar *ptr; |
| if (pending && !*pending) { |
| ptr = prepareExtend(1 + other.m_length, 0, m_length); |
| *ptr++ = 32; |
| } else { |
| ptr = prepareExtend(other.m_length, 0, m_length); |
| } |
| memcpy(ptr, other.m_string.constData() + other.m_offset, other.m_length * 2); |
| if (other.m_file) |
| m_file = other.m_file; |
| } |
| if (pending) |
| *pending = true; |
| } |
| return *this; |
| } |
| |
| ProString &ProString::append(const ProStringList &other, bool *pending, bool skipEmpty1st) |
| { |
| if (const int sz = other.size()) { |
| int startIdx = 0; |
| if (pending && !*pending && skipEmpty1st && other.at(0).isEmpty()) { |
| if (sz == 1) |
| return *this; |
| startIdx = 1; |
| } |
| if (!m_length && sz == startIdx + 1) { |
| *this = other.at(startIdx); |
| } else { |
| int totalLength = sz - startIdx; |
| for (int i = startIdx; i < sz; ++i) |
| totalLength += other.at(i).size(); |
| bool putSpace = false; |
| if (pending && !*pending && m_length) |
| putSpace = true; |
| else |
| totalLength--; |
| |
| QChar *ptr = prepareExtend(totalLength, 0, m_length); |
| for (int i = startIdx; i < sz; ++i) { |
| if (putSpace) |
| *ptr++ = 32; |
| else |
| putSpace = true; |
| const ProString &str = other.at(i); |
| memcpy(ptr, str.m_string.constData() + str.m_offset, str.m_length * 2); |
| ptr += str.m_length; |
| } |
| if (other.last().m_file) |
| m_file = other.last().m_file; |
| } |
| if (pending) |
| *pending = true; |
| } |
| return *this; |
| } |
| |
| QString operator+(const ProString &one, const ProString &two) |
| { |
| if (two.m_length) { |
| if (!one.m_length) { |
| return two.toQString(); |
| } else { |
| QString neu(one.m_length + two.m_length, Qt::Uninitialized); |
| ushort *ptr = (ushort *)neu.constData(); |
| memcpy(ptr, one.m_string.constData() + one.m_offset, one.m_length * 2); |
| memcpy(ptr + one.m_length, two.m_string.constData() + two.m_offset, two.m_length * 2); |
| return neu; |
| } |
| } |
| return one.toQString(); |
| } |
| |
| |
| ProString ProString::mid(int off, int len) const |
| { |
| ProString ret(*this, NoHash); |
| if (off > m_length) |
| off = m_length; |
| ret.m_offset += off; |
| ret.m_length -= off; |
| if ((uint)ret.m_length > (uint)len) // Unsigned comparison to interpret < 0 as infinite |
| ret.m_length = len; |
| return ret; |
| } |
| |
| ProString ProString::trimmed() const |
| { |
| ProString ret(*this, NoHash); |
| int cur = m_offset; |
| int end = cur + m_length; |
| const QChar *data = m_string.constData(); |
| for (; cur < end; cur++) |
| if (!data[cur].isSpace()) { |
| // No underrun check - we know there is at least one non-whitespace |
| while (data[end - 1].isSpace()) |
| end--; |
| break; |
| } |
| ret.m_offset = cur; |
| ret.m_length = end - cur; |
| return ret; |
| } |
| |
| QTextStream &operator<<(QTextStream &t, const ProString &str) |
| { |
| t << str.toQStringRef(); |
| return t; |
| } |
| |
| static QString ProStringList_join(const ProStringList &this_, const QChar *sep, const int sepSize) |
| { |
| int totalLength = 0; |
| const int sz = this_.size(); |
| |
| for (int i = 0; i < sz; ++i) |
| totalLength += this_.at(i).size(); |
| |
| if (sz) |
| totalLength += sepSize * (sz - 1); |
| |
| QString res(totalLength, Qt::Uninitialized); |
| QChar *ptr = (QChar *)res.constData(); |
| for (int i = 0; i < sz; ++i) { |
| if (i) { |
| memcpy(ptr, sep, sepSize * sizeof(QChar)); |
| ptr += sepSize; |
| } |
| const ProString &str = this_.at(i); |
| memcpy(ptr, str.constData(), str.size() * sizeof(QChar)); |
| ptr += str.size(); |
| } |
| return res; |
| } |
| |
| QString ProStringList::join(const ProString &sep) const |
| { |
| return ProStringList_join(*this, sep.constData(), sep.size()); |
| } |
| |
| QString ProStringList::join(const QString &sep) const |
| { |
| return ProStringList_join(*this, sep.constData(), sep.size()); |
| } |
| |
| QString ProStringList::join(QChar sep) const |
| { |
| return ProStringList_join(*this, &sep, 1); |
| } |
| |
| void ProStringList::removeAll(const ProString &str) |
| { |
| for (int i = size(); --i >= 0; ) |
| if (at(i) == str) |
| remove(i); |
| } |
| |
| void ProStringList::removeAll(const char *str) |
| { |
| for (int i = size(); --i >= 0; ) |
| if (at(i) == str) |
| remove(i); |
| } |
| |
| void ProStringList::removeEach(const ProStringList &value) |
| { |
| for (const ProString &str : value) { |
| if (isEmpty()) |
| break; |
| if (!str.isEmpty()) |
| removeAll(str); |
| } |
| } |
| |
| void ProStringList::removeEmpty() |
| { |
| for (int i = size(); --i >= 0;) |
| if (at(i).isEmpty()) |
| remove(i); |
| } |
| |
| void ProStringList::removeDuplicates() |
| { |
| int n = size(); |
| int j = 0; |
| QSet<ProString> seen; |
| seen.reserve(n); |
| for (int i = 0; i < n; ++i) { |
| const ProString &s = at(i); |
| if (seen.contains(s)) |
| continue; |
| seen.insert(s); |
| if (j != i) |
| (*this)[j] = s; |
| ++j; |
| } |
| if (n != j) |
| erase(begin() + j, end()); |
| } |
| |
| void ProStringList::insertUnique(const ProStringList &value) |
| { |
| for (const ProString &str : value) |
| if (!str.isEmpty() && !contains(str)) |
| append(str); |
| } |
| |
| ProStringList::ProStringList(const QStringList &list) |
| { |
| reserve(list.size()); |
| for (const QString &str : list) |
| *this << ProString(str); |
| } |
| |
| QStringList ProStringList::toQStringList() const |
| { |
| QStringList ret; |
| ret.reserve(size()); |
| for (const auto &e : *this) |
| ret.append(e.toQString()); |
| return ret; |
| } |
| |
| bool ProStringList::contains(const ProString &str, Qt::CaseSensitivity cs) const |
| { |
| for (int i = 0; i < size(); i++) |
| if (!at(i).compare(str, cs)) |
| return true; |
| return false; |
| } |
| |
| bool ProStringList::contains(const QStringRef &str, Qt::CaseSensitivity cs) const |
| { |
| for (int i = 0; i < size(); i++) |
| if (!at(i).toQStringRef().compare(str, cs)) |
| return true; |
| return false; |
| } |
| |
| bool ProStringList::contains(const char *str, Qt::CaseSensitivity cs) const |
| { |
| for (int i = 0; i < size(); i++) |
| if (!at(i).compare(str, cs)) |
| return true; |
| return false; |
| } |
| |
| ProFile::ProFile(int id, const QString &fileName) |
| : m_refCount(1), |
| m_fileName(fileName), |
| m_id(id), |
| m_ok(true), |
| m_hostBuild(false) |
| { |
| if (!fileName.startsWith(QLatin1Char('('))) |
| m_directoryName = QFileInfo( // qmake sickness: canonicalize only the directory! |
| fileName.left(fileName.lastIndexOf(QLatin1Char('/')))).canonicalFilePath(); |
| } |
| |
| ProFile::~ProFile() |
| { |
| } |
| |
| ProString ProFile::getStr(const ushort *&tPtr) |
| { |
| uint len = *tPtr++; |
| ProString ret(items(), tPtr - tokPtr(), len); |
| ret.setSource(m_id); |
| tPtr += len; |
| return ret; |
| } |
| |
| ProKey ProFile::getHashStr(const ushort *&tPtr) |
| { |
| uint hash = *tPtr++; |
| hash |= (uint)*tPtr++ << 16; |
| uint len = *tPtr++; |
| ProKey ret(items(), tPtr - tokPtr(), len, hash); |
| tPtr += len; |
| return ret; |
| } |
| |
| QT_END_NAMESPACE |