TFCweb  1.0.4 $Rev: 483 $
TFC Primavera 2012: Nucli d'un servidor web
Base64.cc
Veure la documentació d'aquest fitxer.
1 
9 /*
10  * Copyright (c) 2012 Toni Corvera
11  *
12  * This file is part of TFCWeb.
13  *
14  * TFCWeb is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * TFCWeb is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with TFCWeb. If not, see <http://www.gnu.org/licenses/>.
26  */
27 
28 #include "Base64.h"
29 
30 #include <boost/array.hpp>
31 #include <boost/bimap.hpp>
32 #include <boost/assign.hpp>
33 
34 using namespace std;
35 
36 namespace {
37 
38 // boost::bimap és un mapa bidireccional, és a dir, un mapa en què es pot fer sevir
39 // tant el primer com el segon element com a clau de cerca.
40 // bimap.left es correspon a un mapa normal
41 // bimap.right es correspon a un mapa invers (el valor fa de clau i viceversa)
42 typedef boost::bimap<unsigned int, char> uint_a_char_bimap;
43 // assign::map_list_of no es pot utilitzar directament amb bimap
44 // http://www.boost.org/doc/libs/1_49_0/libs/bimap/example/bimap_and_boost/assign.cpp
45 uint_a_char_bimap TRADUCCIO = boost::assign::list_of<uint_a_char_bimap::relation>
46  ( 0, 'A' )
47  ( 1, 'B' )
48  ( 2, 'C' )
49  ( 3, 'D' )
50  ( 4, 'E' )
51  ( 5, 'F' )
52  ( 6, 'G' )
53  ( 7, 'H' )
54  ( 8, 'I' )
55  ( 9, 'J' )
56  ( 10, 'K' )
57  ( 11, 'L' )
58  ( 12, 'M' )
59  ( 13, 'N' )
60  ( 14, 'O' )
61  ( 15, 'P' )
62  ( 16, 'Q' )
63  ( 17, 'R' )
64  ( 18, 'S' )
65  ( 19, 'T' )
66  ( 20, 'U' )
67  ( 21, 'V' )
68  ( 22, 'W' )
69  ( 23, 'X' )
70  ( 24, 'Y' )
71  ( 25, 'Z' )
72  ( 26, 'a' )
73  ( 27, 'b' )
74  ( 28, 'c' )
75  ( 29, 'd' )
76  ( 30, 'e' )
77  ( 31, 'f' )
78  ( 32, 'g' )
79  ( 33, 'h' )
80  ( 34, 'i' )
81  ( 35, 'j' )
82  ( 36, 'k' )
83  ( 37, 'l' )
84  ( 38, 'm' )
85  ( 39, 'n' )
86  ( 40, 'o' )
87  ( 41, 'p' )
88  ( 42, 'q' )
89  ( 43, 'r' )
90  ( 44, 's' )
91  ( 45, 't' )
92  ( 46, 'u' )
93  ( 47, 'v' )
94  ( 48, 'w' )
95  ( 49, 'x' )
96  ( 50, 'y' )
97  ( 51, 'z' )
98  ( 52, '0' )
99  ( 53, '1' )
100  ( 54, '2' )
101  ( 55, '3' )
102  ( 56, '4' )
103  ( 57, '5' )
104  ( 58, '6' )
105  ( 59, '7' )
106  ( 60, '8' )
107  ( 61, '9' )
108  ( 62, '+' )
109  ( 63, '/' );
110 } // ns anònim
111 
112 namespace tfc {
113 
114 namespace Base64 {
115 
116 string codifica(const string & S) {
117  string s(S);
118  // extensió (padding):
119  short padding = 0;
120  if (s.length() % 3 == 1) {
121  s.push_back(0);
122  s.push_back(0);
123  padding = 2;
124  }
125  else if (s.length() % 3 == 2) {
126  s.push_back(0);
127  padding = 1;
128  }
129  assert( 0 == (s.length() % 3) );
130  string r;
131  r.reserve(static_cast<size_t>(s.length() * 1.34)); // base64 afegeix ~33%
132  typedef bitset<sizeof(int)*8> bset;
133  for (auto it=s.begin(); it != s.end(); it+=3) {
134  // Important que sigui unsigned char per cadenes "binàries"
135  const unsigned char a = *it,
136  b = *(it+1),
137  c = *(it+2);
138  typedef unsigned int uint;
139  const unsigned long abc = a<<16 | b<<8 | c;
140  const uint x1 = static_cast<uint>( abc & 0x00003f),
141  x2 = static_cast<uint>((abc & 0x000fc0) >> 6),
142  x3 = static_cast<uint>((abc & 0x03f000) >> 12),
143  x4 = static_cast<uint>((abc & 0xfc0000) >> 18);
144  r.push_back(TRADUCCIO.left.at(x4));
145  r.push_back(TRADUCCIO.left.at(x3));
146  r.push_back(TRADUCCIO.left.at(x2));
147  r.push_back(TRADUCCIO.left.at(x1));
148  }
149  if (padding > 0) {
150  for (auto rit = r.rbegin(); rit != r.rbegin()+padding; ++rit) {
151  *rit = '=';
152  }
153  }
154  return r;
155 }
156 
157 inline string descodifica_bloc(const boost::array<char,4> & bloc, size_t valids=4) {
158  if ( valids == 0 ) { return ""; }
159 
160  unsigned long bytes = 0;
161  for (size_t idx=0; idx<valids; ++idx) {
162  bytes = bytes << 6 | TRADUCCIO.right.at(bloc[idx]);
163  }
164  const size_t offset_ajust = (4-valids)*6;
165  bytes = bytes << offset_ajust;
166  assert( 0 == (bytes & 0xFF000000) );
167  const char d3 = bytes & 0x0000FF,
168  d2 = (bytes & 0x00FF00) >> 8,
169  d1 = (bytes & 0xFF0000) >> 16;
170  string r;
171  r.reserve(3);
172  r.push_back(d1);
173  r.push_back(d2);
174  r.push_back(d3);
175  // Re-ajust: assegurem que no afegim 0's extra
176  switch (offset_ajust) {
177  case 6:
178  r.erase(r.length()-1);
179  break;
180  case 12:
181  r.erase(r.length()-2);
182  break;
183  case 18: // ???
184  r.erase(r.length()-3);
185  break;
186  default:
187  break;
188  }
189  return r;
190 }
191 
192 string descodifica(const string & S) {
193  string s;
194  s.reserve(S.length());
195  // Neteja de la cadena: Tots els valors fora de la taula de traducció
196  // s'han d'ignorar
197  for (auto it=S.begin(); it!=S.end(); ++it) {
198  try {
199  TRADUCCIO.right.at(*it);
200  s.push_back(*it);
201  }
202  catch (const std::out_of_range &) {
203  continue;
204  }
205  }
206  string r;
207  r.reserve(s.length()); // de sobres, serà ~ un 33% més curta
208  boost::array<char,4> bloc;
209  size_t idx=0;
210  // TODO: Ignorar > 63!
211  for (auto it=s.begin(); it != s.end(); ++it) {
212  bloc[idx++] = *it;
213 
214  if (idx == 4) {
215  r.append(descodifica_bloc(bloc));
216  idx=0;
217  }
218  }
219  const size_t resta = (s.length() % 4);
220  r.append(descodifica_bloc(bloc, resta));
221  return r;
222 }
223 
224 } // ns Base64
225 
226 } // ns tfc
227 
228 // vim:set ts=4 et ai: //