Spinning Topp Logo BlackTopp Studios
inc
binarybuffer.cpp
Go to the documentation of this file.
1 // © Copyright 2010 - 2016 BlackTopp Studios Inc.
2 /* This file is part of The Mezzanine Engine.
3 
4  The Mezzanine Engine is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  The Mezzanine Engine is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with The Mezzanine Engine. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /* The original authors have included a copy of the license specified above in the
18  'Docs' folder. See 'gpl.txt'
19 */
20 /* We welcome the use of the Mezzanine engine to anyone, including companies who wish to
21  Build professional software and charge for their product.
22 
23  However there are some practical restrictions, so if your project involves
24  any of the following you should contact us and we will try to work something
25  out:
26  - DRM or Copy Protection of any kind(except Copyrights)
27  - Software Patents You Do Not Wish to Freely License
28  - Any Kind of Linking to Non-GPL licensed Works
29  - Are Currently In Violation of Another Copyright Holder's GPL License
30  - If You want to change our code and not add a few hundred MB of stuff to
31  your distribution
32 
33  These and other limitations could cause serious legal problems if you ignore
34  them, so it is best to simply contact us or the Free Software Foundation, if
35  you have any questions.
36 
37  Joseph Toppi - toppij@gmail.com
38  John Blackwood - makoenergy02@gmail.com
39 */
40 #ifndef _binarytool_cpp
41 #define _binarytool_cpp
42 
43 /// @file
44 /// @brief The implementation of some tools for making working with binary stuff easier.
45 
46 #include "binarybuffer.h"
47 #include "exception.h"
48 
49 #include <string.h>
50 #include <algorithm>
51 
52 namespace Mezzanine
53 {
54  namespace BinaryTools
55  {
56  /*
57  * Some of the following functions and 1 variable were taken from http://www.adp-gmbh.ch/cpp/common/base64.html
58  * for the functions IsBase64(unsigned char c), Base64Encode(UInt8 const* BytesToEncode, unsigned int Length),
59  * Base64Decode(String const& EncodedString) and Base64Chars and maybe a few others in this file or the
60  * tests for this file
61  * with written permission as follows.
62 
63 
64  Copyright (C) 2004-2008 René Nyffenegger
65 
66  This source code is provided 'as-is', without any express or implied
67  warranty. In no event will the author be held liable for any damages
68  arising from the use of this software.
69 
70  Permission is granted to anyone to use this software for any purpose,
71  including commercial applications, and to alter it and redistribute it
72  freely, subject to the following restrictions:
73 
74  1. The origin of this source code must not be misrepresented; you must not
75  claim that you wrote the original source code. If you use this source code
76  in a product, an acknowledgment in the product documentation would be
77  appreciated but is not required.
78 
79  2. Altered source versions must be plainly marked as such, and must not be
80  misrepresented as being the original source code.
81 
82  3. This notice may not be removed or altered from any source distribution.
83 
84  René Nyffenegger rene.nyffenegger@adp-gmbh.ch
85 
86  */
87 
88  namespace
89  {
90  // Code change to Match BTS naming conventions and formatting
91  static const String Base64Chars =
92  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
93  "abcdefghijklmnopqrstuvwxyz"
94  "0123456789+/";
95 
96  /// @brief The actual implementation of the Base64 decoding
97  /// @param EncodedString The String to decode
98  /// @param Results A reference to a @ref BinaryBuffer with the Size set correctly and a Buffer Allocated, this will be the output.
99  void Base64DecodeImpl(const String& EncodedString, BinaryBuffer& Results)
100  {
101  if(Results.Binary==0 || Results.Size==0)
102  { MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_EXCEPTION, "Cannot encode an empty buffer."); }
103 
104  String::const_iterator Progress = EncodedString.begin();
105  Whole Output = 0;
106 
107  //Initializing this RAM really slows it down like 30% to double onto execution time.
108  //for(Whole c=0; c<Results.Size; c++)
109  //{ Results[c] = 0; }
110 
111  unsigned char First;
112  unsigned char Second;
113  unsigned char Third;
114  unsigned char Fourth;
115 
116  while(Progress<EncodedString.end())
117  {
118  // cout << *(Progress+0) << *(Progress+1) << *(Progress+2) << *(Progress+3) << endl;
119  // cout << !IsBase64(*Progress) << !IsBase64(*(Progress+1)) << !IsBase64(*(Progress+2)) << !IsBase64(*(Progress+3)) <<endl;
120  if(!IsBase64(*Progress) || !IsBase64(*(Progress+1)) || !IsBase64(*(Progress+2)) || !IsBase64(*(Progress+3)))
121  { MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_EXCEPTION, "Base64 contains an invalid character and cannot be decoded."); }
122 
123  First = Base64Chars.find(*(Progress+0));
124  Second = Base64Chars.find(*(Progress+1));
125  Third = *(Progress+2)=='=' ? 0 : Base64Chars.find(*(Progress+2));
126 
127  #ifdef MEZZDEBUG
128  if(Output+1>Results.Size)
129  { MEZZ_EXCEPTION(ExceptionBase::INVALID_STATE_EXCEPTION, "Output of base64 Decoding is larger than it should be."); }
130  #endif
131 
132  *(Results.Binary+Output+0) = (First << 2) + ((Second & 0x30) >> 4);
133  *(Results.Binary+Output+1) = ((Second & 0xf) << 4) + ((Third & 0x3c) >> 2);
134  if(*(Progress+3)!='=')
135  {
136  #ifdef MEZZDEBUG
137  if(Output+2>Results.Size)
138  { MEZZ_EXCEPTION(ExceptionBase::INVALID_STATE_EXCEPTION, "Output of base64 Decoding is larger than it should be."); }
139  #endif
140  Fourth = Base64Chars.find(*(Progress+3));
141  *(Results.Binary+Output+2) = ((Third & 0x3) << 6) + Fourth;
142  }
143 
144  #ifdef MEZZDEBUG
145  if(Progress>EncodedString.end())
146  { MEZZ_EXCEPTION(ExceptionBase::INVALID_STATE_EXCEPTION, "Gone past the end of the input while decoding a base64 string."); }
147  #endif
148  Output+=3;
149  Progress+=4;
150  }
151  }
152  }
153 
154  ///////////////////////////////////////////////////////////////////////////////
155  // Binary Buffer Methods
156 
157  ///////////////////////////////////////////////////////////////////////////////
158  // Construction and Destruction
159 
161  Size(0),
162  Binary(NULL)
163  { }
164 
166  {
167  this->Size = Other.Size;
168  if( Other.Size && Other.Binary ) {
169  this->Binary = new Byte[this->Size * sizeof(Byte)];
170  memcpy(this->Binary,Other.Binary,this->Size);
171  }else{
172  this->Binary = 0;
173  }
174  }
175 
176  BinaryBuffer::BinaryBuffer(const Whole PredeterminedSize) :
177  Size(PredeterminedSize),
178  Binary(new Byte[PredeterminedSize])
179  { }
180 
181  BinaryBuffer::BinaryBuffer(Byte* BinaryPointer, const Whole PredeterminedSize) :
182  Size(PredeterminedSize),
183  Binary(BinaryPointer ? BinaryPointer : new Byte[PredeterminedSize])
184  { }
185 
186  BinaryBuffer::BinaryBuffer(const String& DataString, const Boole IsBase64)
187  {
188  if( IsBase64 ) {
189  this->Binary = NULL;
190  this->CreateFromBase64(DataString);
191  }else{
192  this->Size = DataString.size();
193  this->CreateBuffer();
194  memcpy(this->Binary, DataString.c_str(), this->Size);
195  }
196  }
197 
199  { this->DeleteBuffer(); }
200 
201  ///////////////////////////////////////////////////////////////////////////////
202  // Utility
203 
205  {
206  if( this->Size ) {
207  this->Binary = new Byte[this->Size * sizeof(Byte)];
208  }else{
209  MEZZ_EXCEPTION(ExceptionBase::INVALID_STATE_EXCEPTION, "Cannot create a 0 length Buffer.");
210  }
211  }
212 
213  void BinaryBuffer::DeleteBuffer(const Whole NewSize)
214  {
215  if( this->Binary )
216  { delete[] this->Binary; }
217  this->Binary = NULL;
218  this->Size = NewSize;
219  }
220 
221  void BinaryBuffer::CreateFromBase64(const String& EncodedBinaryData)
222  {
223  if( this->Binary )
224  { delete[] Binary; }
225  this->Size = PredictBinarySizeFromBase64String(EncodedBinaryData);
226  this->Binary = new Byte[Size * sizeof(Byte)];
227  Base64DecodeImpl(EncodedBinaryData,*this);
228  }
229 
231  {
232  if( !this->Size || !this->Binary ) {
233  MEZZ_EXCEPTION(ExceptionBase::INVALID_STATE_EXCEPTION, "Cannot encode 0 length or missing buffer.");
234  }
235  return Base64Encode((UInt8*)this->Binary,this->Size * sizeof(Byte));
236  }
237 
238  void BinaryBuffer::Concatenate(const Byte* OtherBuffer, const Whole ByteSize)
239  {
240  Whole NewSize = (this->Size + ByteSize) * sizeof(Byte);
241  Byte* TargetBuffer = new Byte[NewSize];
242 
243  memcpy(TargetBuffer, this->Binary, this->Size);
244  memcpy(TargetBuffer+this->Size, OtherBuffer, ByteSize);
245 
246  this->DeleteBuffer(NewSize);
247  this->Binary = TargetBuffer;
248  }
249 
250  void BinaryBuffer::Concatenate(const BinaryBuffer BufferFromAnotherMother)
251  { this->Concatenate(BufferFromAnotherMother.Binary, BufferFromAnotherMother.Size); }
252 
254  {
255  if( !this->Size || !this->Binary ) {
256  MEZZ_EXCEPTION(ExceptionBase::INVALID_STATE_EXCEPTION, "Cannot construct string from 0 length or missing buffer.");
257  }
258  return String((char*)(this->Binary),this->Size * sizeof(Byte));
259  }
260 
262  { return this->Size; }
263 
264  ///////////////////////////////////////////////////////////////////////////////
265  // Operators
266 
268  {
269  if( RH.Binary == this->Binary )
270  { MEZZ_EXCEPTION(ExceptionBase::INVALID_ASSIGNMENT, "Attempted a self assignment of a BinaryBuffer"); }
271  this->DeleteBuffer(RH.Size);
272  if( RH.Size && RH.Binary ) {
273  this->CreateBuffer();
274  memcpy(this->Binary,RH.Binary,this->Size);
275  }else{
276  this->Binary = 0;
277  }
278  return *this;
279  }
280 
282  {
283  this->Concatenate(RH);
284  return *this;
285  }
286 
287 
289  {
290  #ifdef MEZZDEBUG
291  if( Index >= this->Size )
292  { MEZZ_EXCEPTION(ExceptionBase::MM_OUT_OF_BOUNDS_EXCEPTION, "Attempted access beyond range of Binary Buffer"); }
293  #endif
294  return *(this->Binary + Index);
295  }
296 
297  ///////////////////////////////////////////////////////////////////////////////
298  // Base64 Helpers
299 
300  // Code change to Match BTS naming conventions and formatting
301  Boole IsBase64(unsigned char Character)
302  { return (isalnum(Character) || (Character == '+') || (Character == '/') || (Character == '=')); }
303 
304  String Base64Encode(String const& Unencoded)
305  { return Base64Encode((UInt8 const*)Unencoded.c_str(), Unencoded.size()); }
306 
307  String Base64Encode(const BinaryBuffer &Buffer)
308  { return Base64Encode((const UInt8*) Buffer.Binary,Buffer.Size); }
309 
310  // Code change to Match BTS naming conventions and formatting
311  String Base64Encode(UInt8 const* BytesToEncode, unsigned int Length)
312  {
313  String Results;
314  Results.reserve(PredictBase64StringSizeFromBinarySize(Length));
315 
316  int i = 0;
317  int j = 0;
318  unsigned char char_array_3[3];
319  unsigned char char_array_4[4];
320 
321  while (Length--)
322  {
323  char_array_3[i++] = *(BytesToEncode++);
324  if (i == 3)
325  {
326  char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
327  char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
328  char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
329  char_array_4[3] = char_array_3[2] & 0x3f;
330 
331  for(i = 0; (i <4) ; i++)
332  { Results += Base64Chars[char_array_4[i]]; }
333  i = 0;
334  }
335  }
336 
337  if (i)
338  {
339  for(j = i; j < 3; j++)
340  { char_array_3[j] = '\0'; }
341 
342  char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
343  char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
344  char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
345  char_array_4[3] = char_array_3[2] & 0x3f;
346 
347  for (j = 0; (j < i + 1); j++)
348  { Results += Base64Chars[char_array_4[j]]; }
349 
350  while((i++ < 3))
351  { Results += '='; }
352  }
353 
354  return Results;
355  }
356 
357  // Code change to Match BTS naming conventions and formatting, then eventually refactored algorithm to adjust return type and switch from Olog(n) to O(n).
358  // This one is guaranteed exactly one memory allocation(+= on String likely only doubles allocation repeatidly), less copying, and in general is simpler
359  BinaryBuffer Base64Decode(String const& EncodedString)
360  {
361  BinaryBuffer Results(PredictBinarySizeFromBase64String(EncodedString));
362 
363  Base64DecodeImpl(EncodedString, Results);
364 
365  return Results;
366  }
367 
368  Whole PredictBinarySizeFromBase64String(String const& EncodedString)
369  {
370  if(EncodedString.size()<4)
371  { MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_EXCEPTION, "It is not possible to have a base64 string less than 4 bytes in length, but one was received.") }
372 
373  return EncodedString.size()/4*3 - ( EncodedString.at(EncodedString.size()-2)=='=' ?1:0) - ( EncodedString.at(EncodedString.size()-1)=='=' ?1:0);
374  }
375 
376  Whole PredictBase64StringSizeFromBinarySize(Whole Length)
377  { return (Length+2)/3*4; }
378  }//BinaryTools
379 }//Mezzanine
380 
381 #endif
void DeleteBuffer(const Whole NewSize=0)
Deletes whatever Binary points at and assigns Size to 0.
Whole GetSize() const
Even though this class is intended to have its internals modified directly in some cases...
BinaryBuffer & operator+=(const BinaryBuffer &RH)
Addition Assignment Operator.
BinaryBuffer & operator=(const BinaryBuffer &RH)
Assignment Operator.
bool Boole
Generally acts a single bit, true or false.
Definition: datatypes.h:173
A grouping of utilities for working with binary data more easily.
void Concatenate(const Byte *OtherBuffer, const Whole ByteSize)
Append another group of arbitrary data onto this one.
String ToString()
Get the contents of this crudely converted to a c style string then stuff it in a string...
#define MEZZ_EXCEPTION(num, desc)
An easy way to throw exceptions with rich information.
Definition: exception.h:3048
void CreateFromBase64(const String &EncodedBinaryData)
This calls deallocates any space, allocates fresh space of the size requestedthen the Decodes the pas...
Byte & operator[](const Whole Index)
Access a part of the buffer.
String ToBase64String()
Get the binary buffer as a base64 string.
Whole Size
How many bytes is Binary in size. This is set to 0 if Binary is invalid and should be a null pointer...
Definition: binarybuffer.h:81
uint8_t UInt8
An 8-bit unsigned integer.
Definition: datatypes.h:118
Thrown when a complex class is assigned to itself or other invalid assignments occur.
Definition: exception.h:118
This implements the exception hiearchy for Mezzanine.
void CreateBuffer()
This will create a buffer with size matching the this->Size and point this->Binary to that Buffer...
Int8 Byte
The type of data this buffer can hold, it is intended to be some type one byte in length...
Definition: binarybuffer.h:75
Thrown when the available information should have worked but failed for unknown reasons.
Definition: exception.h:113
Byte * Binary
A pointer to the actual binary data.
Definition: binarybuffer.h:83
Thrown when parameters are checked at runtime and found invalid.
Definition: exception.h:108
BinaryBuffer()
Default constructor, set everything to zero. Doesn't allocate anything.
The bulk of the engine components go in this namspace.
Definition: actor.cpp:56
unsigned long Whole
Whole is an unsigned integer, it will be at least 32bits in size.
Definition: datatypes.h:151
~BinaryBuffer()
Virtual deconstructor calls DeleteBuffer() to clean up whatever has been inserted here...
Thrown when attempted to access something that really should note be accessed.
Definition: exception.h:98
A way to store and pass binary buffers, for example compiled bytecode.
Definition: binarybuffer.h:68
std::string String
A datatype used to a series of characters.
Definition: datatypes.h:159