MezzanineEngine 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
binarybuffer.cpp
Go to the documentation of this file.
1 // © Copyright 2010 - 2014 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(Exception::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  // ©out << *(Progress+0) << *(Progress+1) << *(Progress+2) << *(Progress+3) << endl;
119  // ©out << !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(Exception::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(Exception::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(Exception::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(Exception::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 
156  {
157  this->Size = Other.Size;
158  if(Other.Size && Other.Binary)
159  {
160  this->Binary = new Byte[this->Size*sizeof(Byte)];
161  memcpy(this->Binary,Other.Binary,this->Size);
162  }else{
163  this->Binary = 0;
164  }
165  }
166 
167  BinaryBuffer::BinaryBuffer(const String& DataString, bool IsBase64)
168  {
169  if(IsBase64)
170  {
171  Binary=0;
172  this->CreateFromBase64(DataString);
173  }
174  else
175  {
176  this->Size = DataString.size();
177  CreateBuffer();
178  memcpy(this->Binary, DataString.c_str(), this->Size);
179  }
180  }
181 
183  {
184  if (RH.Binary == this->Binary)
185  { MEZZ_EXCEPTION(Exception::INVALID_ASSIGNMENT, "Attempted a self assignment of a BinaryBuffer"); }
186  DeleteBuffer(RH.Size);
187  if(RH.Size && RH.Binary)
188  {
189  CreateBuffer();
190  memcpy(this->Binary,RH.Binary,this->Size);
191  }else{
192  this->Binary = 0;
193  }
194  return *this;
195  }
196 
198  { DeleteBuffer(); }
199 
201  {
202  if(Binary)
203  { delete[] Binary; }
204  Binary=0;
205  Size = NewSize;
206  }
207 
209  {
210  if(Size)
211  {
212  this->Binary = new Byte[this->Size*sizeof(Byte)];
213  }else{
214  MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION, "Cannot create a 0 length Buffer.");
215  }
216  }
217 
219  {
220  if(!Size || !Binary)
221  {
222  MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION, "Cannot encode 0 length or missing buffer.");
223  }
224  return Base64Encode((UInt8*)Binary,Size*sizeof(Byte));
225  }
226 
228  {
229  if(!Size || !Binary)
230  {
231  MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION, "Cannot construct string from 0 length or missing buffer.");
232  }
233  return String((char*)(this->Binary),this->Size*sizeof(Byte));
234  }
235 
236  void BinaryBuffer::CreateFromBase64(const String& EncodedBinaryData)
237  {
238  if(Binary)
239  { delete[] Binary; }
240  Size = PredictBinarySizeFromBase64String(EncodedBinaryData);
241  Binary = new Byte[Size*sizeof(Byte)];
242  Base64DecodeImpl(EncodedBinaryData,*this);
243  }
244 
246  {
247  #ifdef MEZZDEBUG
248  if(Index>=Size)
249  { MEZZ_EXCEPTION(Exception::MM_OUT_OF_BOUNDS_EXCEPTION, "Attempted access beyond range of Binary Buffer"); }
250  #endif
251  return *(Binary+Index);
252  }
253 
254  void BinaryBuffer::Concatenate(const Byte* OtherBuffer, Whole ByteSize)
255  {
256  Whole NewSize = (this->Size + ByteSize) * sizeof(Byte);
257  Byte* TargetBuffer = new Byte[NewSize];
258 
259  memcpy(TargetBuffer, this->Binary, this->Size);
260  memcpy(TargetBuffer+this->Size, OtherBuffer, ByteSize);
261 
262  DeleteBuffer(NewSize);
263  this->Binary = TargetBuffer;
264  }
265 
266  void BinaryBuffer::Concatenate(const BinaryBuffer BufferFromAnotherMother)
267  { Concatenate(BufferFromAnotherMother.Binary, BufferFromAnotherMother.Size); }
268 
269 
271  {
272  Concatenate(RH);
273  return *this;
274  }
275 
277  { return Size; }
278 
279  // Code change to Match BTS naming conventions and formatting
280  bool IsBase64(unsigned char Character)
281  { return (isalnum(Character) || (Character == '+') || (Character == '/') || (Character == '=')); }
282 
283  String Base64Encode(String const& Unencoded)
284  { return Base64Encode((UInt8 const*)Unencoded.c_str(), Unencoded.size()); }
285 
287  { return Base64Encode((const UInt8*) Buffer.Binary,Buffer.Size); }
288 
289  // Code change to Match BTS naming conventions and formatting
290  String Base64Encode(UInt8 const* BytesToEncode, unsigned int Length)
291  {
292  String Results;
293  Results.reserve(PredictBase64StringSizeFromBinarySize(Length));
294 
295  int i = 0;
296  int j = 0;
297  unsigned char char_array_3[3];
298  unsigned char char_array_4[4];
299 
300  while (Length--)
301  {
302  char_array_3[i++] = *(BytesToEncode++);
303  if (i == 3)
304  {
305  char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
306  char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
307  char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
308  char_array_4[3] = char_array_3[2] & 0x3f;
309 
310  for(i = 0; (i <4) ; i++)
311  { Results += Base64Chars[char_array_4[i]]; }
312  i = 0;
313  }
314  }
315 
316  if (i)
317  {
318  for(j = i; j < 3; j++)
319  { char_array_3[j] = '\0'; }
320 
321  char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
322  char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
323  char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
324  char_array_4[3] = char_array_3[2] & 0x3f;
325 
326  for (j = 0; (j < i + 1); j++)
327  { Results += Base64Chars[char_array_4[j]]; }
328 
329  while((i++ < 3))
330  { Results += '='; }
331  }
332 
333  return Results;
334  }
335 
336  // 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).
337  // This one is guaranteed exactly one memory allocation(+= on String likely only doubles allocation repeatidly), less copying, and in general is simpler
338  BinaryBuffer Base64Decode(String const& EncodedString)
339  {
340  BinaryBuffer Results(PredictBinarySizeFromBase64String(EncodedString));
341 
342  Base64DecodeImpl(EncodedString, Results);
343 
344  return Results;
345  }
346 
348  {
349  if(EncodedString.size()<4)
350  { MEZZ_EXCEPTION(Exception::PARAMETERS_EXCEPTION, "It is not possible to have a base64 string less than 4 bytes in length, but one was received.") }
351 
352  return EncodedString.size()/4*3 - ( EncodedString.at(EncodedString.size()-2)=='=' ?1:0) - ( EncodedString.at(EncodedString.size()-1)=='=' ?1:0);
353  }
354 
356  { return (Length+2)/3*4; }
357 
358 
359  } // BinaryTools
360 }//Mezzanine
361 
362 #endif