ArnLib  4.0.x
Active Registry Network
ArnXStringMap.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2022 Michael Wiklund.
2 // All rights reserved.
3 // Contact: arnlib@wiklunden.se
4 //
5 // This file is part of the ArnLib - Active Registry Network.
6 // Parts of ArnLib depend on Qt and/or other libraries that have their own
7 // licenses. Usage of these other libraries is subject to their respective
8 // license agreements.
9 //
10 // The MIT License (MIT) Usage
11 // Permission is hereby granted, free of charge, to any person obtaining a
12 // copy of this file to deal in its contained Software without restriction,
13 // including without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to permit
15 // persons to whom the Software is furnished to do so, subject to the
16 // following conditions:
17 // The above copyright notice and this permission notice shall be included
18 // in all copies or substantial portions of the Software in this file.
19 //
20 // GNU Lesser General Public License Usage
21 // This file may be used under the terms of the GNU Lesser General Public
22 // License version 2.1 as published by the Free Software Foundation and
23 // appearing in the file LICENSE_LGPL.txt included in the packaging of this
24 // file. In addition, as a special exception, you may use the rights described
25 // in the Nokia Qt LGPL Exception version 1.1, included in the file
26 // LGPL_EXCEPTION.txt in this package.
27 //
28 // GNU General Public License Usage
29 // Alternatively, this file may be used under the terms of the GNU General Public
30 // License version 3.0 as published by the Free Software Foundation and appearing
31 // in the file LICENSE_GPL.txt included in the packaging of this file.
32 //
33 // Other Usage
34 // Alternatively, this file may be used in accordance with the terms and conditions
35 // contained in a signed written agreement between you and Michael Wiklund.
36 //
37 // This program is distributed in the hope that it will be useful, but WITHOUT ANY
38 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
39 // PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
40 //
41 
42 #include "ArnInc/XStringMap.hpp"
43 #include <QMetaType>
44 #include <QDebug>
45 #include <limits>
46 #include <string.h>
47 
48 
49 namespace Arn {
50 
51 QByteArray XStringMap::_nullValue;
52 
53 
55 {
56  init();
57 }
58 
59 
61  : _keyList( other._keyList)
62  , _valList( other._valList)
63  , _size( other._size)
64 {
65 }
66 
67 
68 XStringMap::XStringMap( const QByteArray& xString)
69 {
70  init();
71  fromXString( xString);
72 }
73 
74 
75 XStringMap::XStringMap( const QVariantMap& variantMap)
76 {
77  init();
78  add( variantMap);
79 }
80 
81 
83 {
84 }
85 
86 
88 {
89  _keyList = other._keyList;
90  _valList = other._valList;
91  _size = other._size;
92 
93  return *this;
94 }
95 
96 
97 void XStringMap::init()
98 {
99  _hasBinCode = false;
100  clear( true);
101 }
102 
103 
104 void XStringMap::clear( bool freeMem)
105 {
106  _size = 0;
107 
108  if (freeMem) {
109  _keyList.clear();
110  _valList.clear();
111  }
112 }
113 
114 
116 {
117  _keyList.resize( _size);
118  _valList.resize( _size);
119  _keyList.squeeze();
120  _valList.squeeze();
121 }
122 
123 
125 {
126  return _options;
127 }
128 
129 
131 {
132  _options = newOptions;
133 }
134 
135 
136 int XStringMap::indexOf( const char* key, int from) const
137 {
138  if (key == arnNullptr) return -1; // Return Not found
139 
140  for (int i = from; i < _size; ++i) {
141  if (_keyList.at(i) == key) {
142  return i; // Return index of found key
143  }
144  }
145  return -1; // Return Not found
146 }
147 
148 
149 int XStringMap::indexOf( const QByteArray& key, int from) const
150 {
151  for (int i = from; i < _size; ++i) {
152  if (_keyList.at(i) == key) {
153  return i; // Return index of found key
154  }
155  }
156  return -1; // Return Not found
157 }
158 
159 
160 int XStringMap::indexOf( const QString& key, int from) const
161 {
162  return indexOf( key.toUtf8(), from);
163 }
164 
165 
166 int XStringMap::indexOfValue( const QByteArray& value, int from) const
167 {
168  for (int i = from; i < _size; ++i) {
169  if (_valList.at(i) == value) {
170  return i; // Return index of found value
171  }
172  }
173  return -1; // Return Not found
174 }
175 
176 
177 int XStringMap::indexOfValue( const QString& value, int from) const
178 {
179  return indexOfValue( value.toUtf8(), from);
180 }
181 
182 
183 int XStringMap::maxEnumOf( const char* keyPrefix) const
184 {
185  int maxEnum = -1;
186  int keyPrefixLen = int( strlen( keyPrefix));
187  bool isOk;
188  for (int i = 0; i < _size; ++i) {
189  if (_keyList.at(i).startsWith( keyPrefix)) {
190  int num = _keyList.at(i).mid( keyPrefixLen).toInt( &isOk);
191  if (isOk && (num > maxEnum))
192  maxEnum = num;
193  }
194  }
195  return maxEnum;
196 }
197 
198 
199 XStringMap& XStringMap::add( const char* key, const QByteArray& val)
200 {
201  if (key == arnNullptr) return *this; // Not valid key
202 
203  checkSpace();
204  _keyList[ _size].resize(0); // Avoid Heap reallocation
205  _valList[ _size].resize(0);
206  _keyList[ _size] += key;
207  _valList[ _size] += val;
208  ++_size;
209 
210  return *this;
211 }
212 
213 
214 XStringMap& XStringMap::add( const char* key, const char* val)
215 {
216  return add( key, QByteArray( val));
217 }
218 
219 
220 XStringMap& XStringMap::add( const char* keyPrefix, uint eNum, const QByteArray& val)
221 {
222  QByteArray key;
223  key += keyPrefix;
224  key += QByteArray::number( eNum);
225 
226  return add( key.constData(), val);
227 }
228 
229 
230 XStringMap& XStringMap::add( const QByteArray& key, const QByteArray& val)
231 {
232  return add( key.constData(), val);
233 }
234 
235 
236 XStringMap& XStringMap::add( const char* key, const QString& val)
237 {
238  return add( key, val.toUtf8());
239 }
240 
241 
242 XStringMap& XStringMap::add( const char* keyPrefix, uint eNum, const QString& val)
243 {
244  return add( keyPrefix, eNum, val.toUtf8());
245 }
246 
247 
248 XStringMap& XStringMap::add( const QByteArray& key, const QString& val)
249 {
250  return add( key, val.toUtf8());
251 }
252 
253 
254 XStringMap& XStringMap::add( const QString& key, const QString& val)
255 {
256  return add( key.toUtf8(), val.toUtf8());
257 }
258 
259 
261 {
262  for (int i = 0; i < other._size; ++i) {
263  add( other._keyList.at(i), other._valList.at(i));
264  }
265 
266  return *this;
267 }
268 
269 
270 XStringMap& XStringMap::add( const QVariantMap& variantMap)
271 {
272  QMapIterator<QString,QVariant> i( variantMap);
273 
274  while (i.hasNext()) {
275  i.next();
276  const QVariant& value = i.value();
277  QByteArray valBytes = value.type() == int(QMetaType::QByteArray) ? value.toByteArray()
278  : value.toString().toUtf8();
279  add( i.key().toUtf8(), valBytes);
280  }
281 
282  return *this;
283 }
284 
285 
286 XStringMap& XStringMap::addNum( const char* key, int val)
287 {
288  return add( key, QByteArray::number( val, 10));
289 }
290 
291 
292 XStringMap& XStringMap::addNum( const QByteArray& key, int val)
293 {
294  return add( key, QByteArray::number( val, 10));
295 }
296 
297 
298 XStringMap& XStringMap::addNum( const QString& key, int val)
299 {
300  return add( key, QString::number( val, 10));
301 }
302 
303 
304 XStringMap& XStringMap::addNum( const char* key, uint val)
305 {
306  return add( key, QByteArray::number( val, 10));
307 }
308 
309 
310 XStringMap& XStringMap::addNum( const QByteArray& key, uint val)
311 {
312  return add( key, QByteArray::number( val, 10));
313 }
314 
315 
316 XStringMap& XStringMap::addNum( const QString& key, uint val)
317 {
318  return add( key, QString::number( val, 10));
319 }
320 
321 
322 XStringMap& XStringMap::addNum( const char* key, double val, int precision)
323 {
324  int prec = precision >= 0 ? precision : std::numeric_limits<double>::digits10;
325  return add( key, QByteArray::number( val, 'g', prec));
326 }
327 
328 
329 XStringMap& XStringMap::addNum( const QByteArray& key, double val, int precision)
330 {
331  int prec = precision >= 0 ? precision : std::numeric_limits<double>::digits10;
332  return add( key, QByteArray::number( val, 'g', prec));
333 }
334 
335 
336 XStringMap& XStringMap::addNum(const QString& key, double val, int precision)
337 {
338  int prec = precision >= 0 ? precision : std::numeric_limits<double>::digits10;
339  return add( key, QString::number( val, 'g', prec));
340 }
341 
342 
343 XStringMap& XStringMap::addValues( const QStringList& stringList)
344 {
345  foreach (const QString& str, stringList) {
346  add("", str);
347  }
348 
349  return *this;
350 }
351 
352 
353 XStringMap& XStringMap::set( int i, const QByteArray& val)
354 {
355  if ((i < 0) || (i >= _size)) return *this; // Not valid index
356 
357  _valList[i].resize(0); // Avoid Heap reallocation
358  _valList[i] += val;
359 
360  return *this;
361 }
362 
363 
364 XStringMap& XStringMap::set( int i, const QString& val)
365 {
366  return set( i, val.toUtf8());
367 }
368 
369 
370 XStringMap& XStringMap::set( const char* key, const QByteArray& val)
371 {
372  int i = indexOf( key);
373  if (i < 0)
374  add( key, val);
375  else
376  set( i, val);
377 
378  return *this;
379 }
380 
381 
382 XStringMap& XStringMap::set( const char* key, const char* val)
383 {
384  return set( key, QByteArray( val));
385 }
386 
387 
388 XStringMap& XStringMap::set( const QByteArray& key, const QByteArray& val)
389 {
390  return set( key.constData(), val);
391 }
392 
393 
394 XStringMap& XStringMap::set( const char* key, const QString& val)
395 {
396  return set( key, val.toUtf8());
397 }
398 
399 
400 XStringMap& XStringMap::set( const QByteArray& key, const QString& val)
401 {
402  return set( key, val.toUtf8());
403 }
404 
405 
406 XStringMap& XStringMap::set( const QString& key, const QString& val)
407 {
408  return set( key.toUtf8(), val.toUtf8());
409 }
410 
411 
412 XStringMap& XStringMap::setKey( int i, const QByteArray& key)
413 {
414  if ((i < 0) || (i >= _size)) return *this; // Not valid index
415 
416  _keyList[i].resize(0); // Avoid Heap reallocation
417  _keyList[i] += key;
418 
419  return *this;
420 }
421 
422 
423 const QByteArray& XStringMap::keyRef( int i) const
424 {
425  if ((i < 0) || (i >= _size)) return _nullValue;
426 
427  return _keyList.at(i);
428 }
429 
430 
431 QByteArray XStringMap::key( int i, const char* def) const
432 {
433  if ((i < 0) || (i >= _size)) return def ? QByteArray( def) : QByteArray();
434 
435  return _keyList.at(i);
436 }
437 
438 
439 QByteArray XStringMap::key( const QByteArray& value, const char* def) const
440 {
441  int i = indexOfValue( value);
442  if (i < 0) return def ? QByteArray( def) : QByteArray();
443 
444  return _keyList.at(i);
445 }
446 
447 
448 QByteArray XStringMap::key( const QString& value, const char* def) const
449 {
450  return key( value.toUtf8(), def);
451 }
452 
453 
454 QString XStringMap::keyString( int i, const QString& def) const
455 {
456  if ((i < 0) || (i >= _size)) return def;
457 
458  const QByteArray& key = _keyList.at(i);
459  return QString::fromUtf8( key.constData(), key.size());
460 }
461 
462 
463 QString XStringMap::keyString( const QString& value, const QString& def) const
464 {
465  int i = indexOfValue( value);
466  return keyString( i, def);
467 }
468 
469 
470 const QByteArray& XStringMap::valueRef( int i) const
471 {
472  if ((i < 0) || (i >= _size)) return _nullValue;
473 
474  return _valList.at(i);
475 }
476 
477 
478 QByteArray XStringMap::value( int i, const char* def) const
479 {
480  if ((i < 0) || (i >= _size)) return def ? QByteArray( def) : QByteArray();
481 
482  return _valList.at(i);
483 }
484 
485 
486 QByteArray XStringMap::value( const char* key, const char* def) const
487 {
488  int i = indexOf( key);
489  if (i < 0) return def ? QByteArray( def) : QByteArray();
490 
491  return _valList.at(i);
492 }
493 
494 
495 QByteArray XStringMap::value( const char* keyPrefix, uint eNum, const char* def) const
496 {
497  if (!keyPrefix) {
498  keyPrefix = "";
499  }
500  QByteArray key;
501  key += keyPrefix;
502  key += QByteArray::number( eNum);
503 
504  return value( key, def);
505 }
506 
507 
508 QByteArray XStringMap::value( const QByteArray& key, const char* def) const
509 {
510  int i = indexOf( key);
511  if (i < 0) return def ? QByteArray( def) : QByteArray();
512 
513  return _valList.at(i);
514 }
515 
516 
517 QByteArray XStringMap::value( const QByteArray& key, const QByteArray& def) const
518 {
519  int i = indexOf( key);
520  if (i < 0) {
521  return def;
522  }
523  return _valList.at(i);
524 }
525 
526 
527 QString XStringMap::valueString( int i, const QString& def) const
528 {
529  if ((i < 0) || (i >= _size)) return def;
530 
531  const QByteArray& val = _valList.at(i);
532  return QString::fromUtf8( val.constData(), val.size());
533 }
534 
535 
536 QString XStringMap::valueString( const char* key, const QString& def) const
537 {
538  int i = indexOf( key);
539  return valueString( i, def);
540 }
541 
542 
543 QString XStringMap::valueString( const char* keyPrefix, uint eNum, const QString& def) const
544 {
545  if (!keyPrefix) keyPrefix = "";
546 
547  QByteArray key;
548  key += keyPrefix;
549  key += QByteArray::number( eNum);
550 
551  return valueString( key.constData(), def);
552 }
553 
554 
555 QString XStringMap::valueString( const QByteArray& key, const QString& def) const
556 {
557  int i = indexOf( key);
558  return valueString( i, def);
559 }
560 
561 
562 QString XStringMap::valueString( const QString& key, const QString& def) const
563 {
564  int i = indexOf( key);
565  return valueString( i, def);
566 }
567 
568 
570 {
571  if ((index < 0) || (index >= _size)) return *this;
572 
573  for (int i = index; i < _size - 1; ++i) {
574  _keyList[i].resize(0); // Avoid Heap reallocation
575  _valList[i].resize(0);
576  _keyList[i] += _keyList.at(i + 1);
577  _valList[i] += _valList.at(i + 1);
578  }
579  --_size;
580 
581  return *this;
582 }
583 
584 
585 XStringMap& XStringMap::remove( const char* key)
586 {
587  return remove( indexOf( key));
588 }
589 
590 
591 XStringMap& XStringMap::remove( const QByteArray& key)
592 {
593  return remove( indexOf( key));
594 }
595 
596 
597 XStringMap& XStringMap::remove( const QString& key)
598 {
599  return remove( indexOf( key.toUtf8()));
600 }
601 
602 
603 XStringMap& XStringMap::removeValue( const QByteArray& val)
604 {
605  return remove( indexOfValue( val));
606 }
607 
608 
609 XStringMap& XStringMap::removeValue( const QString& val)
610 {
611  return remove( indexOfValue( val.toUtf8()));
612 }
613 
614 
616 {
617  for (int i = 0; i < _size; ++i) {
618  if (_keyList.at(i).isEmpty()) {
619  _keyList[i].resize(0); // Avoid Heap reallocation
620  _keyList[i] += _valList.at(i);
621  }
622  }
623 }
624 
625 
627 {
628  if (_size <= 1) return;
629 
630  QByteArray key, val;
631  for (int i = 0; i < _size / 2; ++i) {
632  int ir = _size - i - 1;
633  key = _keyList.at( i);
634  val = _valList.at( i);
635  _keyList[ i].resize( 0);
636  _valList[ i].resize( 0);
637  _keyList[ i] += _keyList.at( ir);
638  _valList[ i] += _valList.at( ir);
639  _keyList[ ir].resize( 0);
640  _valList[ ir].resize( 0);
641  _keyList[ ir] += key;
642  _valList[ ir] += val;
643  }
644 }
645 
646 
647 QStringList XStringMap::keys() const
648 {
649  QStringList retList;
650  for (int i = 0; i < _size; ++i) {
651  const QByteArray& key = _keyList.at(i);
652  retList += QString::fromUtf8( key.constData(), key.size());
653  }
654  return retList;
655 }
656 
657 
658 QStringList XStringMap::values( const char* keyPrefix) const
659 {
660  if (!keyPrefix) keyPrefix = "";
661 
662  QStringList retList;
663  for (int i = 0; i < _size; ++i) {
664  if (*keyPrefix && !_keyList.at(i).startsWith( keyPrefix)) continue;
665 
666  const QByteArray& value = _valList.at(i);
667  retList += QString::fromUtf8( value.constData(), value.size());
668  }
669  return retList;
670 }
671 
672 
673 MQVariantMap XStringMap::toVariantMap( bool useStringVal) const
674 {
675  MQVariantMap retMap;
676 
677  for (int i = 0; i < _size; ++i) {
678  const QByteArray& key = _keyList.at(i);
679  const QByteArray& value = _valList.at(i);
680  retMap.insert( QString::fromUtf8( key.constData(), key.size()),
681  useStringVal ? QVariant( QString::fromUtf8( value))
682  : QVariant( value));
683  }
684  return retMap;
685 }
686 
687 
688 QByteArray XStringMap::toXString() const
689 {
690  bool optFrame = _options.is( Options::Frame);
691  bool optAnyKey = _options.is( Options::AnyKey);
692  QByteArray outXString;
693  QByteArray txtCoded;
694  for (int i = 0; i < _size; ++i) {
695  if (i > 0)
696  outXString += ' ';
697  const QByteArray& key = _keyList.at(i);
698  const QByteArray& val = _valList.at(i);
699  bool useOrgKey = true;
700  if (optAnyKey) {
701  stringCode( txtCoded, key);
702  if (_hasChgCode) {
703  outXString += "^:";
704  outXString += txtCoded;
705  useOrgKey = false;
706  }
707  }
708  if (useOrgKey) {
709  outXString += key;
710  }
711  stringCode( txtCoded, val);
712  if (optFrame && !_hasBinCode) {
713  QByteArray valLenTxt = QByteArray::number( val.size());
714  bool useFrame = txtCoded.size() > val.size() + valLenTxt.size() + 4;
715  if (useFrame) {
716  outXString += "|=";
717  outXString += valLenTxt;
718  outXString += '<';
719  outXString += val;
720  outXString += '>';
721  continue;
722  }
723  }
724  if (!key.isEmpty() || val.isEmpty() || _hasEqChar) {
725  outXString += '=';
726  }
727  outXString += txtCoded;
728  }
729  return outXString;
730 }
731 
732 
734 {
735  QByteArray xstr = toXString();
736  return QString::fromUtf8( xstr.constData(), xstr.size());
737 }
738 
739 
740 bool XStringMap::fromXString( const QByteArray& inXString, int size)
741 {
742  if ((size < 0) || (size > inXString.size())) {
743  size = inXString.size();
744  }
745  clear();
746  if (size == 0) return true; // Nothing to load
747 
748  int startPos = 0;
749  QByteArray key;
750  QByteArray val;
751  QByteArray txtCoded;
752 
753  forever {
754  bool isFrame = false;
755  if (startPos >= size) { // if past end of line, finished
756  break;
757  }
758  while ( inXString.at( startPos) == ' ') { // Skip leading space before key (only possible by manual coding)
759  ++startPos;
760  }
761 
762  // Start getting key
763  key.resize(0); // Default empty key
764  int posEq = inXString.indexOf( '=', startPos);
765  int posSep = inXString.indexOf( ' ', startPos);
766  if (posSep < 0 || posSep >= size) {
767  posSep = size; // last separator pos is set to end of string
768  }
769  if ((posEq >= 0) && (posEq < posSep)) { // Key found
770  int keyOrgStart = startPos;
771  int keyOrgLen = 0;
772  if ((posEq > startPos) && (inXString.at( posEq - 1) == '|')) { // Framed value
773  isFrame = true;
774  int posFrSt = inXString.indexOf( '<', posEq + 1);
775  int frameLen = -1;
776  bool frameLenOk = false;
777  if ((posFrSt > posEq) && (posFrSt < size - 1)) {
778  frameLen = inXString.mid( posEq + 1, posFrSt - posEq - 1).toInt( &frameLenOk);
779  }
780  int posFrEn = posFrSt + frameLen + 1;
781  if (frameLenOk && (posFrEn < size) && (inXString.at( posFrEn) == '>')) {
782  keyOrgLen = posEq - startPos - 1;
783  startPos = posFrSt + 1; // past '<'
784  posSep = posFrEn + 1; // past '>'
785  }
786  else return false; // Can't recover due to broken frame
787  }
788  else {
789  keyOrgLen = posEq - startPos;
790  startPos = posEq + 1; // past '='
791  }
792  if ((keyOrgLen >= 2) && (inXString.at( keyOrgStart) == '^') && (inXString.at( keyOrgStart + 1) == ':')) { // Coded key
793  txtCoded.resize(0);
794  txtCoded.append( inXString.constData() + keyOrgStart + 2, keyOrgLen - 2);
795  stringDecode( key, txtCoded);
796  }
797  else {
798  key.append( inXString.constData() + keyOrgStart, keyOrgLen);
799  }
800  }
801 
802  // Start getting value
803  if (isFrame) {
804  val.resize(0);
805  val.append( inXString.constData() + startPos, posSep - startPos - 1);
806  }
807  else {
808  txtCoded.resize(0);
809  txtCoded.append( inXString.constData() + startPos, posSep - startPos);
810  stringDecode( val, txtCoded);
811  }
812  startPos = posSep + 1; // past separator ' '
813 
814  add( key, val);
815  }
816  return true;
817 }
818 
819 
820 bool XStringMap::fromXString( const QString& inXString)
821 {
822  return fromXString( inXString.toUtf8());
823 }
824 
825 
826 void XStringMap::stringCode( QByteArray& dst, const QByteArray& src) const
827 {
828  bool optRepeatLen = _options.is( Options::RepeatLen);
829  bool optNullTilde = _options.is( Options::NullTilde);
830  bool optAnyKey = _options.is( Options::AnyKey);
831  _hasBinCode = false;
832  _hasChgCode = false;
833  _hasEqChar = false;
834 
835  int srcSize = src.size();
836  const char* srcP = src.constData();
837 
838  dst.resize( 2 * srcSize); // Max size of coded string
839  char* dstStart = dst.data();
840  char* dstP = dstStart;
841  char* dstP0 = dstStart;
842  const char* srcRepLim = arnNullptr;
843 
844  bool actNullTilde = false;
845  qint16 lastChar = -1;
846  uchar lastCharInc = 1;
847  uchar sameCount = 0;
848  uchar srcChar;
849  for (int i = 0; i < srcSize; ++i) {
850  srcChar = uchar( *srcP++);
851  if (optRepeatLen && (srcP >= srcRepLim)) { // Optimize repeated chars
852  if (srcChar == lastChar) {
853  ++sameCount;
854  if ((sameCount < 9) && (i < srcSize - 1)) continue;
855  if (sameCount * lastCharInc > 2 ) {
856  *dstP++ = '\\';
857  *dstP++ = '0' + sameCount;
858  _hasChgCode = true;
859  sameCount = 0;
860  continue;
861  }
862  else { // Last in source, 1 or 2 same
863  srcP -= sameCount - 1;
864  i -= sameCount - 1;
865  }
866  sameCount = 0;
867  }
868  else if (sameCount >= 1) {
869  if (sameCount * lastCharInc > 2 ) {
870  *dstP++ = '\\';
871  *dstP++ = '0' + sameCount;
872  _hasChgCode = true;
873  }
874  else { // Only 1 repeat, rewind last loop and redo
875  srcChar = lastChar;
876  lastChar = -1;
877  srcRepLim = srcP;
878  srcP -= sameCount;
879  i -= sameCount;
880  }
881  sameCount = 0;
882  }
883  }
884  dstP0 = dstP;
885 
886  switch (srcChar) {
887  case ' ':
888  *dstP++ = '_'; // The coded string must not contain any ' '
889  _hasChgCode = true;
890  break;
891  case '_':
892  *dstP++ = '\\';
893  *dstP++ = '_';
894  _hasChgCode = true;
895  break;
896  case '\\':
897  *dstP++ = '\\';
898  *dstP++ = '\\';
899  _hasChgCode = true;
900  break;
901  case '^':
902  *dstP++ = '\\';
903  *dstP++ = '^';
904  _hasChgCode = true;
905  break;
906  case '~':
907  if (actNullTilde) {
908  *dstP++ = '\\';
909  *dstP++ = '~';
910  _hasChgCode = true;
911  }
912  else {
913  *dstP++ = '~';
914  }
915  break;
916  case '=':
917  if (optAnyKey) {
918  *dstP++ = '\\';
919  *dstP++ = ':';
920  _hasChgCode = true;
921  }
922  else {
923  *dstP++ = '=';
924  }
925  _hasEqChar = true;
926  break;
927  case '\n':
928  *dstP++ = '\\';
929  *dstP++ = 'n';
930  _hasBinCode = true;
931  break;
932  case '\r':
933  *dstP++ = '\\';
934  *dstP++ = 'r';
935  _hasBinCode = true;
936  break;
937  case '\0':
938  if (actNullTilde) {
939  *dstP++ = '~'; // Null is a common value, substitute with single char
940  }
941  else if (optNullTilde) {
942  *dstP++ = '^'; // Start the tilde substitution
943  *dstP++ = '~';
944  actNullTilde = true;
945  }
946  else {
947  *dstP++ = '\\';
948  *dstP++ = '0';
949  }
950  _hasBinCode = true;
951  break;
952  default:
953  if (srcChar < 32) { // 0 .. 31 Special control-char
954  *dstP++ = '^';
955  *dstP++ = char('A' + srcChar - 1);
956  _hasBinCode = true;
957  }
958  else { // Normal char (also UTF8 which is above 127)
959  *dstP++ = char(srcChar);
960  }
961  }
962  lastChar = srcChar;
963  lastCharInc = dstP - dstP0;
964  }
965  dst.resize( int( dstP - dstStart)); // Set the real used size for coded string
966  _hasChgCode |= _hasBinCode;
967 }
968 
969 
970 void XStringMap::stringDecode( QByteArray& dst, const QByteArray& src) const
971 {
972  int srcSize = src.size();
973  const char* srcP = src.constData();
974 
975  dst.resize( srcSize * 5); // Max size of decoded string. Worst for repeated chars like "A\9\9"
976  char* dstStart = dst.data();
977  char* dstP = dstStart;
978 
979  bool actNullTilde = false;
980  bool escapeFlag = false;
981  bool ctrlFlag = false;
982  qint16 lastChar = -1;
983  qint16 dstChar;
984  uchar repeatCount;
985  uchar srcChar;
986  for (int i = 0; i < srcSize; ++i) {
987  dstChar = -1;
988  repeatCount = 1;
989  srcChar = uchar( *srcP++);
990  if (escapeFlag) {
991  escapeFlag = false;
992  switch (srcChar) {
993  case 'n':
994  dstChar = '\n';
995  break;
996  case 'r':
997  dstChar = '\r';
998  break;
999  case '0':
1000  dstChar = '\0';
1001  break;
1002  case ':':
1003  dstChar = '=';
1004  break;
1005  default:
1006  if ((srcChar >= '1') && (srcChar <= '9')) {
1007  dstChar = lastChar;
1008  repeatCount = srcChar - '0';
1009  }
1010  else {
1011  dstChar = srcChar;
1012  }
1013  }
1014  }
1015  else if (ctrlFlag) {
1016  ctrlFlag = false;
1017  switch (srcChar) {
1018  case '~':
1019  dstChar = '\0';
1020  actNullTilde = true;
1021  break;
1022  default:
1023  if ((srcChar >= 'A') && (srcChar <= 'A' + 30)) {
1024  dstChar = uchar( srcChar - 'A' + 1); // 1 .. 31 Special control-char
1025  }
1026  }
1027  }
1028  else {
1029  switch (srcChar) {
1030  case '_':
1031  dstChar = ' ';
1032  break;
1033  case '~':
1034  if (actNullTilde) {
1035  dstChar = '\0';
1036  }
1037  else {
1038  dstChar = '~';
1039  }
1040  break;
1041  case '\\':
1042  escapeFlag = true;
1043  break;
1044  case '^':
1045  ctrlFlag = true;
1046  break;
1047  default: // Normal char (also UTF8 which is above 127)
1048  dstChar = srcChar;
1049  }
1050  }
1051  if (dstChar >= 0) {
1052  for (uchar i = 0; i < repeatCount; ++i) {
1053  *dstP++ = uchar( dstChar);
1054  }
1055  lastChar = dstChar;
1056  }
1057  }
1058  dst.resize( int( dstP - dstStart)); // Set the real used size for decoded string
1059 }
1060 
1061 
1062 XStringMap& XStringMap::operator+=( const QVariantMap& other)
1063 {
1064  return add( other);
1065 }
1066 
1067 
1069 {
1070  return add( other);
1071 }
1072 
1073 
1074 QByteArray XStringMap::info()
1075 {
1076  return "XStringMap ver=" ARNXSTRINGMAP_VER;
1077 }
1078 
1079 
1080 void XStringMap::checkSpace()
1081 {
1082  if (_size >= _keyList.size()) { // If out of space allocate more
1083  int newCapacity = (_size > 0) ? (2 * _size) : 8;
1084  _keyList.reserve( newCapacity); // Force exact min amount space beeing guaranted, i.e. no extra margin
1085  _valList.reserve( newCapacity);
1086  _keyList.resize( newCapacity);
1087  _valList.resize( newCapacity);
1088  }
1089 }
1090 
1091 } // Arn::
void setEmptyKeysToValue()
int maxEnumOf(const char *keyPrefix) const
int indexOf(const char *key, int from=0) const
#define ARNXSTRINGMAP_VER
Definition: XStringMap.hpp:52
bool fromXString(const QByteArray &inXString, int size=-1)
QStringList values(const char *keyPrefix=arnNullptr) const
void stringCode(QByteArray &dst, const QByteArray &src) const
QMultiMap< QString, QVariant > MQVariantMap
Definition: XStringMap.hpp:54
Container class with string representation for serialized data.
Definition: XStringMap.hpp:107
XStringMap & set(int i, const QByteArray &val)
QString keyString(int i, const QString &def=QString()) const
XStringMap & addValues(const QStringList &stringList)
const QByteArray & keyRef(int i) const
XStringMap & setKey(int i, const QByteArray &key)
void stringDecode(QByteArray &dst, const QByteArray &src) const
void setOptions(const Options &newOptions)
const Options & options() const
QByteArray value(int i, const char *def=arnNullptr) const
const QByteArray & valueRef(int i) const
void clear(bool freeMem=false)
MQVariantMap toVariantMap(bool useStringVal) const
XStringMap & addNum(const char *key, int val)
QByteArray info()
QByteArray toXString() const
XStringMap & operator+=(const XStringMap &other)
Definition: Arn.cpp:43
XStringMap & operator=(const XStringMap &other)
Make shallow copy (Qt style)
QStringList keys() const
XStringMap & removeValue(const QByteArray &val)
QString valueString(int i, const QString &def=QString()) const
QByteArray key(int i, const char *def=arnNullptr) const
int indexOfValue(const QByteArray &value, int from=0) const
XStringMap & remove(int index)
QString toXStringString() const
XStringMap & add(const char *key, const QByteArray &val)
int size() const
Definition: XStringMap.hpp:121