1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2018 Intel Corporation. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "datastreamconverter.h" |
52 | |
53 | #include <QDataStream> |
54 | #include <QDebug> |
55 | #include <QTextStream> |
56 | |
57 | static const char optionHelp[] = |
58 | "byteorder=host|big|little Byte order to use.\n" |
59 | "version=<n> QDataStream version (default: Qt 5.0).\n" |
60 | ; |
61 | |
62 | static const char signature[] = "qds" ; |
63 | |
64 | static DataStreamDumper dataStreamDumper; |
65 | static DataStreamConverter DataStreamConverter; |
66 | |
67 | QDataStream &operator<<(QDataStream &ds, const VariantOrderedMap &map) |
68 | { |
69 | ds << qint64(map.size()); |
70 | for (const auto &pair : map) |
71 | ds << pair.first << pair.second; |
72 | return ds; |
73 | } |
74 | |
75 | QDataStream &operator>>(QDataStream &ds, VariantOrderedMap &map) |
76 | { |
77 | map.clear(); |
78 | |
79 | qint64 size; |
80 | ds >> size; |
81 | map.reserve(asize: size); |
82 | |
83 | while (size-- > 0) { |
84 | VariantOrderedMap::value_type pair; |
85 | ds >> pair.first >> pair.second; |
86 | map.append(t: pair); |
87 | } |
88 | |
89 | return ds; |
90 | } |
91 | |
92 | |
93 | static QString dumpVariant(const QVariant &v, const QString &indent = QLatin1String("\n" )) |
94 | { |
95 | QString result; |
96 | QString indented = indent + QLatin1String(" " ); |
97 | |
98 | int type = v.userType(); |
99 | if (type == qMetaTypeId<VariantOrderedMap>() || type == QMetaType::QVariantMap) { |
100 | const auto map = (type == QMetaType::QVariantMap) ? |
101 | VariantOrderedMap(v.toMap()) : qvariant_cast<VariantOrderedMap>(v); |
102 | |
103 | result = QLatin1String("Map {" ); |
104 | for (const auto &pair : map) { |
105 | result += indented + dumpVariant(v: pair.first, indent: indented); |
106 | result.chop(n: 1); // remove comma |
107 | result += QLatin1String(" => " ) + dumpVariant(v: pair.second, indent: indented); |
108 | |
109 | } |
110 | result.chop(n: 1); // remove comma |
111 | result += indent + QLatin1String("}," ); |
112 | } else if (type == QMetaType::QVariantList) { |
113 | const QVariantList list = v.toList(); |
114 | |
115 | result = QLatin1String("List [" ); |
116 | for (const auto &item : list) |
117 | result += indented + dumpVariant(v: item, indent: indented); |
118 | result.chop(n: 1); // remove comma |
119 | result += indent + QLatin1String("]," ); |
120 | } else { |
121 | QDebug debug(&result); |
122 | debug.nospace() << v << ','; |
123 | } |
124 | return result; |
125 | } |
126 | |
127 | QString DataStreamDumper::name() |
128 | { |
129 | return QStringLiteral("datastream-dump" ); |
130 | } |
131 | |
132 | Converter::Direction DataStreamDumper::directions() |
133 | { |
134 | return Out; |
135 | } |
136 | |
137 | Converter::Options DataStreamDumper::outputOptions() |
138 | { |
139 | return SupportsArbitraryMapKeys; |
140 | } |
141 | |
142 | const char *DataStreamDumper::optionsHelp() |
143 | { |
144 | return nullptr; |
145 | } |
146 | |
147 | bool DataStreamDumper::probeFile(QIODevice *f) |
148 | { |
149 | Q_UNUSED(f); |
150 | return false; |
151 | } |
152 | |
153 | QVariant DataStreamDumper::loadFile(QIODevice *f, Converter *&outputConverter) |
154 | { |
155 | Q_UNREACHABLE(); |
156 | Q_UNUSED(f); |
157 | Q_UNUSED(outputConverter); |
158 | return QVariant(); |
159 | } |
160 | |
161 | void DataStreamDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) |
162 | { |
163 | Q_UNUSED(options); |
164 | QString s = dumpVariant(v: contents); |
165 | s[s.size() - 1] = QLatin1Char('\n'); // replace the comma with newline |
166 | |
167 | QTextStream out(f); |
168 | out << s; |
169 | } |
170 | |
171 | DataStreamConverter::DataStreamConverter() |
172 | { |
173 | qRegisterMetaType<VariantOrderedMap>(); |
174 | qRegisterMetaTypeStreamOperators<VariantOrderedMap>(); |
175 | } |
176 | |
177 | QString DataStreamConverter::name() |
178 | { |
179 | return QStringLiteral("datastream" ); |
180 | } |
181 | |
182 | Converter::Direction DataStreamConverter::directions() |
183 | { |
184 | return InOut; |
185 | } |
186 | |
187 | Converter::Options DataStreamConverter::outputOptions() |
188 | { |
189 | return SupportsArbitraryMapKeys; |
190 | } |
191 | |
192 | const char *DataStreamConverter::optionsHelp() |
193 | { |
194 | return optionHelp; |
195 | } |
196 | |
197 | bool DataStreamConverter::probeFile(QIODevice *f) |
198 | { |
199 | return f->isReadable() && f->peek(maxlen: sizeof(signature) - 1) == signature; |
200 | } |
201 | |
202 | QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter) |
203 | { |
204 | if (!outputConverter) |
205 | outputConverter = &dataStreamDumper; |
206 | |
207 | char c; |
208 | if (f->read(maxlen: sizeof(signature) -1) != signature || |
209 | !f->getChar(c: &c) || (c != 'l' && c != 'B')) { |
210 | fprintf(stderr, format: "Could not load QDataStream file: invalid signature.\n" ); |
211 | exit(EXIT_FAILURE); |
212 | } |
213 | |
214 | QDataStream ds(f); |
215 | ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian); |
216 | |
217 | std::underlying_type<QDataStream::Version>::type version; |
218 | ds >> version; |
219 | ds.setVersion(QDataStream::Version(version)); |
220 | |
221 | QVariant result; |
222 | ds >> result; |
223 | return result; |
224 | } |
225 | |
226 | void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) |
227 | { |
228 | QDataStream::Version version = QDataStream::Qt_5_0; |
229 | auto order = QDataStream::ByteOrder(QSysInfo::ByteOrder); |
230 | for (const QString &option : options) { |
231 | const QStringList pair = option.split(sep: '='); |
232 | if (pair.size() == 2) { |
233 | if (pair.first() == "byteorder" ) { |
234 | if (pair.last() == "little" ) { |
235 | order = QDataStream::LittleEndian; |
236 | continue; |
237 | } else if (pair.last() == "big" ) { |
238 | order = QDataStream::BigEndian; |
239 | continue; |
240 | } else if (pair.last() == "host" ) { |
241 | order = QDataStream::ByteOrder(QSysInfo::ByteOrder); |
242 | continue; |
243 | } |
244 | } |
245 | if (pair.first() == "version" ) { |
246 | bool ok; |
247 | int n = pair.last().toInt(ok: &ok); |
248 | if (ok) { |
249 | version = QDataStream::Version(n); |
250 | continue; |
251 | } |
252 | |
253 | fprintf(stderr, format: "Invalid version number '%s': must be a number from 1 to %d.\n" , |
254 | qPrintable(pair.last()), QDataStream::Qt_DefaultCompiledVersion); |
255 | exit(EXIT_FAILURE); |
256 | } |
257 | } |
258 | |
259 | fprintf(stderr, format: "Unknown QDataStream formatting option '%s'. Available options are:\n%s" , |
260 | qPrintable(option), optionHelp); |
261 | exit(EXIT_FAILURE); |
262 | } |
263 | |
264 | char c = order == QDataStream::LittleEndian ? 'l' : 'B'; |
265 | f->write(data: signature); |
266 | f->write(data: &c, len: 1); |
267 | |
268 | QDataStream ds(f); |
269 | ds.setVersion(version); |
270 | ds.setByteOrder(order); |
271 | ds << std::underlying_type<decltype(version)>::type(version); |
272 | ds << contents; |
273 | } |
274 | |