Main Page · Modules · All Classes · Class Hierarchy
MCBinaryData.cpp
1 /*
2  * This file is part of the AiBO+ project
3  *
4  * Copyright (C) 2005-2016 Csaba Kertész (csaba.kertesz@gmail.com)
5  *
6  * AiBO+ is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * AiBO+ is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
19  *
20  */
21 
22 #include "MCBinaryData.hpp"
23 
24 #include "3rdparty/archives/portable_iarchive.hpp"
25 #include "3rdparty/archives/portable_oarchive.hpp"
26 #include "MCDefs.hpp"
27 #include "MCLog.hpp"
28 
29 #ifndef __AIBO_BUILD__
30 #include <qdatastream.h>
31 #include <qfile.h>
32 #include <qresource.h>
33 #endif
34 
35 #include <boost/archive/binary_oarchive.hpp>
36 #include <boost/archive/binary_iarchive.hpp>
37 #include <boost/archive/iterators/base64_from_binary.hpp>
38 #include <boost/archive/iterators/binary_from_base64.hpp>
39 #if __cplusplus >= 201103L || defined(__MINGW32__) || defined(__MINGW64__)
40 #pragma GCC diagnostic push
41 #pragma GCC diagnostic ignored "-Wreorder"
42 #if !defined(__clang__)
43 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
44 #endif
45 #endif
46 #include <boost/archive/iterators/transform_width.hpp>
47 #if __cplusplus >= 201103L || defined(__MINGW32__) || defined(__MINGW64__)
48 #pragma GCC diagnostic pop
49 #endif
50 #include <boost/serialization/vector.hpp>
51 
52 #include <md5.h>
53 
54 #include <stdlib.h>
55 #include <string.h>
56 
57 #include <fstream>
58 
59 namespace
60 {
61 using namespace boost::archive::iterators;
62 
63 // Taken from:
64 // http://stackoverflow.com/questions/7053538/how-do-i-encode-a-string-to-base64-using-only-boost
75 uint base64_encode(unsigned char* dest, const unsigned char* src, uint len)
76 {
77  typedef base64_from_binary<transform_width<const char *, 6, 8> > base64_enc;
78 
79  uint one_third_len = len/3;
80  uint len_rounded_down = one_third_len*3;
81  uint j = len_rounded_down + one_third_len;
82 
83  std::copy(base64_enc(src), base64_enc(src + len_rounded_down), dest);
84 
85  if (len_rounded_down != len)
86  {
87  char tail[3] = {0, 0, 0};
88  uint i = 0;
89 
90  for (; i < len - len_rounded_down; ++i)
91  {
92  tail[i] = src[len_rounded_down+i];
93  }
94  std::copy(base64_enc(tail), base64_enc(tail + 3), dest + j);
95 
96  for (i = len + one_third_len + 1; i < j+4; ++i)
97  {
98  dest[i] = '=';
99  }
100  return i;
101  }
102  return j;
103 }
104 
114 const char* base64_decode(unsigned char* dest, const char* src, uint* len)
115 {
116  uint output_len = *len;
117  typedef transform_width<binary_from_base64<const char*>, 8, 6> base64_dec;
118 
119  uint i = 0;
120 
121  MC_TRY_BEGIN
122  base64_dec src_it(src);
123  for(; i < output_len; ++i)
124  {
125  *dest++ = *src_it;
126  ++src_it;
127  }
128  MC_CATCH_BEGIN
129  MC_CATCH_END
130 
131  // This is a joke...but real bug in boost.
132  // Remove the padding characters, cf. https://svn.boost.org/trac/boost/ticket/5629
133  if (src[(int)*len-1] == '=')
134  {
135  --i;
136  if (src[(int)*len-2] == '=')
137  --i;
138  }
139  *len = i;
140  return src + (i+2)/3*4; // bytes in = bytes out / 3 rounded up * 4
141 }
142 }
143 
144 MCBinaryData::MCBinaryData() : Size(0), Position(0)
145 {
146 }
147 
148 
150 {
151  Allocate(size);
152 }
153 
154 
155 MCBinaryData::MCBinaryData(int size, unsigned char value) : Size(0), Position(0)
156 {
157  Allocate(size);
158  memset(&Data[0], (int)value, (size_t)size);
159 }
160 
161 
163 {
164  Clone(other);
165 }
166 
167 
169 {
170  *this = std::move(other);
171  // Free the resources of other instance
172  other.Free();
173 }
174 
175 
176 MCBinaryData::~MCBinaryData()
177 {
178 }
179 
180 
182 {
183  // Deallocate the content
184  Free();
185  if (size <= 0)
186  return;
187 
188  if (Size == size)
189  {
190  // Only clear the memory and position without reallocation
191  Position = 0;
192  Clear();
193  return;
194  }
195  // Allocate new data
196  Data.resize(size);
197  Size = size;
198  Position = 0;
199  // Clear the memory
200  Clear();
201 }
202 
203 
204 void MCBinaryData::Shrink(int size)
205 {
206  if (size == 0)
207  {
208  Free();
209  return;
210  }
211  if (size < 0 || size >= Size)
212  return;
213 
214  Data.resize(size);
215  Size = size;
216  Position = 0;
217 }
218 
219 
221 {
222  // Free the data if it is owned
223  // http://stackoverflow.com/questions/1111078/reduce-the-capacity-of-an-stl-vector
224  std::vector<unsigned char>(Data).swap(Data);
225  Size = 0;
226  Position = 0;
227 }
228 
229 
231 {
232  // Use the value 255 since 0 can be interpreted into valid content
233  if (Size > 0)
234  memset(&Data[0], 255, Size);
235  Position = 0;
236 }
237 
238 
240 {
241  for (int i = 0; i < Size; ++i)
242  {
243  if (Data[i] != 255)
244  return false;
245  }
246  return true;
247 }
248 
249 
250 void MCBinaryData::Set(unsigned char* data, int size)
251 {
252  if (Size != size)
253  {
254  Free();
255  Allocate(size);
256  }
257  memcpy(&Data[0], data, size);
258  Size = size;
259  Position = 0;
260 }
261 
262 
264 {
265  Free();
266  Allocate(other.Size);
267  Data = other.Data;
268  Position = other.Position;
269 }
270 
271 
273 {
274  MCBinaryData* NewInstance = new MCBinaryData(Size);
275 
276  memcpy(&NewInstance->Data[0], &Data[0], Size);
277  NewInstance->Position = Position;
278  return NewInstance;
279 }
280 
281 
282 bool MCBinaryData::WriteData(MCBinaryData& target, int capacity)
283 {
284  int Count = CopyData(target, capacity);
285 
286  if (Count > 0)
287  {
288  Position += Count;
289  return true;
290  }
291  return false;
292 }
293 
294 
295 unsigned int MCBinaryData::CopyData(MCBinaryData& target, int capacity) const
296 {
297  int Capacity = capacity < 0 ? target.Size : capacity;
298 
299  if (Capacity > Size-Position)
300  {
301  MC_WARNING("%d source bytes are needed, but only %d available to write binary data.",
302  Capacity, Size-Position);
303  return 0;
304  }
305  if (Capacity > target.Size-target.Position)
306  {
307  MC_WARNING("%d target bytes are needed, but only %d available to write binary data.",
308  Capacity, target.Size-target.Position);
309  return 0;
310  }
311  memcpy(&target.Data[target.Position], &Data[Position], Capacity);
312  target.IncrementPosition(Capacity);
313  return Capacity;
314 }
315 
316 
317 bool MCBinaryData::LoadFromFile(const std::string& file_name)
318 {
319  std::ifstream InputFile(file_name.c_str(), std::ios::in | std::ios::binary);
320 
321  if (!InputFile.is_open())
322  {
323  MC_WARNING("File does not exist to load binary data: %s", file_name.c_str());
324  return false;
325  }
326  InputFile.seekg(0, std::ios::end);
327  Allocate((int)InputFile.tellg());
328  InputFile.seekg(0, std::ios::beg);
329  InputFile.read(reinterpret_cast<char*>(&Data[0]), Data.size());
330  return InputFile.good();
331 }
332 
333 
334 #ifndef __AIBO_BUILD__
335 bool MCBinaryData::LoadFromQtResource(const QString& resource_str)
336 {
337  QResource XmlResource(resource_str);
338  QFile ClassifierFile(XmlResource.absoluteFilePath());
339  QDataStream InputStream(&ClassifierFile);
340 
341  Allocate(ClassifierFile.size());
342  if (!ClassifierFile.open(QIODevice::ReadOnly))
343  return false;
344  if (InputStream.readRawData((char*)GetData(), ClassifierFile.size()) < 0)
345  {
346  ClassifierFile.close();
347  return false;
348  }
349  ClassifierFile.close();
350  return true;
351 }
352 #endif
353 
354 
355 bool MCBinaryData::SaveToFile(const std::string& file_name) const
356 {
357  std::ofstream OutputFile(file_name.c_str(), std::ios::out | std::ios::binary);
358 
359  if (!OutputFile.is_open())
360  {
361  MC_WARNING("Unable to create file to save binary data: %s", file_name.c_str());
362  return false;
363  }
364  OutputFile.write(reinterpret_cast<const char*>(&Data[0]), Data.size());
365  OutputFile.flush();
366  return OutputFile.good();
367 }
368 
369 
371 {
372  MCBinaryData Result(16);
373 
374  dlib::md5(GetData(), Size, Result.GetData());
375  return Result;
376 }
377 
378 
379 void MCBinaryData::AddUChar(unsigned char new_char)
380 {
381  if (Position == Size)
382  {
383  MC_WARNING("Can't encode unsigned char number since no space left.");
384  return;
385  }
386  Data[Position] = new_char;
387  Position++;
388 }
389 
390 
391 void MCBinaryData::AddInt16(int16_t new_int, bool reverse_order)
392 {
393  if (Position+2 > Size)
394  {
395  MC_WARNING("Can't encode int16_t number since no space left.");
396  return;
397  }
398  uint16_t TempInt = *reinterpret_cast<uint16_t*>(&new_int);
399 
400  if (reverse_order)
401  {
402  Data[Position+1] = (unsigned char)(TempInt / 256);
403  Data[Position] = (unsigned char)(TempInt % 256);
404  } else {
405  Data[Position] = (unsigned char)(TempInt / 256);
406  Data[Position+1] = (unsigned char)(TempInt % 256);
407  }
408  Position += 2;
409 }
410 
411 
412 void MCBinaryData::AddInt32(int32_t new_int, bool reverse_order)
413 {
414  if (Position+4 > Size)
415  {
416  MC_WARNING("Can't encode int32_t number since no space left.");
417  return;
418  }
419  uint32_t TempInt = *reinterpret_cast<uint32_t*>(&new_int);
420 
421  if (reverse_order)
422  {
423  Data[Position+3] = TempInt >> 24 & 0xff;
424  Data[Position+2] = TempInt >> 16 & 0xff;
425  Data[Position+1] = TempInt >> 8 & 0xff;
426  Data[Position+0] = TempInt & 0xff;
427  } else {
428  Data[Position] = TempInt >> 24 & 0xff;
429  Data[Position+1] = TempInt >> 16 & 0xff;
430  Data[Position+2] = TempInt >> 8 & 0xff;
431  Data[Position+3] = TempInt & 0xff;
432  }
433  Position += 4;
434 }
435 
436 
437 void MCBinaryData::AddString(const std::string& str)
438 {
439  if (Position+(int)str.size() > Size)
440  {
441  MC_WARNING("Can't encode string since no space left.");
442  return;
443  }
444  memcpy(&Data[Position], str.c_str(), str.size());
445  Position += str.size();
446 }
447 
448 
449 unsigned char MCBinaryData::GetUChar()
450 {
451  if (Position == Size)
452  {
453  MC_WARNING("Can't decode unsigned char since no space left.");
454  return 0;
455  }
456  unsigned char Ret = Data[Position];
457 
458  Position++;
459  return Ret;
460 }
461 
462 
463 int16_t MCBinaryData::GetInt16(bool reverse_order)
464 {
465  if (Position+2 > Size)
466  {
467  MC_WARNING("Can't decode int16_t number since no space left.");
468  return 0;
469  }
470  uint16_t Ret = 0;
471 
472  if (reverse_order)
473  {
474  Ret = Data[Position+1]*256+(uint16_t)Data[Position];
475  } else {
476  Ret = Data[Position]*256+(uint16_t)Data[Position+1];
477  }
478  Position += 2;
479  return *reinterpret_cast<int16_t*>(&Ret);
480 }
481 
482 
483 int32_t MCBinaryData::GetInt32(bool reverse_order)
484 {
485  if (Position+4 > Size)
486  {
487  MC_WARNING("Can't decode int32_t number since no space left.");
488  return 0;
489  }
490  uint32_t Ret = 0;
491 
492  if (reverse_order)
493  {
494  Ret = (Data[Position+3] << 24)+(Data[Position+2] << 16)+(Data[Position+1] << 8)+Data[Position];
495  } else {
496  Ret = (Data[Position] << 24)+(Data[Position+1] << 16)+(Data[Position+2] << 8)+Data[Position+3];
497  }
498  Position += 4;
499  return *reinterpret_cast<int32_t*>(&Ret);
500 }
501 
502 
503 bool MCBinaryData::ValidateString(unsigned int length, const std::string& additional_chars)
504 {
505  if (Position+(int)length > Size)
506  {
507  MC_WARNING("Too long string is expected to decode (%d > %d)", length, Size-Position);
508  return false;
509  }
510  for (unsigned int i = Position; i < Position+length; ++i)
511  {
512  bool Found = false;
513 
514  for (unsigned int i1 = 0; i1 < additional_chars.size(); ++i1)
515  {
516  if (Data[i] == additional_chars[i1])
517  {
518  Found = true;
519  break;
520  }
521  }
522  if (Found)
523  continue;
524  if (!(Data[i] >= 'a' && Data[i] <= 'z') && !(Data[i] >= 'A' && Data[i] <= 'Z') &&
525  !(Data[i] >= '0' && Data[i] <= '9') && Data[i] != ' ')
526  {
527  return false;
528  }
529  }
530  return true;
531 }
532 
533 
534 std::string MCBinaryData::GetString(unsigned int length, const std::string& additional_chars)
535 {
536  if (!ValidateString(length, additional_chars))
537  {
538  return "";
539  }
540  std::string Result(length, 0);
541 
542  memcpy((char*)Result.c_str(), &Data[Position], length);
543  IncrementPosition(length);
544  return Result;
545 }
546 
547 
548 unsigned char* MCBinaryData::GetData() const
549 {
550  return (unsigned char*)&Data[0];
551 }
552 
553 
555 {
556  return Size;
557 }
558 
559 
560 const std::vector<unsigned char>& MCBinaryData::GetVectorData() const
561 {
562  return Data;
563 }
564 
565 
567 {
568  return Position;
569 }
570 
571 
572 unsigned int MCBinaryData::GetRemainingCapacity(unsigned int capacity) const
573 {
574  return (unsigned int)((Position+(int)capacity <= Size) ? capacity : Size-Position);
575 }
576 
577 
579 {
580  return Position == Size;
581 }
582 
583 
584 void MCBinaryData::SetPosition(unsigned int position)
585 {
586  Position = MCMin((int)position, Size);
587 }
588 
589 
590 void MCBinaryData::IncrementPosition(unsigned int position)
591 {
592  SetPosition(Position+position);
593 }
594 
595 
597 {
598  Position = 0;
599 }
600 
601 
602 std::string MCBinaryData::ToBase64() const
603 {
604  if (Size <= 0)
605  return "";
606 
607  MC::BinaryDataSPtr BinaryData(new MCBinaryData((int)((float)Size*1.4)));
608  uint Result = base64_encode(BinaryData->GetData(), &Data[0], Size);
609 
610  return std::string((char*)BinaryData->GetData(), (int)Result);
611 }
612 
613 
614 void MCBinaryData::FromBase64(const std::string& base64_str)
615 {
616  if (base64_str.empty())
617  return;
618 
619  MC::BinaryDataSPtr BinaryData(new MCBinaryData(base64_str.size()));
620  uint Result = base64_str.size();
621 
622  base64_decode(BinaryData->GetData(), base64_str.c_str(), &Result);
623  Set(BinaryData->GetData(), (int)Result);
624 }
625 
626 
628 {
629  if (this == &other)
630  return *this;
631 
632  if (other.Size <= 0)
633  {
634  Free();
635  return *this;
636  }
637  Position = other.Position;
638  Size = other.Size;
639  Data = std::move(other.Data);
640  // Free the resources of other instance
641  other.Free();
642  return *this;
643 }
644 
645 
647 {
648  if (*this == other)
649  return *this;
650 
651  Clone(other);
652  return *this;
653 }
654 
655 
656 bool MCBinaryData::operator==(const MCBinaryData& other) const
657 {
658  bool Ret = true;
659 
660  // At least the size must be the same
661  Ret = Ret && (Size == other.Size);
662  for (int i = 0; i < Size && Ret; ++i)
663  {
664  Ret = Ret && (Data[i] == other.Data[i]);
665  }
666  return Ret;
667 }
668 
669 
670 bool MCBinaryData::operator!=(const MCBinaryData& other) const
671 {
672  return !(*this == other);
673 }
674 
675 
676 template<class Archive>
677 void MCBinaryData::serialize(Archive& archive, const unsigned int version)
678 {
679  MC_UNUSED(version);
680 
681  archive & Data;
682  archive & Size;
683  archive & Position;
684 }
685 
686 
687 // Explicit template instantiation
688 // http://stackoverflow.com/questions/2152002/how-do-i-force-a-particular-instance-of-a-c-template-to-instantiate
689 // Post by Alexander Poluektov
690 template void MCBinaryData::serialize<boost::archive::binary_iarchive>(boost::archive::binary_iarchive&,
691  const unsigned int);
692 template void MCBinaryData::serialize<boost::archive::binary_oarchive>(boost::archive::binary_oarchive&,
693  const unsigned int);
694 template void MCBinaryData::serialize<eos::portable_iarchive>(eos::portable_iarchive&, const unsigned int);
695 template void MCBinaryData::serialize<eos::portable_oarchive>(eos::portable_oarchive&, const unsigned int);
int16_t GetInt16(bool reverse_order=false)
Get a 16 bit integer from the current position.
bool ValidateString(unsigned int length, const std::string &additional_chars="")
Check if a string can be read from the current position.
std::vector< unsigned char > Data
Pointer to the encapsulated data.
bool IsEmpty() const
Check if the binary data is not filled with invalid data.
bool operator!=(const MCBinaryData &other) const
Inequality (!=) operator.
void AddInt16(int16_t new_int, bool reverse_order=false)
Add a 16 bit integer at the current position.
Binary data class.
std::string ToBase64() const
Convert the content to base64.
const T MCMin(const U &container)
Get the minimal value of a container.
MCBinaryData GenerateMd5Hash() const
Generate md5 hash.
MCBinaryData & operator=(MCBinaryData &&other)
Move assignment operator.
void AddString(const std::string &str)
Add a string at the current position.
bool SaveToFile(const std::string &file_name) const
Save data to a file.
MCBinaryData()
Class constructor.
unsigned char GetUChar()
Get an unsigned char from the current position.
bool LoadFromQtResource(const QString &resource_str)
Load data from a Qt resource.
void ResetPosition()
Reset the cursor position back to the start position.
#define MC_WARNING(...)
Warning macro.
Definition: MCLog.hpp:43
std::string GetString(unsigned int length, const std::string &additional_chars="")
Get a string from the current position.
void FromBase64(const std::string &base64_str)
Convert back from base64.
void Allocate(int size)
Allocate a certain data size.
void AddInt32(int32_t new_int, bool reverse_order=false)
Add a 32 bit integer at the current position.
unsigned int GetRemainingCapacity(unsigned int capacity) const
Check if the binary data has enough remaining capacity.
bool WriteData(MCBinaryData &target, int capacity=-1)
Write data into a target instance.
void SetPosition(unsigned int position)
Set the cursor position.
int Size
Size of the data.
unsigned int CopyData(MCBinaryData &target, int capacity=-1) const
Copy data into a target instance.
#define MC_UNUSED(a)
Helper macro to avoid compiler warning about unused function parameters.
Definition: MCDefs.hpp:601
void Free()
Free the allocated data.
void IncrementPosition(unsigned int position=1)
Increment the cursor position.
void Shrink(int size)
Shrink to a certain data size.
void Set(unsigned char *data, int size)
Set binary data content.
void AddUChar(unsigned char new_char)
Add an unsigned char at the current position.
unsigned char * GetData() const
Get direct access to the binary data.
int32_t GetInt32(bool reverse_order=false)
Get a 32 bit integer from the current position.
bool LoadFromFile(const std::string &file_name)
Load data from a file.
MCBinaryData * Clone()
Clone the binary data.
bool operator==(const MCBinaryData &other) const
Equality (==) operator.
void Clear()
Clear the allocated data.
const std::vector< unsigned char > & GetVectorData() const
Get binary data in vector form.
int GetPosition() const
Get the current position in the binary data.
bool IsPositionAtEnd() const
Check if the current position is at the end of the binary data.
int Position
Internal position index.
int GetSize() const
Get binary data size.