From eb8a3b5ea7cea8a4deb8cefa3deef3271215145e Mon Sep 17 00:00:00 2001 From: S David <2100425+s-daveb@users.noreply.github.com> Date: Fri, 3 May 2024 22:20:24 -0400 Subject: [PATCH] Add clang-format, clang-tidy; reindent files, increas readability --- .clang-format | 41 ++ .clang-tidy | 79 ++++ QJsonModel.cpp | 916 +++++++++++++++++++++----------------- include/QJsonModel.hpp | 146 +++--- include/details/QUtf8.hpp | 340 +++++++------- 5 files changed, 900 insertions(+), 622 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..cbea09b --- /dev/null +++ b/.clang-format @@ -0,0 +1,41 @@ +--- +BasedOnStyle: Mozilla +Language: Cpp + +IndentWidth: 8 +UseTab: AlignWithSpaces +ColumnLimit: 81 +AccessModifierOffset: -4 +BinPackParameters: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +PenaltyReturnTypeOnItsOwnLine: 50 +ContinuationIndentWidth: 4 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +AllowAllArgumentsOnNextLine: true +AlignAfterOpenBracket: BlockIndent + +BreakBeforeBraces: WebKit +#BraceWrapping: +# AfterCaseLabel: false +# AfterClass: true +# AfterControlStatement: MultiLine +# AfterEnum: false +# AfterFunction: true +# AfterNamespace: false +# AfterObjCDeclaration: false +# AfterStruct: true +# AfterUnion: true +# AfterExternBlock: false +# +# BeforeCatch: true +# BeforeElse: false +# BeforeLambdaBody: false +# BeforeWhile: false +# IndentBraces: false +# SplitEmptyFunction: true +# SplitEmptyRecord: false +# SplitEmptyNamespace: true +# +--- diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..276542d --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,79 @@ +HeaderFilterRegex: '.*hpp' +Checks: '-*,readability-identifier-naming,readability-identifier-length,readability-function-cognitive-complexity,google-readability-casting,-modernize-use-trailing-return-type,modernize-use-default-member-init,modernize-use-uncaught-exceptions,modernize-type-traits,modernize-use-override,misc-non-copyable-objects' + +CheckOptions: + # Modernize constructors + - { key: modernize-use-default-member-init.UseAssignment, value : false} + - { key: modernize-use-default-member-init.IgnoreMacros, value : false} + + # Warn about complex functions + - { key: readability-function-cognitive-complexity.Threshold, value: 10 } + - { key: readability-function-cognitive-complexity.DescribeBasicIncrements, value: true } + - { key: readability-function-cognitive-complexity.IgnoreMacros, value: true } + + # Minimum Variable Length + - { key: readability-identifier-length.MinimumVariableNameLength, value: 3 } + - { key: readability-identifier-length.IgnoredVariableNames, value: "^(i|j|n|it)$" } + + # Minimum Parameter Length + - { key: readability-identifier-length.MinimumParameterNameLength, value: 3 } + - { key: readability-identifier-length.IgnoredParameterNames, value: "^(i|j|n|it)$" } + + # Minimum Loop Counter Length + - { key: readability-identifier-length.MinimumLoopCounterNameLength, value: 3 } + - { key: readability-identifier-length.IgnoredLoopCounterNames, value: "^(i|j|n|it)$" } + + # Minimum ExceptionName Length: + - { key: readability-identifier-length.MinimumExceptionNameLength, value: 3 } + - { key: readability-identifier-length.IgnoredExceptionVariableNames, value: "^[e]$" } + + # Class Names + - { key: readability-identifier-naming.ClassCase, value: CamelCase } + - { key: readability-identifier-naming.StructCase, value: CamelCase } + - { key: readability-identifier-naming.EnumCase, value: CamelCase } + + - { key: readability-identifier-naming.EnumIgnoredRegexp, value: '^.*_t$' } + + # Abstract Class Name + # - { key: readability-identifier-naming.AbstractClassPrefix,value: 'I' } + # - { key: readability-identifier-naming.AbstractClassIgnoredRegexp, + # value: '^.*able$|^.*Base$|^Abstract.*|^Component$' } + + # Template Parameters + - { key: readability-identifier-naming.TypeTemplateParameterPrefix, + value: '' } + - { key: readability-identifier-naming.TypeTemplateParameterSuffix, + value: '' } + - { key: readability-identifier-naming.TypeTemplateParameterCase, + value: CamelCase } + - { key: readability-identifier-naming.TypeTemplateParameterIgnoredRegexp, value: "^T$" } + + # TypeAlias Rules + - { key: readability-identifier-naming.TypeAliasCase, value: CamelCase } + - { key: readability-identifier-naming.TypeAliasIgnoredRegexp, + value: '.*_t|string|.*_string' } + + # Function Names + - { key: readability-identifier-naming.FunctionCase, value: camelBack } + - { key: readability-identifier-naming.PublicMethodCase, value: camelBack } + - { key: readability-identifier-naming.PrivateMethodCase, value: camelBack } + + # Variable Names + - { key: readability-identifier-naming.VariableCase, value: camelBack } + - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } + + # Constants and Enum Values + #- { key: readability-identifier-naming.ConstantPrefix, value: 'k' } + - { key: readability-identifier-naming.ConstantCase, value: camelBack } + + - { key: readability-identifier-naming.EnumConstantPrefix, value: '' } + - { key: readability-identifier-naming.EnumConstantCase, value: CamelCase } + + # Constant Expression + - { key: readability-identifier-naming.ConstexprVariablePrefix, value: 'k' } + - { key: readability-identifier-naming.ConstexprVariableCase, value: Camel_Snake_Case } + + - { key: readability-identifier-naming.ConstexprFunctionCase, value: Camel_Snake_Case } + - { key: readability-identifier-naming.ConstexprMethodCase, value: Camel_Snake_Case } + +# vim: set ts=4 noet sw=4 sts=0 colorcolumn=100 : diff --git a/QJsonModel.cpp b/QJsonModel.cpp index 9c43555..eeafc38 100644 --- a/QJsonModel.cpp +++ b/QJsonModel.cpp @@ -23,483 +23,595 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -// NOLINTBEGIN #include "QJsonModel.hpp" #include #include #include -inline bool contains(const QStringList &list, const QString &value) { - for (auto val : list) - if (value.contains(val, Qt::CaseInsensitive)) - return true; +inline bool contains(const QStringList& list, const QString& value) +{ + for (auto val : list) + if (value.contains(val, Qt::CaseInsensitive)) + return true; - return false; + return false; } -QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent) { mParent = parent; } - -QJsonTreeItem::~QJsonTreeItem() { qDeleteAll(mChilds); } - -void QJsonTreeItem::appendChild(QJsonTreeItem *item) { mChilds.append(item); } - -QJsonTreeItem *QJsonTreeItem::child(int row) { return mChilds.value(row); } - -QJsonTreeItem *QJsonTreeItem::parent() { return mParent; } - -int QJsonTreeItem::childCount() const { return mChilds.count(); } - -int QJsonTreeItem::row() const { - if (mParent) - return mParent->mChilds.indexOf(const_cast(this)); - - return 0; +QJsonTreeItem::QJsonTreeItem(QJsonTreeItem* parent) +{ + mParent = parent; } -void QJsonTreeItem::setKey(const QString &key) { mKey = key; } +QJsonTreeItem::~QJsonTreeItem() +{ + qDeleteAll(mChilds); +} -void QJsonTreeItem::setValue(const QVariant &value) { mValue = value; } +void QJsonTreeItem::appendChild(QJsonTreeItem* item) +{ + mChilds.append(item); +} -void QJsonTreeItem::setType(const QJsonValue::Type &type) { mType = type; } +QJsonTreeItem* QJsonTreeItem::child(int row) +{ + return mChilds.value(row); +} -QString QJsonTreeItem::key() const { return mKey; } +QJsonTreeItem* QJsonTreeItem::parent() +{ + return mParent; +} -QVariant QJsonTreeItem::value() const { return mValue; } +int QJsonTreeItem::childCount() const +{ + return mChilds.count(); +} -QJsonValue::Type QJsonTreeItem::type() const { return mType; } +int QJsonTreeItem::row() const +{ + if (mParent) + return mParent->mChilds.indexOf(const_cast(this) + ); -QJsonTreeItem *QJsonTreeItem::load(const QJsonValue &value, - const QStringList &exceptions, - QJsonTreeItem *parent) { - QJsonTreeItem *rootItem = new QJsonTreeItem(parent); - rootItem->setKey("root"); + return 0; +} - if (value.isObject()) { - // Get all QJsonValue childs - const QStringList keys = - value.toObject().keys(); // To prevent clazy-range warning - for (const QString &key : keys) { - if (contains(exceptions, key)) { - continue; - } - QJsonValue v = value.toObject().value(key); - QJsonTreeItem *child = load(v, exceptions, rootItem); - child->setKey(key); - child->setType(v.type()); - rootItem->appendChild(child); - } - } else if (value.isArray()) { - // Get all QJsonValue childs - int index = 0; - const QJsonArray array = value.toArray(); // To prevent clazy-range warning - for (const QJsonValue &v : array) { - QJsonTreeItem *child = load(v, exceptions, rootItem); - child->setKey(QString::number(index)); - child->setType(v.type()); - rootItem->appendChild(child); - ++index; - } - } else { - rootItem->setValue(value.toVariant()); - rootItem->setType(value.type()); - } +void QJsonTreeItem::setKey(const QString& key) +{ + mKey = key; +} - return rootItem; +void QJsonTreeItem::setValue(const QVariant& value) +{ + mValue = value; +} + +void QJsonTreeItem::setType(const QJsonValue::Type& type) +{ + mType = type; +} + +QString QJsonTreeItem::key() const +{ + return mKey; +} + +QVariant QJsonTreeItem::value() const +{ + return mValue; +} + +QJsonValue::Type QJsonTreeItem::type() const +{ + return mType; +} + +QJsonTreeItem* QJsonTreeItem::load( + const QJsonValue& value, const QStringList& exceptions, QJsonTreeItem* parent +) +{ + QJsonTreeItem* rootItem = new QJsonTreeItem(parent); + rootItem->setKey("root"); + + if (value.isObject()) { + // Get all QJsonValue childs + const QStringList keys = + value.toObject().keys(); // To prevent clazy-range warning + for (const QString& key : keys) { + if (contains(exceptions, key)) { + continue; + } + QJsonValue jsonValue = value.toObject().value(key); + QJsonTreeItem* child = load(jsonValue, exceptions, rootItem); + child->setKey(key); + child->setType(jsonValue.type()); + rootItem->appendChild(child); + } + } else if (value.isArray()) { + // Get all QJsonValue childs + int index = 0; + const QJsonArray array = + value.toArray(); // To prevent clazy-range warning + for (const QJsonValue& jsonValue : array) { + QJsonTreeItem* child = + load(jsonValue, exceptions, rootItem); + child->setKey(QString::number(index)); + child->setType(jsonValue.type()); + rootItem->appendChild(child); + ++index; + } + } else { + rootItem->setValue(value.toVariant()); + rootItem->setType(value.type()); + } + + return rootItem; } //========================================================================= -inline uchar hexdig(uint u) { return (u < 0xa ? '0' + u : 'a' + u - 0xa); } - -QByteArray escapedString(const QString &s) { - QByteArray ba(s.length(), Qt::Uninitialized); - uchar *cursor = reinterpret_cast(const_cast(ba.constData())); - const uchar *ba_end = cursor + ba.length(); - const ushort *src = reinterpret_cast(s.constBegin()); - const ushort *const end = reinterpret_cast(s.constEnd()); - while (src != end) { - if (cursor >= ba_end - 6) { - // ensure we have enough space - int pos = cursor - reinterpret_cast(ba.constData()); - ba.resize(ba.size() * 2); - cursor = reinterpret_cast(ba.data()) + pos; - ba_end = reinterpret_cast(ba.constData()) + ba.length(); - } - uint u = *src++; - if (u < 0x80) { - if (u < 0x20 || u == 0x22 || u == 0x5c) { - *cursor++ = '\\'; - switch (u) { - case 0x22: - *cursor++ = '"'; - break; - case 0x5c: - *cursor++ = '\\'; - break; - case 0x8: - *cursor++ = 'b'; - break; - case 0xc: - *cursor++ = 'f'; - break; - case 0xa: - *cursor++ = 'n'; - break; - case 0xd: - *cursor++ = 'r'; - break; - case 0x9: - *cursor++ = 't'; - break; - default: - *cursor++ = 'u'; - *cursor++ = '0'; - *cursor++ = '0'; - *cursor++ = hexdig(u >> 4); - *cursor++ = hexdig(u & 0xf); - } - } else { - *cursor++ = (uchar)u; - } - } else if (QUtf8Functions::toUtf8(u, cursor, src, end) < - 0) { - // failed to get valid utf8 use JSON escape sequence - *cursor++ = '\\'; - *cursor++ = 'u'; - *cursor++ = hexdig(u >> 12 & 0x0f); - *cursor++ = hexdig(u >> 8 & 0x0f); - *cursor++ = hexdig(u >> 4 & 0x0f); - *cursor++ = hexdig(u & 0x0f); - } - } - ba.resize(cursor - reinterpret_cast(ba.constData())); - return ba; +inline uchar hexdig(uint positiveValue) +{ + return ( + positiveValue < 0xa ? '0' + positiveValue : 'a' + positiveValue - 0xa + ); } -QJsonModel::QJsonModel(QObject *parent) - : QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} { - mHeaders.append("key"); - mHeaders.append("value"); +QByteArray escapedString(const QString& text) +{ + QByteArray byteArray(text.length(), Qt::Uninitialized); + uchar* cursor = + reinterpret_cast(const_cast(byteArray.constData())); + const uchar* byteArrayEnd = cursor + byteArray.length(); + const ushort* src = reinterpret_cast(text.constBegin()); + const ushort* const end = + reinterpret_cast(text.constEnd()); + while (src != end) { + if (cursor >= byteArrayEnd - 6) { + // ensure we have enough space + int pos = cursor - reinterpret_cast( + byteArray.constData() + ); + byteArray.resize(byteArray.size() * 2); + cursor = + reinterpret_cast(byteArray.data()) + pos; + byteArrayEnd = + reinterpret_cast(byteArray.constData() + ) + + byteArray.length(); + } + uint uValue = *src++; // uValue = unsigned value + if (uValue < 0x80) { + if (uValue < 0x20 || uValue == 0x22 || uValue == 0x5c) { + *cursor++ = '\\'; + switch (uValue) { + case 0x22: + *cursor++ = '"'; + break; + case 0x5c: + *cursor++ = '\\'; + break; + case 0x8: + *cursor++ = 'b'; + break; + case 0xc: + *cursor++ = 'f'; + break; + case 0xa: + *cursor++ = 'n'; + break; + case 0xd: + *cursor++ = 'r'; + break; + case 0x9: + *cursor++ = 't'; + break; + default: + *cursor++ = 'u'; + *cursor++ = '0'; + *cursor++ = '0'; + *cursor++ = hexdig(uValue >> 4); + *cursor++ = hexdig(uValue & 0xf); + } + } else { + *cursor++ = static_cast(uValue); + } + } else if (QUtf8Functions::toUtf8(uValue, cursor, src, end) < 0) { + // failed to get valid utf8 use JSON escape sequence + *cursor++ = '\\'; + *cursor++ = 'u'; + *cursor++ = hexdig(uValue >> 12 & 0x0f); + *cursor++ = hexdig(uValue >> 8 & 0x0f); + *cursor++ = hexdig(uValue >> 4 & 0x0f); + *cursor++ = hexdig(uValue & 0x0f); + } + } + byteArray.resize( + cursor - reinterpret_cast(byteArray.constData()) + ); + return byteArray; } -QJsonModel::QJsonModel(const QString &fileName, QObject *parent) - : QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} { - mHeaders.append("key"); - mHeaders.append("value"); - load(fileName); +QJsonModel::QJsonModel(QObject* parent) + : QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem } +{ + mHeaders.append("key"); + mHeaders.append("value"); } -QJsonModel::QJsonModel(QIODevice *device, QObject *parent) - : QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} { - mHeaders.append("key"); - mHeaders.append("value"); - load(device); +QJsonModel::QJsonModel(const QString& fileName, QObject* parent) + : QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem } +{ + mHeaders.append("key"); + mHeaders.append("value"); + load(fileName); } -QJsonModel::QJsonModel(const QByteArray &json, QObject *parent) - : QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} { - mHeaders.append("key"); - mHeaders.append("value"); - loadJson(json); +QJsonModel::QJsonModel(QIODevice* device, QObject* parent) + : QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem } +{ + mHeaders.append("key"); + mHeaders.append("value"); + load(device); } -QJsonModel::~QJsonModel() { delete mRootItem; } - -bool QJsonModel::load(const QString &fileName) { - QFile file(fileName); - bool success = false; - if (file.open(QIODevice::ReadOnly)) { - success = load(&file); - file.close(); - } else { - success = false; - } - - return success; +QJsonModel::QJsonModel(const QByteArray& json, QObject* parent) + : QAbstractItemModel(parent), mRootItem{ new QJsonTreeItem } +{ + mHeaders.append("key"); + mHeaders.append("value"); + loadJson(json); } -bool QJsonModel::load(QIODevice *device) { return loadJson(device->readAll()); } - -bool QJsonModel::loadJson(const QByteArray &json) { - auto const &jdoc = QJsonDocument::fromJson(json); - - if (!jdoc.isNull()) { - beginResetModel(); - delete mRootItem; - if (jdoc.isArray()) { - mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.array()), mExceptions); - mRootItem->setType(QJsonValue::Array); - - } else { - mRootItem = QJsonTreeItem::load(QJsonValue(jdoc.object()), mExceptions); - mRootItem->setType(QJsonValue::Object); - } - endResetModel(); - return true; - } - - qDebug() << Q_FUNC_INFO << "cannot load json"; - return false; +QJsonModel::~QJsonModel() +{ + delete mRootItem; } -QVariant QJsonModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) - return {}; +bool QJsonModel::load(const QString& fileName) +{ + QFile file(fileName); + bool success = false; + if (file.open(QIODevice::ReadOnly)) { + success = load(&file); + file.close(); + } else { + success = false; + } - QJsonTreeItem *item = static_cast(index.internalPointer()); - - if (role == Qt::DisplayRole) { - if (index.column() == 0) - return QString("%1").arg(item->key()); - - if (index.column() == 1) - return item->value(); - } else if (Qt::EditRole == role) { - if (index.column() == 1) - return item->value(); - } - - return {}; + return success; } -bool QJsonModel::setData(const QModelIndex &index, const QVariant &value, - int role) { - int col = index.column(); - if (Qt::EditRole == role) { - if (col == 1) { - QJsonTreeItem *item = - static_cast(index.internalPointer()); - item->setValue(value); - emit dataChanged(index, index, {Qt::EditRole}); - return true; - } - } - - return false; +bool QJsonModel::load(QIODevice* device) +{ + return loadJson(device->readAll()); } -QVariant QJsonModel::headerData(int section, Qt::Orientation orientation, - int role) const { - if (role != Qt::DisplayRole) - return {}; +bool QJsonModel::loadJson(const QByteArray& json) +{ + auto const& jdoc = QJsonDocument::fromJson(json); - if (orientation == Qt::Horizontal) - return mHeaders.value(section); - else - return {}; + if (!jdoc.isNull()) { + beginResetModel(); + delete mRootItem; + if (jdoc.isArray()) { + mRootItem = QJsonTreeItem::load( + QJsonValue(jdoc.array()), mExceptions + ); + mRootItem->setType(QJsonValue::Array); + + } else { + mRootItem = QJsonTreeItem::load( + QJsonValue(jdoc.object()), mExceptions + ); + mRootItem->setType(QJsonValue::Object); + } + endResetModel(); + return true; + } + + qDebug() << Q_FUNC_INFO << "cannot load json"; + return false; } -QModelIndex QJsonModel::index(int row, int column, - const QModelIndex &parent) const { - if (!hasIndex(row, column, parent)) - return {}; +QVariant QJsonModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return {}; - QJsonTreeItem *parentItem; + QJsonTreeItem* item = + static_cast(index.internalPointer()); - if (!parent.isValid()) - parentItem = mRootItem; - else - parentItem = static_cast(parent.internalPointer()); + if (role == Qt::DisplayRole) { + if (index.column() == 0) + return QString("%1").arg(item->key()); - QJsonTreeItem *childItem = parentItem->child(row); - if (childItem) - return createIndex(row, column, childItem); - else - return {}; + if (index.column() == 1) + return item->value(); + } else if (Qt::EditRole == role) { + if (index.column() == 1) + return item->value(); + } + + return {}; } -QModelIndex QJsonModel::parent(const QModelIndex &index) const { - if (!index.isValid()) - return {}; +bool QJsonModel::setData( + const QModelIndex& index, const QVariant& value, int role +) +{ + int col = index.column(); + if (Qt::EditRole == role) { + if (col == 1) { + QJsonTreeItem* item = + static_cast(index.internalPointer()); + item->setValue(value); + emit dataChanged(index, index, { Qt::EditRole }); + return true; + } + } - QJsonTreeItem *childItem = - static_cast(index.internalPointer()); - QJsonTreeItem *parentItem = childItem->parent(); - - if (parentItem == mRootItem) - return QModelIndex(); - - return createIndex(parentItem->row(), 0, parentItem); + return false; } -int QJsonModel::rowCount(const QModelIndex &parent) const { - QJsonTreeItem *parentItem; - if (parent.column() > 0) - return 0; +QVariant +QJsonModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return {}; - if (!parent.isValid()) - parentItem = mRootItem; - else - parentItem = static_cast(parent.internalPointer()); - - return parentItem->childCount(); + if (orientation == Qt::Horizontal) + return mHeaders.value(section); + else + return {}; } -int QJsonModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent) - return 2; +QModelIndex +QJsonModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return {}; + + QJsonTreeItem* parentItem; + + if (!parent.isValid()) + parentItem = mRootItem; + else + parentItem = + static_cast(parent.internalPointer()); + + QJsonTreeItem* childItem = parentItem->child(row); + if (childItem) + return createIndex(row, column, childItem); + else + return {}; } -Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const { - int col = index.column(); - auto item = static_cast(index.internalPointer()); +QModelIndex QJsonModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return {}; - auto isArray = QJsonValue::Array == item->type(); - auto isObject = QJsonValue::Object == item->type(); + QJsonTreeItem* childItem = + static_cast(index.internalPointer()); + QJsonTreeItem* parentItem = childItem->parent(); - if ((col == 1) && !(isArray || isObject)) - return Qt::ItemIsEditable | QAbstractItemModel::flags(index); - else - return QAbstractItemModel::flags(index); + if (parentItem == mRootItem) + return QModelIndex(); + + return createIndex(parentItem->row(), 0, parentItem); } -QByteArray QJsonModel::json(bool compact) { - auto jsonValue = genJson(mRootItem); - QByteArray json; - if (jsonValue.isNull()) - return json; +int QJsonModel::rowCount(const QModelIndex& parent) const +{ + QJsonTreeItem* parentItem; + if (parent.column() > 0) + return 0; - if (jsonValue.isArray()) - arrayToJson(jsonValue.toArray(), json, 0, compact); - else - objectToJson(jsonValue.toObject(), json, 0, compact); + if (!parent.isValid()) + parentItem = mRootItem; + else + parentItem = + static_cast(parent.internalPointer()); - return json; + return parentItem->childCount(); } -void QJsonModel::objectToJson(QJsonObject jsonObject, QByteArray &json, - int indent, bool compact) { - json += compact ? "{" : "{\n"; - objectContentToJson(jsonObject, json, indent + (compact ? 0 : 1), compact); - json += QByteArray(4 * indent, ' '); - json += compact ? "}" : "}\n"; -} -void QJsonModel::arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent, - bool compact) { - json += compact ? "[" : "[\n"; - arrayContentToJson(jsonArray, json, indent + (compact ? 0 : 1), compact); - json += QByteArray(4 * indent, ' '); - json += compact ? "]" : "]\n"; +int QJsonModel::columnCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent) + return 2; } -void QJsonModel::arrayContentToJson(QJsonArray jsonArray, QByteArray &json, - int indent, bool compact) { - if (jsonArray.size() <= 0) - return; +Qt::ItemFlags QJsonModel::flags(const QModelIndex& index) const +{ + int col = index.column(); + auto item = static_cast(index.internalPointer()); - QByteArray indentString(4 * indent, ' '); - int i = 0; - while (1) { - json += indentString; - valueToJson(jsonArray.at(i), json, indent, compact); - if (++i == jsonArray.size()) { - if (!compact) - json += '\n'; - break; - } - json += compact ? "," : ",\n"; - } -} -void QJsonModel::objectContentToJson(QJsonObject jsonObject, QByteArray &json, - int indent, bool compact) { - if (jsonObject.size() <= 0) - return; + auto isArray = QJsonValue::Array == item->type(); + auto isObject = QJsonValue::Object == item->type(); - QByteArray indentString(4 * indent, ' '); - int i = 0; - while (1) { - QString key = jsonObject.keys().at(i); - json += indentString; - json += '"'; - json += escapedString(key); - json += compact ? "\":" : "\": "; - valueToJson(jsonObject.value(key), json, indent, compact); - if (++i == jsonObject.size()) { - if (!compact) - json += '\n'; - break; - } - json += compact ? "," : ",\n"; - } + if ((col == 1) && !(isArray || isObject)) + return Qt::ItemIsEditable | QAbstractItemModel::flags(index); + else + return QAbstractItemModel::flags(index); } -void QJsonModel::valueToJson(QJsonValue jsonValue, QByteArray &json, int indent, - bool compact) { - QJsonValue::Type type = jsonValue.type(); - switch (type) { - case QJsonValue::Bool: - json += jsonValue.toBool() ? "true" : "false"; - break; - case QJsonValue::Double: { - const double d = jsonValue.toDouble(); - if (qIsFinite(d)) { - json += QByteArray::number(d, 'f', QLocale::FloatingPointShortest); - } else { - json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4) - } - break; - } - case QJsonValue::String: - json += '"'; - json += escapedString(jsonValue.toString()); - json += '"'; - break; - case QJsonValue::Array: - json += compact ? "[" : "[\n"; - arrayContentToJson(jsonValue.toArray(), json, indent + (compact ? 0 : 1), - compact); - json += QByteArray(4 * indent, ' '); - json += ']'; - break; - case QJsonValue::Object: - json += compact ? "{" : "{\n"; - objectContentToJson(jsonValue.toObject(), json, indent + (compact ? 0 : 1), - compact); - json += QByteArray(4 * indent, ' '); - json += '}'; - break; - case QJsonValue::Null: - default: - json += "null"; - } +QByteArray QJsonModel::json(bool compact) +{ + auto jsonValue = genJson(mRootItem); + QByteArray json; + if (jsonValue.isNull()) + return json; + + if (jsonValue.isArray()) + arrayToJson(jsonValue.toArray(), json, 0, compact); + else + objectToJson(jsonValue.toObject(), json, 0, compact); + + return json; } -void QJsonModel::addException(const QStringList &exceptions) { - mExceptions = exceptions; +void QJsonModel::objectToJson( + QJsonObject jsonObject, QByteArray& json, int indent, bool compact +) +{ + json += compact ? "{" : "{\n"; + objectContentToJson( + jsonObject, json, indent + (compact ? 0 : 1), compact + ); + json += QByteArray(4 * indent, ' '); + json += compact ? "}" : "}\n"; +} +void QJsonModel::arrayToJson( + QJsonArray jsonArray, QByteArray& json, int indent, bool compact +) +{ + json += compact ? "[" : "[\n"; + arrayContentToJson(jsonArray, json, indent + (compact ? 0 : 1), compact); + json += QByteArray(4 * indent, ' '); + json += compact ? "]" : "]\n"; } -QJsonValue QJsonModel::genJson(QJsonTreeItem *item) const { - auto type = item->type(); - int nchild = item->childCount(); +void QJsonModel::arrayContentToJson( + QJsonArray jsonArray, QByteArray& json, int indent, bool compact +) +{ + if (jsonArray.size() <= 0) + return; - if (QJsonValue::Object == type) { - QJsonObject jo; - for (int i = 0; i < nchild; ++i) { - auto ch = item->child(i); - auto key = ch->key(); - jo.insert(key, genJson(ch)); - } - return jo; - } else if (QJsonValue::Array == type) { - QJsonArray arr; - for (int i = 0; i < nchild; ++i) { - auto ch = item->child(i); - arr.append(genJson(ch)); - } - return arr; - } else { - QJsonValue va; - switch (item->value().typeId()) { - case QMetaType::Bool: { - va = item->value().toBool(); - break; - } - default: - va = item->value().toString(); - break; - } - (item->value()); - return va; - } + QByteArray indentString(4 * indent, ' '); + int i = 0; + while (1) { + json += indentString; + valueToJson(jsonArray.at(i), json, indent, compact); + if (++i == jsonArray.size()) { + if (!compact) + json += '\n'; + break; + } + json += compact ? "," : ",\n"; + } } +void QJsonModel::objectContentToJson( + QJsonObject jsonObject, QByteArray& json, int indent, bool compact +) +{ + if (jsonObject.size() <= 0) + return; + + QByteArray indentString(4 * indent, ' '); + int i = 0; + while (1) { + QString key = jsonObject.keys().at(i); + json += indentString; + json += '"'; + json += escapedString(key); + json += compact ? "\":" : "\": "; + valueToJson(jsonObject.value(key), json, indent, compact); + if (++i == jsonObject.size()) { + if (!compact) + json += '\n'; + break; + } + json += compact ? "," : ",\n"; + } +} + +void QJsonModel::valueToJson( + QJsonValue jsonValue, QByteArray& json, int indent, bool compact +) +{ + QJsonValue::Type type = jsonValue.type(); + switch (type) { + case QJsonValue::Bool: + json += jsonValue.toBool() ? "true" : "false"; + break; + case QJsonValue::Double: { + const double value = jsonValue.toDouble(); + if (qIsFinite(value)) { + json += QByteArray::number( + value, 'f', QLocale::FloatingPointShortest + ); + } else { + json += "null"; // +INF || -INF || NaN (see + // RFC4627#section2.4) + } + break; + } + case QJsonValue::String: + json += '"'; + json += escapedString(jsonValue.toString()); + json += '"'; + break; + case QJsonValue::Array: + json += compact ? "[" : "[\n"; + arrayContentToJson( + jsonValue.toArray(), + json, + indent + (compact ? 0 : 1), + compact + ); + json += QByteArray(4 * indent, ' '); + json += ']'; + break; + case QJsonValue::Object: + json += compact ? "{" : "{\n"; + objectContentToJson( + jsonValue.toObject(), + json, + indent + (compact ? 0 : 1), + compact + ); + json += QByteArray(4 * indent, ' '); + json += '}'; + break; + case QJsonValue::Null: + default: + json += "null"; + } +} + +void QJsonModel::addException(const QStringList& exceptions) +{ + mExceptions = exceptions; +} + +QJsonValue QJsonModel::genJson(QJsonTreeItem* item) const +{ + auto type = item->type(); + int nchild = item->childCount(); + + if (QJsonValue::Object == type) { + QJsonObject jo; + for (int i = 0; i < nchild; ++i) { + auto ch = item->child(i); + auto key = ch->key(); + jo.insert(key, genJson(ch)); + } + return jo; + } else if (QJsonValue::Array == type) { + QJsonArray arr; + for (int i = 0; i < nchild; ++i) { + auto ch = item->child(i); + arr.append(genJson(ch)); + } + return arr; + } else { + QJsonValue va; + switch (item->value().typeId()) { + case QMetaType::Bool: { + va = item->value().toBool(); + break; + } + default: + va = item->value().toString(); + break; + } + (item->value()); + return va; + } +} +// clang-format off +// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=4 sts=0 sw=8 noexpandtab ft=cpp.doxygen : diff --git a/include/QJsonModel.hpp b/include/QJsonModel.hpp index 23af7f5..39de788 100644 --- a/include/QJsonModel.hpp +++ b/include/QJsonModel.hpp @@ -38,78 +38,92 @@ class QJsonModel; class QJsonItem; class QJsonTreeItem { -public: - QJsonTreeItem(QJsonTreeItem *parent = nullptr); - ~QJsonTreeItem(); - void appendChild(QJsonTreeItem *item); - QJsonTreeItem *child(int row); - QJsonTreeItem *parent(); - int childCount() const; - int row() const; - void setKey(const QString &key); - void setValue(const QVariant &value); - void setType(const QJsonValue::Type &type); - QString key() const; - QVariant value() const; - QJsonValue::Type type() const; + public: + QJsonTreeItem(QJsonTreeItem* parent = nullptr); + ~QJsonTreeItem(); + void appendChild(QJsonTreeItem* item); + QJsonTreeItem* child(int row); + QJsonTreeItem* parent(); + int childCount() const; + int row() const; + void setKey(const QString& key); + void setValue(const QVariant& value); + void setType(const QJsonValue::Type& type); + QString key() const; + QVariant value() const; + QJsonValue::Type type() const; - static QJsonTreeItem *load(const QJsonValue &value, - const QStringList &exceptions = {}, - QJsonTreeItem *parent = nullptr); + static QJsonTreeItem* load( + const QJsonValue& value, const QStringList& exceptions = {}, + QJsonTreeItem* parent = nullptr + ); -protected: -private: - QString mKey; - QVariant mValue; - QJsonValue::Type mType; - QList mChilds; - QJsonTreeItem *mParent = nullptr; + protected: + private: + QString mKey; + QVariant mValue; + QJsonValue::Type mType; + QList mChilds; + QJsonTreeItem* mParent = nullptr; }; //--------------------------------------------------- class QJsonModel : public QAbstractItemModel { - Q_OBJECT -public: - explicit QJsonModel(QObject *parent = nullptr); - QJsonModel(const QString &fileName, QObject *parent = nullptr); - QJsonModel(QIODevice *device, QObject *parent = nullptr); - QJsonModel(const QByteArray &json, QObject *parent = nullptr); - ~QJsonModel(); - bool load(const QString &fileName); - bool load(QIODevice *device); - bool loadJson(const QByteArray &json); - QVariant data(const QModelIndex &index, int role) const override; - bool setData(const QModelIndex &index, const QVariant &value, - int role = Qt::EditRole) override; - QVariant headerData(int section, Qt::Orientation orientation, - int role) const override; - QModelIndex index(int row, int column, - const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &index) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - Qt::ItemFlags flags(const QModelIndex &index) const override; - QByteArray json(bool compact = false); - QByteArray jsonToByte(QJsonValue jsonValue); - void objectToJson(QJsonObject jsonObject, QByteArray &json, int indent, - bool compact); - void arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent, - bool compact); - void arrayContentToJson(QJsonArray jsonArray, QByteArray &json, int indent, - bool compact); - void objectContentToJson(QJsonObject jsonObject, QByteArray &json, int indent, - bool compact); - void valueToJson(QJsonValue jsonValue, QByteArray &json, int indent, - bool compact); - //! List of tags to skip during JSON parsing - void addException(const QStringList &exceptions); + // NOLINTNEXTLINE + Q_OBJECT + public: + explicit QJsonModel(QObject* parent = nullptr); + QJsonModel(const QString& fileName, QObject* parent = nullptr); + QJsonModel(QIODevice* device, QObject* parent = nullptr); + QJsonModel(const QByteArray& json, QObject* parent = nullptr); + ~QJsonModel() override; + bool load(const QString& fileName); + bool load(QIODevice* device); + bool loadJson(const QByteArray& json); + QVariant data(const QModelIndex& index, int role) const override; + bool setData( + const QModelIndex& index, const QVariant& value, + int role = Qt::EditRole + ) override; + QVariant headerData(int section, Qt::Orientation orientation, int role) + const override; + QModelIndex index( + int row, int column, const QModelIndex& parent = QModelIndex() + ) const override; + QModelIndex parent(const QModelIndex& index) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int + columnCount(const QModelIndex& parent = QModelIndex()) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + QByteArray json(bool compact = false); + QByteArray jsonToByte(QJsonValue jsonValue); + void objectToJson( + QJsonObject jsonObject, QByteArray& json, int indent, bool compact + ); + void arrayToJson( + QJsonArray jsonArray, QByteArray& json, int indent, bool compact + ); + void arrayContentToJson( + QJsonArray jsonArray, QByteArray& json, int indent, bool compact + ); + void objectContentToJson( + QJsonObject jsonObject, QByteArray& json, int indent, bool compact + ); + void valueToJson( + QJsonValue jsonValue, QByteArray& json, int indent, bool compact + ); + //! List of tags to skip during JSON parsing + void addException(const QStringList& exceptions); -private: - QJsonValue genJson(QJsonTreeItem *) const; - QJsonTreeItem *mRootItem = nullptr; - QStringList mHeaders; - //! List of exceptions (e.g. comments). Case insensitive, compairs on - //! "contains". - QStringList mExceptions; + private: + QJsonValue genJson(QJsonTreeItem*) const; + QJsonTreeItem* mRootItem = nullptr; + QStringList mHeaders; + //! List of exceptions (e.g. comments). Case insensitive, compairs on + //! "contains". + QStringList mExceptions; }; + +// clang-format off +// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen : diff --git a/include/details/QUtf8.hpp b/include/details/QUtf8.hpp index b934351..3403d94 100644 --- a/include/details/QUtf8.hpp +++ b/include/details/QUtf8.hpp @@ -29,166 +29,198 @@ namespace QUtf8Functions { /// Error if \a u is a low surrogate; /// if \a u is a high surrogate, Error if the next isn't a low one, /// EndOfString if we run into the end of the string. -template -inline int toUtf8(ushort u, OutputPtr &dst, InputPtr &src, InputPtr end) { - if (!Traits::skipAsciiHandling && u < 0x80) { - // U+0000 to U+007F (US-ASCII) - one byte - Traits::appendByte(dst, uchar(u)); - return 0; - } else if (u < 0x0800) { - // U+0080 to U+07FF - two bytes - // first of two bytes - Traits::appendByte(dst, 0xc0 | uchar(u >> 6)); - } else { - if (!QChar::isSurrogate(u)) { - // U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes - if (!Traits::allowNonCharacters && QChar::isNonCharacter(u)) - return Traits::Error; - // first of three bytes - Traits::appendByte(dst, 0xe0 | uchar(u >> 12)); - } else { - // U+10000 to U+10FFFF - four bytes - // need to get one extra codepoint - if (Traits::availableUtf16(src, end) == 0) - return Traits::EndOfString; - ushort low = Traits::peekUtf16(src); - if (!QChar::isHighSurrogate(u)) - return Traits::Error; - if (!QChar::isLowSurrogate(low)) - return Traits::Error; - Traits::advanceUtf16(src); - uint ucs4 = QChar::surrogateToUcs4(u, low); - if (!Traits::allowNonCharacters && QChar::isNonCharacter(ucs4)) - return Traits::Error; - // first byte - Traits::appendByte(dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf)); - // second of four bytes - Traits::appendByte(dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f)); - // for the rest of the bytes - u = ushort(ucs4); - } - // second to last byte - Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f)); - } - // last byte - Traits::appendByte(dst, 0x80 | (u & 0x3f)); - return 0; +template +inline int toUtf8(ushort u, OutputPtr& dst, InputPtr& src, InputPtr end) +{ + if (!Traits::skipAsciiHandling && u < 0x80) { + // U+0000 to U+007F (US-ASCII) - one byte + Traits::appendByte(dst, uchar(u)); + return 0; + } else if (u < 0x0800) { + // U+0080 to U+07FF - two bytes + // first of two bytes + Traits::appendByte(dst, 0xc0 | uchar(u >> 6)); + } else { + if (!QChar::isSurrogate(u)) { + // U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes + if (!Traits::allowNonCharacters && + QChar::isNonCharacter(u)) + return Traits::Error; + // first of three bytes + Traits::appendByte(dst, 0xe0 | uchar(u >> 12)); + } else { + // U+10000 to U+10FFFF - four bytes + // need to get one extra codepoint + if (Traits::availableUtf16(src, end) == 0) + return Traits::EndOfString; + ushort low = Traits::peekUtf16(src); + if (!QChar::isHighSurrogate(u)) + return Traits::Error; + if (!QChar::isLowSurrogate(low)) + return Traits::Error; + Traits::advanceUtf16(src); + uint ucs4 = QChar::surrogateToUcs4(u, low); + if (!Traits::allowNonCharacters && + QChar::isNonCharacter(ucs4)) + return Traits::Error; + // first byte + Traits::appendByte( + dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf) + ); + // second of four bytes + Traits::appendByte( + dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f) + ); + // for the rest of the bytes + u = ushort(ucs4); + } + // second to last byte + Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f)); + } + // last byte + Traits::appendByte(dst, 0x80 | (u & 0x3f)); + return 0; +} +inline bool isContinuationByte(uchar b) +{ + return (b & 0xc0) == 0x80; } -inline bool isContinuationByte(uchar b) { return (b & 0xc0) == 0x80; } /// returns the number of characters consumed (including \a b) in case of /// success; returns negative in case of error: Traits::Error or /// Traits::EndOfString -template -inline int fromUtf8(uchar b, OutputPtr &dst, InputPtr &src, InputPtr end) { - int charsNeeded; - uint min_uc; - uint uc; - if (!Traits::skipAsciiHandling && b < 0x80) { - // US-ASCII - Traits::appendUtf16(dst, b); - return 1; - } - if (!Traits::isTrusted && Q_UNLIKELY(b <= 0xC1)) { - // an UTF-8 first character must be at least 0xC0 - // however, all 0xC0 and 0xC1 first bytes can only produce overlong - // sequences - return Traits::Error; - } else if (b < 0xe0) { - charsNeeded = 2; - min_uc = 0x80; - uc = b & 0x1f; - } else if (b < 0xf0) { - charsNeeded = 3; - min_uc = 0x800; - uc = b & 0x0f; - } else if (b < 0xf5) { - charsNeeded = 4; - min_uc = 0x10000; - uc = b & 0x07; - } else { - // the last Unicode character is U+10FFFF - // it's encoded in UTF-8 as "\xF4\x8F\xBF\xBF" - // therefore, a byte higher than 0xF4 is not the UTF-8 first byte - return Traits::Error; - } - int bytesAvailable = Traits::availableBytes(src, end); - if (Q_UNLIKELY(bytesAvailable < charsNeeded - 1)) { - // it's possible that we have an error instead of just unfinished bytes - if (bytesAvailable > 0 && !isContinuationByte(Traits::peekByte(src, 0))) - return Traits::Error; - if (bytesAvailable > 1 && !isContinuationByte(Traits::peekByte(src, 1))) - return Traits::Error; - return Traits::EndOfString; - } - // first continuation character - b = Traits::peekByte(src, 0); - if (!isContinuationByte(b)) - return Traits::Error; - uc <<= 6; - uc |= b & 0x3f; - if (charsNeeded > 2) { - // second continuation character - b = Traits::peekByte(src, 1); - if (!isContinuationByte(b)) - return Traits::Error; - uc <<= 6; - uc |= b & 0x3f; - if (charsNeeded > 3) { - // third continuation character - b = Traits::peekByte(src, 2); - if (!isContinuationByte(b)) - return Traits::Error; - uc <<= 6; - uc |= b & 0x3f; - } - } - // we've decoded something; safety-check it - if (!Traits::isTrusted) { - if (uc < min_uc) - return Traits::Error; - if (QChar::isSurrogate(uc) || uc > QChar::LastValidCodePoint) - return Traits::Error; - if (!Traits::allowNonCharacters && QChar::isNonCharacter(uc)) - return Traits::Error; - } - // write the UTF-16 sequence - if (!QChar::requiresSurrogates(uc)) { - // UTF-8 decoded and no surrogates are required - // detach if necessary - Traits::appendUtf16(dst, ushort(uc)); - } else { - // UTF-8 decoded to something that requires a surrogate pair - Traits::appendUcs4(dst, uc); - } - Traits::advanceByte(src, charsNeeded - 1); - return charsNeeded; +template +inline int fromUtf8(uchar b, OutputPtr& dst, InputPtr& src, InputPtr end) +{ + int charsNeeded; + uint min_uc; + uint uc; + if (!Traits::skipAsciiHandling && b < 0x80) { + // US-ASCII + Traits::appendUtf16(dst, b); + return 1; + } + if (!Traits::isTrusted && Q_UNLIKELY(b <= 0xC1)) { + // an UTF-8 first character must be at least 0xC0 + // however, all 0xC0 and 0xC1 first bytes can only produce + // overlong sequences + return Traits::Error; + } else if (b < 0xe0) { + charsNeeded = 2; + min_uc = 0x80; + uc = b & 0x1f; + } else if (b < 0xf0) { + charsNeeded = 3; + min_uc = 0x800; + uc = b & 0x0f; + } else if (b < 0xf5) { + charsNeeded = 4; + min_uc = 0x10000; + uc = b & 0x07; + } else { + // the last Unicode character is U+10FFFF + // it's encoded in UTF-8 as "\xF4\x8F\xBF\xBF" + // therefore, a byte higher than 0xF4 is not the UTF-8 first byte + return Traits::Error; + } + int bytesAvailable = Traits::availableBytes(src, end); + if (Q_UNLIKELY(bytesAvailable < charsNeeded - 1)) { + // it's possible that we have an error instead of just unfinished + // bytes + if (bytesAvailable > 0 && + !isContinuationByte(Traits::peekByte(src, 0))) + return Traits::Error; + if (bytesAvailable > 1 && + !isContinuationByte(Traits::peekByte(src, 1))) + return Traits::Error; + return Traits::EndOfString; + } + // first continuation character + b = Traits::peekByte(src, 0); + if (!isContinuationByte(b)) + return Traits::Error; + uc <<= 6; + uc |= b & 0x3f; + if (charsNeeded > 2) { + // second continuation character + b = Traits::peekByte(src, 1); + if (!isContinuationByte(b)) + return Traits::Error; + uc <<= 6; + uc |= b & 0x3f; + if (charsNeeded > 3) { + // third continuation character + b = Traits::peekByte(src, 2); + if (!isContinuationByte(b)) + return Traits::Error; + uc <<= 6; + uc |= b & 0x3f; + } + } + // we've decoded something; safety-check it + if (!Traits::isTrusted) { + if (uc < min_uc) + return Traits::Error; + if (QChar::isSurrogate(uc) || uc > QChar::LastValidCodePoint) + return Traits::Error; + if (!Traits::allowNonCharacters && QChar::isNonCharacter(uc)) + return Traits::Error; + } + // write the UTF-16 sequence + if (!QChar::requiresSurrogates(uc)) { + // UTF-8 decoded and no surrogates are required + // detach if necessary + Traits::appendUtf16(dst, ushort(uc)); + } else { + // UTF-8 decoded to something that requires a surrogate pair + Traits::appendUcs4(dst, uc); + } + Traits::advanceByte(src, charsNeeded - 1); + return charsNeeded; } } // namespace QUtf8Functions struct QUtf8BaseTraits { - static const bool isTrusted = false; - static const bool allowNonCharacters = true; - static const bool skipAsciiHandling = false; - static const int Error = -1; - static const int EndOfString = -2; - static bool isValidCharacter(uint u) { return int(u) >= 0; } - static void appendByte(uchar *&ptr, uchar b) { *ptr++ = b; } - static uchar peekByte(const uchar *ptr, int n = 0) { return ptr[n]; } - static qptrdiff availableBytes(const uchar *ptr, const uchar *end) { - return end - ptr; - } - static void advanceByte(const uchar *&ptr, int n = 1) { ptr += n; } - static void appendUtf16(ushort *&ptr, ushort uc) { *ptr++ = uc; } - static void appendUcs4(ushort *&ptr, uint uc) { - appendUtf16(ptr, QChar::highSurrogate(uc)); - appendUtf16(ptr, QChar::lowSurrogate(uc)); - } - static ushort peekUtf16(const ushort *ptr, int n = 0) { return ptr[n]; } - static qptrdiff availableUtf16(const ushort *ptr, const ushort *end) { - return end - ptr; - } - static void advanceUtf16(const ushort *&ptr, int n = 1) { ptr += n; } - // it's possible to output to UCS-4 too - static void appendUtf16(uint *&ptr, ushort uc) { *ptr++ = uc; } - static void appendUcs4(uint *&ptr, uint uc) { *ptr++ = uc; } + static const bool isTrusted = false; + static const bool allowNonCharacters = true; + static const bool skipAsciiHandling = false; + static const int error = -1; + static const int endOfString = -2; + static bool isValidCharacter(uint unsignedInt) + { + return static_cast(unsignedInt) >= 0; + } + static void appendByte(uchar*& ptr, uchar byteVal) { *ptr++ = byteVal; } + static uchar peekByte(const uchar* ptr, int n = 0) { return ptr[n]; } + static qptrdiff availableBytes(const uchar* ptr, const uchar* end) + { + return end - ptr; + } + static void advanceByte(const uchar*& ptr, int n = 1) { ptr += n; } + static void appendUtf16(ushort*& ptr, ushort unicodeChar) + { + *ptr++ = unicodeChar; + } + static void appendUcs4(ushort*& ptr, uint unicodeChar) + { + appendUtf16(ptr, QChar::highSurrogate(unicodeChar)); + appendUtf16(ptr, QChar::lowSurrogate(unicodeChar)); + } + static ushort peekUtf16(const ushort* ptr, int n = 0) { return ptr[n]; } + static qptrdiff availableUtf16(const ushort* ptr, const ushort* end) + { + return end - ptr; + } + static void advanceUtf16(const ushort*& ptr, int n = 1) { ptr += n; } + // it's possible to output to UCS-4 too + static void appendUtf16(uint*& ptr, ushort unicodeChar) + { + *ptr++ = unicodeChar; + } + static void appendUcs4(uint*& ptr, uint unicodeChar) + { + *ptr++ = unicodeChar; + } }; + +// clang-format off +// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :