mirror of https://github.com/qt/qtgrpc.git
Privately introduce QProtobufPropertyOrderBuilder
I wanted to write a Builder class for this for some time now because protobuf technically supports dynamically retrieving new types using some URL scheme. But we never needed it, so it wasn't done. Now that we want to treat a Map entry as what it is - a message with two fields - we need to be able to set up a "property ordering" object for it. This is what the builder is for. Pick-to: 6.8 Change-Id: I3c8da6be98745c5ba270ca320b2898157a1b32e4 Reviewed-by: Tatiana Borisova <tatiana.borisova@qt.io>
This commit is contained in:
parent
15e10394d7
commit
af54f630a2
|
@ -137,7 +137,7 @@ CMakeCache.txt
|
|||
cmake_install.cmake
|
||||
CTestTestfile.cmake
|
||||
CMakeLists.txt.user
|
||||
**build*
|
||||
**/build/
|
||||
|
||||
# vscode specific
|
||||
.vscode/
|
||||
|
|
|
@ -14,6 +14,7 @@ qt_internal_add_module(Protobuf
|
|||
qtprotobuflogging.cpp qtprotobuflogging_p.h
|
||||
qprotobufregistration.cpp qprotobufregistration.h
|
||||
qprotobufpropertyordering.cpp qprotobufpropertyordering.h
|
||||
qprotobufpropertyorderingbuilder_p.h
|
||||
qtprotobuftypes.cpp qtprotobuftypes.h
|
||||
qprotobufoneof.cpp qprotobufoneof.h
|
||||
qtprotobufdefs_p.h
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include "qprotobufpropertyordering.h"
|
||||
#include "qprotobufpropertyorderingbuilder_p.h"
|
||||
#include "qtprotobuflogging_p.h"
|
||||
#include "qtprotobufdefs_p.h"
|
||||
#include "qprotobufregistration.h"
|
||||
|
||||
|
||||
|
@ -212,6 +214,117 @@ QProtobufMessagePointer constructMessageByName(const QString &messageType)
|
|||
return pointer;
|
||||
}
|
||||
|
||||
class QProtobufPropertyOrderingBuilderPrivate
|
||||
{
|
||||
public:
|
||||
struct FieldDefinition
|
||||
{
|
||||
QByteArray jsonName;
|
||||
uint fieldNumber;
|
||||
uint propertyIndex;
|
||||
FieldFlags flags;
|
||||
};
|
||||
|
||||
std::vector<FieldDefinition> fields;
|
||||
QByteArray packageName;
|
||||
};
|
||||
|
||||
QProtobufPropertyOrderingBuilder::QProtobufPropertyOrderingBuilder(QByteArray packageName)
|
||||
: d_ptr(new QProtobufPropertyOrderingBuilderPrivate())
|
||||
{
|
||||
|
||||
d_ptr->packageName = std::move(packageName);
|
||||
}
|
||||
|
||||
QProtobufPropertyOrderingBuilder::~QProtobufPropertyOrderingBuilder()
|
||||
{
|
||||
delete d_ptr;
|
||||
}
|
||||
|
||||
void QProtobufPropertyOrderingBuilder::addV0Field(QByteArray jsonName, uint fieldNumber,
|
||||
uint propertyIndex, FieldFlags flags)
|
||||
{
|
||||
Q_D(QProtobufPropertyOrderingBuilder);
|
||||
Q_ASSERT_X(fieldNumber <= ProtobufFieldNumMax && fieldNumber >= ProtobufFieldNumMin,
|
||||
"QProtobufPropertyOrderingBuilder::addV0Field",
|
||||
"Field number is out of the valid field number range.");
|
||||
Q_ASSERT(d->fields.size() < ProtobufFieldMaxCount);
|
||||
d->fields.push_back({ std::move(jsonName), fieldNumber, propertyIndex, flags });
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Builds the QProtobufPropertyOrdering object from the builder data.
|
||||
|
||||
The Data struct must be manually destructed (var->~Data()) and then
|
||||
free()d by the caller.
|
||||
*/
|
||||
QProtobufPropertyOrdering::Data *QProtobufPropertyOrderingBuilder::build() const
|
||||
{
|
||||
Q_D(const QProtobufPropertyOrderingBuilder);
|
||||
|
||||
if (d->fields.size() > ProtobufFieldMaxCount)
|
||||
return nullptr;
|
||||
|
||||
qsizetype charSpaceNeeded = NullTerminator + d->packageName.size() + NullTerminator;
|
||||
for (const auto &field : d->fields)
|
||||
charSpaceNeeded += field.jsonName.size() + NullTerminator;
|
||||
|
||||
const size_t uintSpaceNeeded = sizeof(QProtobufPropertyOrdering::Data)
|
||||
+ d->fields.size() * sizeof(uint) * 4 + sizeof(uint) /* eos marker offset */;
|
||||
|
||||
static_assert(sizeof(QProtobufPropertyOrdering::Data) == 24,
|
||||
"Data size changed, update builder code");
|
||||
|
||||
const size_t spaceNeeded = uintSpaceNeeded + charSpaceNeeded;
|
||||
|
||||
auto *storage = static_cast<char *>(calloc(1, spaceNeeded));
|
||||
auto *data = new (storage) QProtobufPropertyOrdering::Data();
|
||||
auto raii = qScopeGuard([data] { data->~Data(); free(data); });
|
||||
|
||||
data->version = 0u;
|
||||
data->numFields = uint(d->fields.size());
|
||||
data->fieldNumberOffset = uint(d->fields.size() * 1 + 1);
|
||||
data->propertyIndexOffset = uint(d->fields.size() * 2 + 1);
|
||||
data->flagsOffset = uint(d->fields.size() * 3 + 1);
|
||||
data->fullPackageNameSize = uint(d->packageName.size());
|
||||
|
||||
using NonConstTag = QProtobufPropertyOrdering::NonConstTag;
|
||||
QProtobufPropertyOrdering ordering{data};
|
||||
|
||||
uint *uintData = ordering.uint_data(NonConstTag{});
|
||||
uint jsonArrayOffset = data->fullPackageNameSize + NullTerminator;
|
||||
for (uint i = 0; i < data->numFields; ++i) {
|
||||
const auto &field = d->fields[i];
|
||||
if (field.fieldNumber > ProtobufFieldNumMax || field.fieldNumber < ProtobufFieldNumMin)
|
||||
return nullptr;
|
||||
|
||||
uintData[i] = jsonArrayOffset;
|
||||
jsonArrayOffset += field.jsonName.size() + NullTerminator;
|
||||
uintData[i + data->fieldNumberOffset] = field.fieldNumber;
|
||||
uintData[i + data->propertyIndexOffset] = field.propertyIndex;
|
||||
uintData[i + data->flagsOffset] = uint(field.flags.toInt());
|
||||
}
|
||||
uintData[d->fields.size()] = jsonArrayOffset;
|
||||
Q_ASSERT(jsonArrayOffset + NullTerminator == charSpaceNeeded);
|
||||
|
||||
char *charData = ordering.char_data(NonConstTag{});
|
||||
[[maybe_unused]]
|
||||
char * const charStart = charData;
|
||||
charData = std::copy_n(d->packageName.constData(), d->packageName.size() + NullTerminator,
|
||||
charData);
|
||||
for (auto &field : d->fields) {
|
||||
charData = std::copy_n(field.jsonName.constData(), field.jsonName.size() + NullTerminator,
|
||||
charData);
|
||||
}
|
||||
charData[0] = '\0'; // Empty string at the end of the char array
|
||||
++charData; // Trailing null terminator
|
||||
Q_ASSERT(std::distance(charStart, charData) == charSpaceNeeded);
|
||||
Q_ASSERT(quintptr(charData) - quintptr(data) == quintptr(spaceNeeded));
|
||||
|
||||
raii.dismiss();
|
||||
return data;
|
||||
}
|
||||
} // namespace QtProtobufPrivate
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -33,6 +33,7 @@ enum FieldFlag : uint {
|
|||
Q_DECLARE_FLAGS(FieldFlags, FieldFlag)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(FieldFlags)
|
||||
|
||||
class QProtobufPropertyOrderingBuilder;
|
||||
struct QProtobufPropertyOrdering
|
||||
{
|
||||
const struct Data
|
||||
|
@ -54,6 +55,7 @@ struct QProtobufPropertyOrdering
|
|||
int fieldCount() const { return int(data->numFields); }
|
||||
|
||||
private:
|
||||
friend class QProtobufPropertyOrderingBuilder;
|
||||
struct NonConstTag {};
|
||||
uint *uint_data(NonConstTag) const;
|
||||
char *char_data(NonConstTag) const;
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QPROTOBUFPROPERTYORDERINGBUILDER_P_H
|
||||
#define QPROTOBUFPROPERTYORDERINGBUILDER_P_H
|
||||
|
||||
#include <QtProtobuf/qtprotobufglobal.h>
|
||||
|
||||
#include <QtProtobuf/qprotobufpropertyordering.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
// Source is in qprotobufpropertyordering.cpp
|
||||
// The intent is to share some of the code
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of other Qt classes. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QtProtobufPrivate {
|
||||
|
||||
class QProtobufPropertyOrderingBuilderPrivate;
|
||||
class QProtobufPropertyOrderingBuilder
|
||||
{
|
||||
public:
|
||||
Q_PROTOBUF_EXPORT explicit QProtobufPropertyOrderingBuilder(QByteArray packageName);
|
||||
Q_DISABLE_COPY_MOVE(QProtobufPropertyOrderingBuilder)
|
||||
Q_PROTOBUF_EXPORT ~QProtobufPropertyOrderingBuilder();
|
||||
|
||||
Q_PROTOBUF_EXPORT void addV0Field(QByteArray jsonName, uint fieldNumber, uint propertyIndex,
|
||||
FieldFlags flags);
|
||||
Q_PROTOBUF_EXPORT QProtobufPropertyOrdering::Data *build() const;
|
||||
|
||||
private:
|
||||
QProtobufPropertyOrderingBuilderPrivate *d_ptr;
|
||||
Q_DECLARE_PRIVATE(QProtobufPropertyOrderingBuilder)
|
||||
};
|
||||
|
||||
} // namespace QtProtobufPrivate
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QPROTOBUFPROPERTYORDERINGBUILDER_P_H
|
|
@ -19,6 +19,7 @@ QT_BEGIN_NAMESPACE
|
|||
|
||||
constexpr int ProtobufFieldNumMin = 1;
|
||||
constexpr int ProtobufFieldNumMax = 536870911;
|
||||
constexpr int ProtobufFieldMaxCount = ProtobufFieldNumMax - ProtobufFieldNumMin + 1;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
|
|
@ -31,3 +31,4 @@ if(TARGET Qt6::qtprotobufgen)
|
|||
endif()
|
||||
add_subdirectory(qprotobuflazymessagepointer)
|
||||
add_subdirectory(qprotobufoneof)
|
||||
add_subdirectory(qprotobufpropertyorderingbuilder)
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(tst_qprotobufoneof LANGUAGES CXX)
|
||||
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||
endif()
|
||||
|
||||
qt_internal_add_test(tst_qprotobufpropertyorderingbuilder
|
||||
SOURCES
|
||||
tst_qprotobufpropertyorderingbuilder.cpp
|
||||
LIBRARIES
|
||||
Qt::ProtobufPrivate
|
||||
)
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// Copyright (C) 2022 Alexey Edelev <semlanik@gmail.com>, Viktor Kopp <vifactor@gmail.com>
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include <QtProtobuf/private/qprotobufpropertyorderingbuilder_p.h>
|
||||
|
||||
class tst_QProtobufPropertyOrderingBuilder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void buildObject();
|
||||
};
|
||||
|
||||
struct DataDeleter {
|
||||
void operator()(QtProtobufPrivate::QProtobufPropertyOrdering::Data *ptr) noexcept
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QProtobufPropertyOrderingBuilder::buildObject()
|
||||
{
|
||||
using namespace QtProtobufPrivate;
|
||||
QProtobufPropertyOrderingBuilder builder("TestMessage");
|
||||
builder.addV0Field("field1", 1, 0, FieldFlag::Optional);
|
||||
builder.addV0Field("field6", 6, 1, FieldFlag::Repeated | FieldFlag::Enum);
|
||||
builder.addV0Field("field30", 30, 60, FieldFlag::NoFlags);
|
||||
std::unique_ptr<QProtobufPropertyOrdering::Data, DataDeleter> data(builder.build());
|
||||
|
||||
QProtobufPropertyOrdering ordering{data.get()};
|
||||
QCOMPARE(ordering.getMessageFullName(), QByteArray("TestMessage"));
|
||||
QCOMPARE(ordering.fieldCount(), 3);
|
||||
|
||||
QCOMPARE(ordering.indexOfFieldNumber(1), 0);
|
||||
QCOMPARE(ordering.indexOfFieldNumber(6), 1);
|
||||
QCOMPARE(ordering.indexOfFieldNumber(30), 2);
|
||||
QCOMPARE(ordering.indexOfFieldNumber(10), -1);
|
||||
|
||||
QCOMPARE(ordering.getPropertyIndex(0), 0);
|
||||
QCOMPARE(ordering.getPropertyIndex(1), 1);
|
||||
QCOMPARE(ordering.getPropertyIndex(2), 60);
|
||||
QCOMPARE(ordering.getPropertyIndex(3), -1);
|
||||
|
||||
QCOMPARE(ordering.getFieldNumber(0), 1);
|
||||
QCOMPARE(ordering.getFieldNumber(1), 6);
|
||||
QCOMPARE(ordering.getFieldNumber(2), 30);
|
||||
QCOMPARE(ordering.getFieldNumber(3), -1);
|
||||
|
||||
QCOMPARE(ordering.getJsonName(0), QByteArray("field1"));
|
||||
QCOMPARE(ordering.getJsonName(1), QByteArray("field6"));
|
||||
QCOMPARE(ordering.getJsonName(2), QByteArray("field30"));
|
||||
QCOMPARE(ordering.getJsonName(3), QByteArray());
|
||||
|
||||
QCOMPARE(ordering.getFieldFlags(0), FieldFlag::Optional);
|
||||
QCOMPARE(ordering.getFieldFlags(1), FieldFlag::Repeated | FieldFlag::Enum);
|
||||
QCOMPARE(ordering.getFieldFlags(2), FieldFlag::NoFlags);
|
||||
QCOMPARE(ordering.getFieldFlags(3), FieldFlag::NoFlags);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QProtobufPropertyOrderingBuilder)
|
||||
#include "tst_qprotobufpropertyorderingbuilder.moc"
|
Loading…
Reference in New Issue