Bumping manifests a=b2g-bump
[gecko.git] / xpcom / io / Base64.cpp
blob34e69bf3e8144f02de2df896e29f0e3e29643e52
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "Base64.h"
9 #include "nsIInputStream.h"
10 #include "nsString.h"
12 #include "plbase64.h"
14 namespace {
16 // BEGIN base64 encode code copied and modified from NSPR
17 const unsigned char* base =
18 (unsigned char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
19 "abcdefghijklmnopqrstuvwxyz"
20 "0123456789+/";
22 template<typename T>
23 static void
24 Encode3to4(const unsigned char* aSrc, T* aDest)
26 uint32_t b32 = (uint32_t)0;
27 int i, j = 18;
29 for (i = 0; i < 3; ++i) {
30 b32 <<= 8;
31 b32 |= (uint32_t)aSrc[i];
34 for (i = 0; i < 4; ++i) {
35 aDest[i] = base[(uint32_t)((b32 >> j) & 0x3F)];
36 j -= 6;
40 template<typename T>
41 static void
42 Encode2to4(const unsigned char* aSrc, T* aDest)
44 aDest[0] = base[(uint32_t)((aSrc[0] >> 2) & 0x3F)];
45 aDest[1] = base[(uint32_t)(((aSrc[0] & 0x03) << 4) | ((aSrc[1] >> 4) & 0x0F))];
46 aDest[2] = base[(uint32_t)((aSrc[1] & 0x0F) << 2)];
47 aDest[3] = (unsigned char)'=';
50 template<typename T>
51 static void
52 Encode1to4(const unsigned char* aSrc, T* aDest)
54 aDest[0] = base[(uint32_t)((aSrc[0] >> 2) & 0x3F)];
55 aDest[1] = base[(uint32_t)((aSrc[0] & 0x03) << 4)];
56 aDest[2] = (unsigned char)'=';
57 aDest[3] = (unsigned char)'=';
60 template<typename T>
61 static void
62 Encode(const unsigned char* aSrc, uint32_t aSrcLen, T* aDest)
64 while (aSrcLen >= 3) {
65 Encode3to4(aSrc, aDest);
66 aSrc += 3;
67 aDest += 4;
68 aSrcLen -= 3;
71 switch (aSrcLen) {
72 case 2:
73 Encode2to4(aSrc, aDest);
74 break;
75 case 1:
76 Encode1to4(aSrc, aDest);
77 break;
78 case 0:
79 break;
80 default:
81 NS_NOTREACHED("coding error");
85 // END base64 encode code copied and modified from NSPR.
87 template<typename T>
88 struct EncodeInputStream_State
90 unsigned char c[3];
91 uint8_t charsOnStack;
92 typename T::char_type* buffer;
95 template<typename T>
96 NS_METHOD
97 EncodeInputStream_Encoder(nsIInputStream* aStream,
98 void* aClosure,
99 const char* aFromSegment,
100 uint32_t aToOffset,
101 uint32_t aCount,
102 uint32_t* aWriteCount)
104 NS_ASSERTION(aCount > 0, "Er, what?");
106 EncodeInputStream_State<T>* state =
107 static_cast<EncodeInputStream_State<T>*>(aClosure);
109 // If we have any data left from last time, encode it now.
110 uint32_t countRemaining = aCount;
111 const unsigned char* src = (const unsigned char*)aFromSegment;
112 if (state->charsOnStack) {
113 unsigned char firstSet[4];
114 if (state->charsOnStack == 1) {
115 firstSet[0] = state->c[0];
116 firstSet[1] = src[0];
117 firstSet[2] = (countRemaining > 1) ? src[1] : '\0';
118 firstSet[3] = '\0';
119 } else /* state->charsOnStack == 2 */ {
120 firstSet[0] = state->c[0];
121 firstSet[1] = state->c[1];
122 firstSet[2] = src[0];
123 firstSet[3] = '\0';
125 Encode(firstSet, 3, state->buffer);
126 state->buffer += 4;
127 countRemaining -= (3 - state->charsOnStack);
128 src += (3 - state->charsOnStack);
129 state->charsOnStack = 0;
132 // Encode the bulk of the
133 uint32_t encodeLength = countRemaining - countRemaining % 3;
134 NS_ABORT_IF_FALSE(encodeLength % 3 == 0,
135 "Should have an exact number of triplets!");
136 Encode(src, encodeLength, state->buffer);
137 state->buffer += (encodeLength / 3) * 4;
138 src += encodeLength;
139 countRemaining -= encodeLength;
141 // We must consume all data, so if there's some data left stash it
142 *aWriteCount = aCount;
144 if (countRemaining) {
145 // We should never have a full triplet left at this point.
146 NS_ABORT_IF_FALSE(countRemaining < 3, "We should have encoded more!");
147 state->c[0] = src[0];
148 state->c[1] = (countRemaining == 2) ? src[1] : '\0';
149 state->charsOnStack = countRemaining;
152 return NS_OK;
155 template<typename T>
156 nsresult
157 EncodeInputStream(nsIInputStream* aInputStream,
158 T& aDest,
159 uint32_t aCount,
160 uint32_t aOffset)
162 nsresult rv;
163 uint64_t count64 = aCount;
165 if (!aCount) {
166 rv = aInputStream->Available(&count64);
167 if (NS_WARN_IF(NS_FAILED(rv))) {
168 return rv;
170 // if count64 is over 4GB, it will be failed at the below condition,
171 // then will return NS_ERROR_OUT_OF_MEMORY
172 aCount = (uint32_t)count64;
175 uint64_t countlong =
176 (count64 + 2) / 3 * 4; // +2 due to integer math.
177 if (countlong + aOffset > UINT32_MAX) {
178 return NS_ERROR_OUT_OF_MEMORY;
181 uint32_t count = uint32_t(countlong);
183 aDest.SetLength(count + aOffset);
184 if (aDest.Length() != count + aOffset) {
185 return NS_ERROR_OUT_OF_MEMORY;
188 EncodeInputStream_State<T> state;
189 state.charsOnStack = 0;
190 state.c[2] = '\0';
191 state.buffer = aOffset + aDest.BeginWriting();
193 while (1) {
194 uint32_t read = 0;
196 rv = aInputStream->ReadSegments(&EncodeInputStream_Encoder<T>,
197 (void*)&state,
198 aCount,
199 &read);
200 if (NS_FAILED(rv)) {
201 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
202 NS_RUNTIMEABORT("Not implemented for async streams!");
204 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
205 NS_RUNTIMEABORT("Requires a stream that implements ReadSegments!");
207 return rv;
210 if (!read) {
211 break;
215 // Finish encoding if anything is left
216 if (state.charsOnStack) {
217 Encode(state.c, state.charsOnStack, state.buffer);
220 if (aDest.Length()) {
221 // May belong to an nsCString with an unallocated buffer, so only null
222 // terminate if there is a need to.
223 *aDest.EndWriting() = '\0';
226 return NS_OK;
229 } // namespace (anonymous)
231 namespace mozilla {
233 nsresult
234 Base64EncodeInputStream(nsIInputStream* aInputStream,
235 nsACString& aDest,
236 uint32_t aCount,
237 uint32_t aOffset)
239 return EncodeInputStream<nsACString>(aInputStream, aDest, aCount, aOffset);
242 nsresult
243 Base64EncodeInputStream(nsIInputStream* aInputStream,
244 nsAString& aDest,
245 uint32_t aCount,
246 uint32_t aOffset)
248 return EncodeInputStream<nsAString>(aInputStream, aDest, aCount, aOffset);
251 nsresult
252 Base64Encode(const nsACString& aBinaryData, nsACString& aString)
254 // Check for overflow.
255 if (aBinaryData.Length() > (UINT32_MAX / 4) * 3) {
256 return NS_ERROR_FAILURE;
259 // Don't ask PR_Base64Encode to encode empty strings
260 if (aBinaryData.IsEmpty()) {
261 aString.Truncate();
262 return NS_OK;
265 uint32_t stringLen = ((aBinaryData.Length() + 2) / 3) * 4;
267 char* buffer;
269 // Add one byte for null termination.
270 if (aString.SetCapacity(stringLen + 1, fallible_t()) &&
271 (buffer = aString.BeginWriting()) &&
272 PL_Base64Encode(aBinaryData.BeginReading(), aBinaryData.Length(), buffer)) {
273 // PL_Base64Encode doesn't null terminate the buffer for us when we pass
274 // the buffer in. Do that manually.
275 buffer[stringLen] = '\0';
277 aString.SetLength(stringLen);
278 return NS_OK;
281 aString.Truncate();
282 return NS_ERROR_INVALID_ARG;
285 nsresult
286 Base64Encode(const nsAString& aString, nsAString& aBinaryData)
288 NS_LossyConvertUTF16toASCII string(aString);
289 nsAutoCString binaryData;
291 nsresult rv = Base64Encode(string, binaryData);
292 if (NS_SUCCEEDED(rv)) {
293 CopyASCIItoUTF16(binaryData, aBinaryData);
294 } else {
295 aBinaryData.Truncate();
298 return rv;
301 nsresult
302 Base64Decode(const nsACString& aString, nsACString& aBinaryData)
304 // Check for overflow.
305 if (aString.Length() > UINT32_MAX / 3) {
306 return NS_ERROR_FAILURE;
309 // Don't ask PR_Base64Decode to decode the empty string
310 if (aString.IsEmpty()) {
311 aBinaryData.Truncate();
312 return NS_OK;
315 uint32_t binaryDataLen = ((aString.Length() * 3) / 4);
317 char* buffer;
319 // Add one byte for null termination.
320 if (aBinaryData.SetCapacity(binaryDataLen + 1, fallible_t()) &&
321 (buffer = aBinaryData.BeginWriting()) &&
322 PL_Base64Decode(aString.BeginReading(), aString.Length(), buffer)) {
323 // PL_Base64Decode doesn't null terminate the buffer for us when we pass
324 // the buffer in. Do that manually, taking into account the number of '='
325 // characters we were passed.
326 if (!aString.IsEmpty() && aString[aString.Length() - 1] == '=') {
327 if (aString.Length() > 1 && aString[aString.Length() - 2] == '=') {
328 binaryDataLen -= 2;
329 } else {
330 binaryDataLen -= 1;
333 buffer[binaryDataLen] = '\0';
335 aBinaryData.SetLength(binaryDataLen);
336 return NS_OK;
339 aBinaryData.Truncate();
340 return NS_ERROR_INVALID_ARG;
343 nsresult
344 Base64Decode(const nsAString& aBinaryData, nsAString& aString)
346 NS_LossyConvertUTF16toASCII binaryData(aBinaryData);
347 nsAutoCString string;
349 nsresult rv = Base64Decode(binaryData, string);
350 if (NS_SUCCEEDED(rv)) {
351 CopyASCIItoUTF16(string, aString);
352 } else {
353 aString.Truncate();
356 return rv;
359 } // namespace mozilla