Torc  0.1
torcbinaryplistserialiser.cpp
Go to the documentation of this file.
1 /* Class TorcBinaryPListSerialiser
2 *
3 * Copyright (C) Mark Kendall 2012-18
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 
20 // Qt
21 #include <QTextCodec>
22 #include <QtEndian>
23 #include <QUuid>
24 
25 // Torc
26 #include "torclogging.h"
27 #include "torcplist.h"
29 
30 #include <stdint.h>
31 
32 #define START_OBJECT { m_objectOffsets.append(Dest.size()); }
33 
41  : TorcSerialiser(),
42  m_referenceSize(8),
43  m_objectOffsets(),
44  m_strings()
45 {
46 }
47 
48 static inline void WriteReference(quint64 Reference, quint8 Size, quint8* Pointer, quint64 &Counter)
49 {
50  switch (Size)
51  {
52  case 1:
53  {
54  Pointer[Counter++] = (quint8)(Reference & 0xff);
55  return;
56  }
57  case 2:
58  {
59  quint16 buffer = qToBigEndian((quint16)(Reference & 0xffff));
60  for (int i = 0; i < 2; ++i)
61  Pointer[Counter++] = *((quint8*)&buffer + i);
62  return;
63  }
64  case 4:
65  {
66  quint32 buffer = qToBigEndian((quint32)(Reference & 0xffffffff));
67  for (int i = 0; i < 4; ++i)
68  Pointer[Counter++] = *((quint8*)&buffer + i);
69  return;
70  }
71  case 8:
72  {
73  quint64 buffer = qToBigEndian(Reference);
74  for (int i = 0; i < 8; ++i)
75  Pointer[Counter++] = *((quint8*)&buffer + i);
76  return;
77  }
78  }
79 }
80 
82 {
84 }
85 
87 {
88 }
89 
90 void TorcBinaryPListSerialiser::Begin(QByteArray &Dest)
91 {
92  Dest.reserve(1024);
93  m_objectOffsets.clear();
94  Dest.append("bplist00");
95 }
96 
97 void TorcBinaryPListSerialiser::AddProperty(QByteArray &Dest, const QString &Name, const QVariant &Value)
98 {
99  // this is the maximum count before string optimisation
100  quint64 count = 2;
101  CountObjects(count, Value);
102 
103  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Max object count %1").arg(count));
104 
105  m_referenceSize = count < 0x000000ff ? 1 :
106  count < 0x0000ffff ? 2 :
107  count < 0xffffffff ? 4 : 8;
108 
110  Dest.append((quint8)(TorcPList::BPLIST_DICT | 1));
111  quint64 offset = Dest.size();
112  QByteArray references(2 * m_referenceSize, 0);
113  Dest.append(references);
114 
115  WriteReference(BinaryFromQString(Dest, Name), m_referenceSize, (quint8*)Dest.data(), offset);
116  WriteReference(BinaryFromVariant(Dest, Name, Value), m_referenceSize, (quint8*)Dest.data(), offset);
117 }
118 
119 void TorcBinaryPListSerialiser::End(QByteArray &Dest)
120 {
121  quint64 table = Dest.size();
122 
123  quint8 offsetsize = table < 0x000000ff ? 1 :
124  table < 0x0000ffff ? 2 :
125  table < 0xffffffff ? 4 : 8;
126 
127  QList<quint64>::const_iterator it = m_objectOffsets.constBegin();
128  switch (offsetsize)
129  {
130  case 1:
131  {
132  for ( ; it != m_objectOffsets.constEnd(); ++it)
133  Dest.append((quint8)((*it) & 0xff));
134  break;
135  }
136  case 2:
137  {
138  for ( ; it != m_objectOffsets.constEnd(); ++it)
139  {
140  quint16 buffer = qToBigEndian((quint16)((*it) & 0xffff));
141  Dest.append(*((quint8*)&buffer));
142  Dest.append(*((quint8*)&buffer + 1));
143  }
144  break;
145  }
146  case 4:
147  {
148  for ( ; it != m_objectOffsets.constEnd(); ++it)
149  {
150  quint32 buffer= qToBigEndian((quint32)((*it) & 0xffffffff));
151  Dest.append(*((quint8*)&buffer));
152  Dest.append(*((quint8*)&buffer + 1));
153  Dest.append(*((quint8*)&buffer + 2));
154  Dest.append(*((quint8*)&buffer + 3));
155  }
156  break;
157  }
158  case 8:
159  {
160  for ( ; it != m_objectOffsets.constEnd(); ++it)
161  {
162  quint64 buffer = qToBigEndian((*it));
163  Dest.append(*((quint8*)&buffer));
164  Dest.append(*((quint8*)&buffer + 1));
165  Dest.append(*((quint8*)&buffer + 2));
166  Dest.append(*((quint8*)&buffer + 3));
167  Dest.append(*((quint8*)&buffer + 4));
168  Dest.append(*((quint8*)&buffer + 5));
169  Dest.append(*((quint8*)&buffer + 6));
170  Dest.append(*((quint8*)&buffer + 7));
171  }
172  break;
173  }
174  default:
175  break;
176  }
177 
178  table = qToBigEndian(table);
179  quint64 count = qToBigEndian((quint64)m_objectOffsets.count());
180 
181  QByteArray trailer(32, 0);
182  trailer[6] = offsetsize;
183  trailer[7] = m_referenceSize;
184  trailer[8] = *((quint8*)&count);
185  trailer[9] = *((quint8*)&count + 1);
186  trailer[10] = *((quint8*)&count + 2);
187  trailer[11] = *((quint8*)&count + 3);
188  trailer[12] = *((quint8*)&count + 4);
189  trailer[13] = *((quint8*)&count + 5);
190  trailer[14] = *((quint8*)&count + 6);
191  trailer[15] = *((quint8*)&count + 7);
192  trailer[24] = *((quint8*)&table);
193  trailer[25] = *((quint8*)&table + 1);
194  trailer[26] = *((quint8*)&table + 2);
195  trailer[27] = *((quint8*)&table + 3);
196  trailer[28] = *((quint8*)&table + 4);
197  trailer[29] = *((quint8*)&table + 5);
198  trailer[30] = *((quint8*)&table + 6);
199  trailer[31] = *((quint8*)&table + 7);
200 
201  Dest.append(trailer);
202 
203  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("Actual object count %1").arg(m_objectOffsets.count()));
204 
205 #if 1
206  QByteArray testdata(Dest.data(), Dest.size());
207  TorcPList test(testdata);
208  LOG(VB_GENERAL, LOG_INFO, QStringLiteral("\n%1").arg(test.ToString()));
209 #endif
210 }
211 
212 quint64 TorcBinaryPListSerialiser::BinaryFromVariant(QByteArray &Dest, const QString &Name, const QVariant &Value)
213 {
214  // object formats not used: ascii string, set
215  quint64 result = m_objectOffsets.size();
216 
217  if (Value.isNull())
218  {
219  Dest.append((char)TorcPList::BPLIST_NULL);
220  return result;
221  }
222 
223  switch ((int)Value.type())
224  {
225  case QMetaType::QVariantList: return BinaryFromArray(Dest, Name, Value.toList());
226  case QMetaType::QStringList: return BinaryFromStringList(Dest, Name, Value.toStringList());
227  case QMetaType::QVariantMap: return BinaryFromMap(Dest, Name, Value.toMap());
228  case QMetaType::QUuid: BinaryFromUuid(Dest, Value); return result;
229  case QMetaType::QByteArray: BinaryFromData(Dest, Value); return result;
230  case QMetaType::Bool:
231  {
233  bool value = Value.toBool();
234  Dest.append((quint8)(TorcPList::BPLIST_NULL | (value ? TorcPList::BPLIST_TRUE : TorcPList::BPLIST_FALSE)));
235  return result;
236  }
237  case QMetaType::Char:
238  {
240  Dest.append((quint8)(TorcPList::BPLIST_NULL | TorcPList::BPLIST_FILL));
241  return result;
242  }
243  case QMetaType::Int:
244  case QMetaType::Short:
245  case QMetaType::Long:
246  case QMetaType::LongLong:
247  case QMetaType::Float:
248  case QMetaType::Double:
249  {
251  double value = Value.toDouble();
252  Dest.append((quint8)(TorcPList::BPLIST_REAL | 3));
253 #if Q_BYTE_ORDER == Q_BIG_ENDIAN && !defined (__VFP_FP__)
254  Dest.append(*((char*)&value));
255  Dest.append(*((char*)&value + 1));
256  Dest.append(*((char*)&value + 2));
257  Dest.append(*((char*)&value + 3));
258  Dest.append(*((char*)&value + 4));
259  Dest.append(*((char*)&value + 5));
260  Dest.append(*((char*)&value + 6));
261  Dest.append(*((char*)&value + 7));
262 #else
263  Dest.append(*((char*)&value + 7));
264  Dest.append(*((char*)&value + 6));
265  Dest.append(*((char*)&value + 5));
266  Dest.append(*((char*)&value + 4));
267  Dest.append(*((char*)&value + 3));
268  Dest.append(*((char*)&value + 2));
269  Dest.append(*((char*)&value + 1));
270  Dest.append(*((char*)&value));
271 #endif
272  return result;
273  }
274  case QMetaType::UInt:
275  case QMetaType::UShort:
276  case QMetaType::ULong:
277  case QMetaType::ULongLong:
278  {
280  BinaryFromUInt(Dest, Value.toULongLong());
281  return result;
282  }
283  case QMetaType::QDateTime:
284  {
286  Dest.append((quint8)(TorcPList::BPLIST_DATE | 3));
287  double value = (double)Value.toDateTime().toTime_t();
288 #if Q_BYTE_ORDER == Q_BIG_ENDIAN && !defined (__VFP_FP__)
289  Dest.append(*((char*)&value));
290  Dest.append(*((char*)&value + 1));
291  Dest.append(*((char*)&value + 2));
292  Dest.append(*((char*)&value + 3));
293  Dest.append(*((char*)&value + 4));
294  Dest.append(*((char*)&value + 5));
295  Dest.append(*((char*)&value + 6));
296  Dest.append(*((char*)&value + 7));
297 #else
298  Dest.append(*((char*)&value + 7));
299  Dest.append(*((char*)&value + 6));
300  Dest.append(*((char*)&value + 5));
301  Dest.append(*((char*)&value + 4));
302  Dest.append(*((char*)&value + 3));
303  Dest.append(*((char*)&value + 2));
304  Dest.append(*((char*)&value + 1));
305  Dest.append(*((char*)&value));
306 #endif
307  return result;
308  }
309  case QMetaType::QString:
310  default:
311  return BinaryFromQString(Dest, Value.toString());
312  }
313 }
314 
315 quint64 TorcBinaryPListSerialiser::BinaryFromStringList(QByteArray &Dest, const QString &Name, const QStringList &Value)
316 {
317  (void)Name;
318 
319  quint64 result = m_objectOffsets.size();
321 
322  int size = Value.size();
323  Dest.append((quint8)(TorcPList::BPLIST_ARRAY | (size < BPLIST_LOW_MAX ? size : BPLIST_LOW_MAX)));
324  if (size >= BPLIST_LOW_MAX)
325  BinaryFromUInt(Dest, size);
326 
327  quint64 offset = Dest.size();
328  QByteArray references(size * m_referenceSize, 0);
329  Dest.append(references);
330 
331  QStringList::const_iterator it = Value.constBegin();
332  for ( ; it != Value.constEnd(); ++it)
333  WriteReference(BinaryFromQString(Dest, (*it)), m_referenceSize, (quint8*)Dest.data(), offset);
334 
335  return result;
336 }
337 
338 quint64 TorcBinaryPListSerialiser::BinaryFromArray(QByteArray &Dest, const QString &Name, const QVariantList &Value)
339 {
340  if (!Value.isEmpty())
341  {
342  int type = Value[0].type();
343 
344  QVariantList::const_iterator it = Value.constBegin();
345  for ( ; it != Value.constEnd(); ++it)
346  if ((int)(*it).type() != type)
347  return BinaryFromQString(Dest, QStringLiteral("Error: QVariantList is not valid service return type"));
348  }
349 
350  quint64 result = m_objectOffsets.size();
352 
353  int size = Value.size();
354  Dest.append((quint8)(TorcPList::BPLIST_ARRAY | (size < BPLIST_LOW_MAX ? size : BPLIST_LOW_MAX)));
355  if (size >= BPLIST_LOW_MAX)
356  BinaryFromUInt(Dest, size);
357 
358  quint64 offset = Dest.size();
359  QByteArray references(size * m_referenceSize, 0);
360  Dest.append(references);
361 
362  QVariantList::const_iterator it = Value.constBegin();
363  for ( ; it != Value.constEnd(); ++it)
364  WriteReference(BinaryFromVariant(Dest, Name, (*it)), m_referenceSize, (quint8*)Dest.data(), offset);
365 
366  return result;
367 }
368 
369 quint64 TorcBinaryPListSerialiser::BinaryFromMap(QByteArray &Dest, const QString &Name, const QVariantMap &Value)
370 {
371  (void)Name;
372 
373  quint64 result = m_objectOffsets.size();
375 
376  int size = Value.size();
377  Dest.append((quint8)(TorcPList::BPLIST_DICT | (size < BPLIST_LOW_MAX ? size : BPLIST_LOW_MAX)));
378  if (size >= BPLIST_LOW_MAX)
379  BinaryFromUInt(Dest, size);
380 
381  quint64 offset = Dest.size();
382  QByteArray references(size * 2 * m_referenceSize, 0);
383  Dest.append(references);
384 
385  QVariantMap::const_iterator it = Value.constBegin();
386  for ( ; it != Value.constEnd(); ++it)
387  WriteReference(BinaryFromQString(Dest, it.key()), m_referenceSize, (quint8*)Dest.data(), offset);
388 
389  it = Value.begin();
390  for ( ; it != Value.end(); ++it)
391  WriteReference(BinaryFromVariant(Dest, it.key(), it.value()), m_referenceSize, (quint8*)Dest.data(), offset);
392 
393  return result;
394 }
395 
396 quint64 TorcBinaryPListSerialiser::BinaryFromQString(QByteArray &Dest, const QString &Value)
397 {
398  static QTextCodec* textCodec = QTextCodec::codecForName("UTF-16BE");
399 
400  QHash<QString,quint64>::const_iterator it = m_strings.constFind(Value);
401  if (it != m_strings.constEnd())
402  return it.value();
403 
404  quint64 result = (quint64)m_objectOffsets.size();
405  m_strings.insert(Value, result);
406 
408 
409  QByteArray output = textCodec->fromUnicode(Value);
410 
411  quint64 size = (output.size() >> 1) - 1;
412 
413  Dest.append((quint8)(TorcPList::BPLIST_UNICODE | (size < BPLIST_LOW_MAX ? size : BPLIST_LOW_MAX)));
414  if (size >= BPLIST_LOW_MAX)
415  BinaryFromUInt(Dest, size);
416 
417  Dest.append(output.data() + 2, output.size() - 2);
418  return result;
419 }
420 
421 inline void binaryFromUint(QByteArray &Dest, quint64 Value, uint8_t size)
422 {
423  if (8 == size)
424  {
425  Dest.append(*((quint8*)&Value));
426  Dest.append(*((quint8*)&Value + 1));
427  Dest.append(*((quint8*)&Value + 2));
428  Dest.append(*((quint8*)&Value + 3));
429  Dest.append(*((quint8*)&Value + 4));
430  Dest.append(*((quint8*)&Value + 5));
431  Dest.append(*((quint8*)&Value + 6));
432  Dest.append(*((quint8*)&Value + 7));
433  }
434  else if (4 == size)
435  {
436  quint32 value32= qToBigEndian((quint32)(Value & 0xffffffff));
437  Dest.append(*((quint8*)&value32));
438  Dest.append(*((quint8*)&value32 + 1));
439  Dest.append(*((quint8*)&value32 + 2));
440  Dest.append(*((quint8*)&value32 + 3));
441  }
442  else if (2 == size)
443  {
444  quint16 value16 = qToBigEndian((quint16)(Value & 0xffff));
445  Dest.append(*((quint8*)&value16));
446  Dest.append(*((quint8*)&value16 + 1));
447  }
448  else
449  {
450  Dest.append((quint8)(Value & 0xff));
451  }
452 }
453 
454 void TorcBinaryPListSerialiser::BinaryFromData(QByteArray &Dest, const QVariant &Value)
455 {
457  QByteArray data = Value.toByteArray();
458  if (data.isNull())
459  {
460  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Failed to retrieve binary data"));
461  Dest.append((quint8)(TorcPList::BPLIST_NULL | TorcPList::BPLIST_FALSE));
462  return;
463  }
464 
465  int size = data.size();
466  Dest.append((quint8)(TorcPList::BPLIST_DATA | (size < BPLIST_LOW_MAX ? size : BPLIST_LOW_MAX)));
467  if (size >= BPLIST_LOW_MAX)
468  BinaryFromUInt(Dest, size);
469  Dest.append(data);
470 }
471 
472 void TorcBinaryPListSerialiser::BinaryFromUuid(QByteArray &Dest, const QVariant &Value)
473 {
475  QByteArray value = Value.toUuid().toRfc4122();
476  if (value.size() == 16)
477  {
478 
479  quint64 buffer = (quint64)(qToBigEndian(*((quint64*)value.constData() + 8)));
480  uint8_t size = buffer <= UINT8_MAX ? 1 : buffer <= UINT16_MAX ? 2 : buffer <= UINT32_MAX ? 4 : 8;
481  Dest.append((quint8)(TorcPList::BPLIST_UID | (size -1)));
482  binaryFromUint(Dest, buffer, size);
483  }
484  else
485  {
486  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Unknown UUID binary with size %1 bytes").arg(value.size()));
487  Dest.append((quint8)(TorcPList::BPLIST_NULL | TorcPList::BPLIST_FALSE));
488  }
489 }
490 
491 void TorcBinaryPListSerialiser::BinaryFromUInt(QByteArray &Dest, quint64 Value)
492 {
493  uint8_t size = Value <= UINT8_MAX ? 0 : Value <= UINT16_MAX ? 1 : Value <= UINT32_MAX ? 2 : 3;
494  Dest.append((quint8)(TorcPList::BPLIST_UINT | size));
495  binaryFromUint(Dest, Value, 2 ^ size);
496 }
497 
498 void TorcBinaryPListSerialiser::CountObjects(quint64 &Count, const QVariant &Value)
499 {
500  switch (static_cast<QMetaType::Type>(Value.type()))
501  {
502  case QMetaType::QVariantMap:
503  {
504  QVariantMap map = Value.toMap();
505  QVariantMap::const_iterator it = map.constBegin();
506  for ( ; it != map.constEnd(); ++it)
507  CountObjects(Count, it.value());
508  Count += map.size();
509  return;
510  }
511  case QMetaType::QVariantList:
512  {
513  Count++;
514  bool dict = false;
515  QVariantList list = Value.toList();
516  QVariantList::const_iterator it = list.constBegin();
517  uint type = list.isEmpty() ? QMetaType::UnknownType : static_cast<QMetaType::Type>(list[0].type());
518  for ( ; it != list.constEnd(); ++it)
519  {
520  if ((*it).type() != type)
521  dict = true;
522  CountObjects(Count, (*it));
523  }
524  if (dict)
525  Count += list.size();
526  return;
527  }
528  case (QVariant::Type)QMetaType::QStringList:
529  {
530  Count++;
531  QStringList list = Value.toStringList();
532  Count += list.size();
533  return;
534  }
535  default:
536  break;
537  }
538 
539  Count++;
540 }
541 
543 {
544  public:
545  TorcBinaryPListSerialiserFactory() : TorcSerialiserFactory(QStringLiteral("application"), QStringLiteral("x-plist"), QStringLiteral("Binary PList"))
546  {
547  }
548 
550  {
551  return new TorcBinaryPListSerialiser();
552  }
554 
556 {
557  public:
558  TorcAppleBinaryPListSerialiserFactory() : TorcSerialiserFactory(QStringLiteral("application"), QStringLiteral("x-apple-binary-plist"), QStringLiteral("Binary PList"))
559  {
560  }
561 
563  {
564  return new TorcBinaryPListSerialiser();
565  }
567 
void Begin(QByteArray &Dest) override
static void WriteReference(quint64 Reference, quint8 Size, quint8 *Pointer, quint64 &Counter)
void binaryFromUint(QByteArray &Dest, quint64 Value, uint8_t size)
#define START_OBJECT
TorcBinaryPListSerialiserFactory TorcBinaryPListSerialiserFactory
void Prepare(QByteArray &) override
void AddProperty(QByteArray &Dest, const QString &Name, const QVariant &Value) override
A parser for binary property lists.
Definition: torcplist.h:12
void End(QByteArray &Dest) override
HTTPResponseType
#define BPLIST_LOW_MAX
Definition: torcplist.h:10
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
HTTPResponseType ResponseType(void) override
TorcAppleBinaryPListSerialiserFactory TorcAppleBinaryPListSerialiserFactory