This repository was archived by the owner on Mar 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathsynchelper.cpp
138 lines (118 loc) · 3.08 KB
/
synchelper.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "synchelper_p.h"
#include <QtCore/QCryptographicHash>
#include <QtCore/QVariant>
#include <QtCore/QLocale>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonArray>
#include "message_p.h"
using namespace QtDataSync;
using namespace QtDataSync::SyncHelper;
using std::tuple;
using std::make_tuple;
namespace {
void hashNext(QCryptographicHash &hash, const QJsonValue &value);
}
QByteArray SyncHelper::jsonHash(const QJsonObject &object)
{
QCryptographicHash hash(QCryptographicHash::Sha3_256);
hashNext(hash, object);
return hash.result();
}
QByteArray SyncHelper::combine(const ObjectKey &key, quint64 version, const QJsonObject &data)
{
QByteArray out;
QDataStream stream(&out, QIODevice::WriteOnly | QIODevice::Unbuffered);
Message::setupStream(stream);
stream << key
<< version
<< QJsonDocument(data).toJson(QJsonDocument::Compact);
if(stream.status() != QDataStream::Ok)
throw DataStreamException(stream);
return out;
}
QByteArray SyncHelper::combine(const ObjectKey &key, quint64 version)
{
QByteArray out;
QDataStream stream(&out, QIODevice::WriteOnly | QIODevice::Unbuffered);
Message::setupStream(stream);
stream << key
<< version
<< QByteArray();
if(stream.status() != QDataStream::Ok)
throw DataStreamException(stream);
return out;
}
tuple<bool, ObjectKey, quint64, QJsonObject> SyncHelper::extract(const QByteArray &data)
{
ObjectKey key;
quint64 version;
QByteArray jData;
QDataStream stream(data);
Message::setupStream(stream);
stream.startTransaction();
stream >> key
>> version
>> jData;
QJsonObject obj;
if(jData.isNull())
stream.commitTransaction();
else {
QJsonParseError error;
auto doc = QJsonDocument::fromJson(jData, &error);
if(error.error != QJsonParseError::NoError || !doc.isObject())
stream.abortTransaction();
else {
obj = doc.object();
stream.commitTransaction();
}
}
if(stream.status() != QDataStream::Ok)
throw DataStreamException(stream);
return make_tuple(jData.isNull(), key, version, obj);
}
namespace {
void hashNext(QCryptographicHash &hash, const QJsonValue &value)
{
switch (value.type()) {
case QJsonValue::Null:
hash.addData("null");
break;
case QJsonValue::Bool:
hash.addData(value.toBool() ? "true" : "false");
break;
case QJsonValue::Double:
hash.addData(QByteArray::number(value.toDouble(), 'g', QLocale::FloatingPointShortest));
break;
case QJsonValue::String:
hash.addData(value.toString().toUtf8());
break;
case QJsonValue::Array:
for(auto v : value.toArray()) // clazy:exclude=range-loop
hashNext(hash, v);
break;
case QJsonValue::Object:
{
auto obj = value.toObject();
//helper code to assert the obj iterator is sorted.
#ifndef QT_NO_DEBUG
QString pKey;
#endif
for(auto it = obj.begin(); it != obj.end(); it++) { //if "keys" is sorted, this must be as well
#ifndef QT_NO_DEBUG
if(!pKey.isNull())
Q_ASSERT(pKey < it.key());
pKey = it.key();
#endif
hash.addData(it.key().toUtf8());
hashNext(hash, it.value());
}
break;
}
case QJsonValue::Undefined: //ignored for the hash
break;
default:
Q_UNREACHABLE();
break;
}
}
}