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/. */
9 #include "nsIInputStream.h"
16 // BEGIN base64 encode code copied and modified from NSPR
17 const unsigned char* base
=
18 (unsigned char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
19 "abcdefghijklmnopqrstuvwxyz"
24 Encode3to4(const unsigned char* aSrc
, T
* aDest
)
26 uint32_t b32
= (uint32_t)0;
29 for (i
= 0; i
< 3; ++i
) {
31 b32
|= (uint32_t)aSrc
[i
];
34 for (i
= 0; i
< 4; ++i
) {
35 aDest
[i
] = base
[(uint32_t)((b32
>> j
) & 0x3F)];
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)'=';
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)'=';
62 Encode(const unsigned char* aSrc
, uint32_t aSrcLen
, T
* aDest
)
64 while (aSrcLen
>= 3) {
65 Encode3to4(aSrc
, aDest
);
73 Encode2to4(aSrc
, aDest
);
76 Encode1to4(aSrc
, aDest
);
81 NS_NOTREACHED("coding error");
85 // END base64 encode code copied and modified from NSPR.
88 struct EncodeInputStream_State
92 typename
T::char_type
* buffer
;
97 EncodeInputStream_Encoder(nsIInputStream
* aStream
,
99 const char* aFromSegment
,
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';
119 } else /* state->charsOnStack == 2 */ {
120 firstSet
[0] = state
->c
[0];
121 firstSet
[1] = state
->c
[1];
122 firstSet
[2] = src
[0];
125 Encode(firstSet
, 3, state
->buffer
);
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;
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
;
157 EncodeInputStream(nsIInputStream
* aInputStream
,
163 uint64_t count64
= aCount
;
166 rv
= aInputStream
->Available(&count64
);
167 if (NS_WARN_IF(NS_FAILED(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
;
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;
191 state
.buffer
= aOffset
+ aDest
.BeginWriting();
196 rv
= aInputStream
->ReadSegments(&EncodeInputStream_Encoder
<T
>,
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!");
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';
229 } // namespace (anonymous)
234 Base64EncodeInputStream(nsIInputStream
* aInputStream
,
239 return EncodeInputStream
<nsACString
>(aInputStream
, aDest
, aCount
, aOffset
);
243 Base64EncodeInputStream(nsIInputStream
* aInputStream
,
248 return EncodeInputStream
<nsAString
>(aInputStream
, aDest
, aCount
, aOffset
);
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()) {
265 uint32_t stringLen
= ((aBinaryData
.Length() + 2) / 3) * 4;
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
);
282 return NS_ERROR_INVALID_ARG
;
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
);
295 aBinaryData
.Truncate();
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();
315 uint32_t binaryDataLen
= ((aString
.Length() * 3) / 4);
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] == '=') {
333 buffer
[binaryDataLen
] = '\0';
335 aBinaryData
.SetLength(binaryDataLen
);
339 aBinaryData
.Truncate();
340 return NS_ERROR_INVALID_ARG
;
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
);
359 } // namespace mozilla