1 | #include <xstream/config.h> |
2 | #include <xstream/base64.h> |
3 | #include <xstream/except/base64.h> |
4 | |
5 | #include "debug.h" |
6 | |
7 | namespace xstream |
8 | { |
9 | namespace base64 |
10 | { |
11 | |
12 | static const char dictionary[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
13 | |
14 | static const int eof = std::streambuf::traits_type::eof(); |
15 | |
16 | ostreambuf::ostreambuf(std::streambuf* sb, unsigned int w, char c) |
17 | : _sb(sb), delim(c), delim_w(w), col(0) { |
18 | LOG("base64::ostreambuf (streambuf*)"); |
19 | reset(); |
20 | } |
21 | |
22 | void ostreambuf::reset() { |
23 | LOG("base64::ostreambuf::reset"); |
24 | setp(buf,buf+sizeof(buf)/sizeof(char)); |
25 | } |
26 | |
27 | |
28 | int ostreambuf::sync() { |
29 | LOG("base64::ostreambuf::sync"); |
30 | return _sb->pubsync(); |
31 | } |
32 | |
33 | static inline void encode(const char* in, char* out) { |
34 | LOG("base64::encode"); |
35 | static const unsigned int hi6 = ((1 << 6) -1 ) << 2; |
36 | static const unsigned int lo2 = (1 << 2) - 1; |
37 | static const unsigned int hi4 = ((1 << 4) - 1) << 4; |
38 | static const unsigned int lo4 = (1 << 4) - 1; |
39 | static const unsigned int hi2 = ((1 << 2) - 1) << 6; |
40 | static const unsigned int lo6 = (1 << 6) - 1; |
41 | |
42 | out[0] = dictionary[(hi6 & in[0]) >> 2]; |
43 | out[1] = dictionary[(lo2 & in[0]) << 4 | (hi4 & in[1]) >> 4]; |
44 | out[2] = dictionary[(lo4 & in[1]) << 2 | (hi2 & in[2]) >> 6]; |
45 | out[3] = dictionary[lo6 & in[2]]; |
46 | } |
47 | |
48 | int ostreambuf::write(const char* buf, size_t len) { |
49 | unsigned int rcol = delim_w - col; |
50 | int ret = 0; |
51 | |
52 | |
53 | if (delim_w > 0 && rcol <= len) { |
54 | LOG("\t" << rcol << " columns to padding"); |
55 | ret = _sb->sputn(buf, rcol); |
| Value stored to 'ret' is never read |
56 | ret = _sb->sputc(delim); |
57 | ret = _sb->sputn(buf + rcol, len - rcol); |
58 | col = len - rcol; |
59 | LOG("\tcol = " << col); |
60 | } |
61 | else { |
62 | ret = _sb->sputn(buf,len); |
63 | col += len; |
64 | } |
65 | |
66 | reset(); |
67 | |
68 | return ret; |
69 | } |
70 | |
71 | int ostreambuf::overflow(int c) { |
72 | LOG("base64::ostreambuf::overflow (" << c << ")\n\t[" << *buf << *(buf+1) << *(buf+2) << "]"); |
73 | |
74 | char enc[4]; |
75 | encode(buf, enc); |
76 | |
77 | write(enc, 4); |
78 | |
79 | *pptr() = static_cast < char >(c); |
80 | pbump(1); |
81 | |
82 | return c; |
83 | } |
84 | |
85 | ostreambuf::~ostreambuf() { |
86 | LOG("base64::~ostreambuf"); |
87 | int av = available(); |
88 | |
89 | if (3 == av) { |
90 | |
91 | } |
92 | else { |
93 | char enc[4]; |
94 | for(int i=0; i < av; ++i) { |
95 | |
96 | buf[2-i] = '\0'; |
97 | } |
98 | encode(buf,enc); |
99 | |
100 | if (1 <= av) { |
101 | enc[3] = '='; |
102 | } |
103 | if (2 <= av) { |
104 | enc[2] = '='; |
105 | } |
106 | |
107 | |
108 | write(enc,4); |
109 | } |
110 | |
111 | _sb->pubsync(); |
112 | } |
113 | |
114 | |
115 | static inline char index(char c) { |
116 | static const int window = 'Z'- 'A' + 1 ; |
117 | |
118 | |
119 | |
120 | |
121 | int d = c - 'A'; |
122 | int ret = -1; |
123 | |
124 | if (d >= 0 && d < window) { |
125 | ret = d; |
126 | } |
127 | else { |
128 | d = c - 'a'; |
129 | if (d >= 0 && d < window) { |
130 | ret = d + window; |
131 | } |
132 | else { |
133 | d = c - '0'; |
134 | if (d >= 0 && d < 10) { |
135 | ret = d + 2 * window; |
136 | } |
137 | else { |
138 | if ('+' == c) { |
139 | ret = 62; |
140 | } |
141 | else { |
142 | if ('/' == c) { |
143 | ret = 63; |
144 | } |
145 | } |
146 | } |
147 | } |
148 | } |
149 | |
150 | if (-1 == ret) { |
151 | LOG("base64::index (" << c << ") [unknown]"); |
152 | throw(decode_error(std::string("character '") + c + "' not part of base64 alphabet")); |
153 | } |
154 | else { |
155 | return ret; |
156 | } |
157 | } |
158 | |
159 | static inline void decode(const char* in, char* out) { |
160 | LOG("base64::decode"); |
161 | static const unsigned char hi2 = ((1 << 2) - 1) << 4; |
162 | static const unsigned char lo4 = ((1 << 4) - 1); |
163 | static const unsigned char hi4 = ((1 << 4) - 1) << 2; |
164 | static const unsigned char lo2 = ((1 << 2) - 1); |
165 | |
166 | char _in[4]; |
167 | |
168 | for (int i=0; i < 4; ++i) { |
169 | _in[i] = index(in[i]); |
170 | } |
171 | |
172 | char c; |
173 | c = _in[0] << 2; |
174 | c |= (_in[1] & hi2) >> 4; |
175 | out[0] = c; |
176 | |
177 | c = (_in[1] & lo4) << 4; |
178 | c |= (_in[2] & hi4) >> 2; |
179 | out[1] = c; |
180 | |
181 | c = (_in[2] & lo2) << 6; |
182 | c |= _in[3]; |
183 | out[2] = c; |
184 | } |
185 | |
186 | istreambuf::istreambuf(std::streambuf* sb, unsigned int d_w, char d) |
187 | : _sb(sb), end(false), delim(d), delim_w(d_w), col(0) { |
188 | LOG("base64::istreambuf (streambuf*)"); |
189 | setg(buf, buf, buf); |
190 | } |
191 | |
192 | int istreambuf::underflow() { |
193 | LOG("base64::istreambuf::underflow"); |
194 | |
195 | if (end) { |
196 | LOG("\tattempt to read from an ended stream"); |
197 | return eof; |
198 | } |
199 | else { |
200 | char enc[4 + 1]; |
201 | int av = (delim_w - col); |
202 | if (delim_w > 0 && av <= 4) { |
203 | LOG("\texpecting delimiter at " << av); |
204 | int ret = _sb->sgetn(enc, 5); |
205 | LOG("\tenc=" << enc); |
206 | if (5 != ret) { |
207 | LOG("\tread " << ret << " but wanted " << 5); |
208 | |
209 | end = true; |
210 | return eof; |
211 | } |
212 | if (delim != enc[av]) { |
213 | LOG("\texpected delimiter " << delim << " but got " << enc[av]); |
214 | end = true; |
215 | throw decode_error("expected delimiter missing"); |
216 | return eof; |
217 | } |
218 | else { |
219 | col = 4 - av; |
220 | for (int i=av; i < 4; ++i) { |
221 | enc[i] = enc[i + 1]; |
222 | } |
223 | } |
224 | } |
225 | else { |
226 | int ret = _sb->sgetn(enc, 4); |
227 | col += 4; |
228 | |
229 | if (4 != ret) { |
230 | |
231 | end = true; |
232 | return eof; |
233 | } |
234 | } |
235 | int len=3; |
236 | if ('=' == enc[3]) { |
237 | enc[3] = 'A'; |
238 | if ('=' == enc[2]) { |
239 | len = 1; |
240 | enc[2] = 'A'; |
241 | } else { |
242 | len = 2; |
243 | } |
244 | } |
245 | if (3 != len) { |
246 | end = true; |
247 | } |
248 | |
249 | decode(enc, buf); |
250 | setg(buf, buf, buf + len); |
251 | return 0; |
252 | } |
253 | } |
254 | |
255 | istreambuf::~istreambuf() { |
256 | LOG("base64::~istreambuf"); |
257 | } |
258 | |
259 | } |
260 | } |