1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsConverterInputStream.h"
7 #include "nsIInputStream.h"
8 #include "nsReadLine.h"
9 #include "nsStreamUtils.h"
11 #include "mozilla/dom/EncodingUtils.h"
13 using mozilla::dom::EncodingUtils
;
15 #define CONVERTER_BUFFER_SIZE 8192
17 NS_IMPL_ISUPPORTS(nsConverterInputStream
, nsIConverterInputStream
,
18 nsIUnicharInputStream
, nsIUnicharLineInputStream
)
22 nsConverterInputStream::Init(nsIInputStream
* aStream
,
25 char16_t aReplacementChar
)
29 label
.AssignLiteral("UTF-8");
34 if (aBufferSize
<=0) aBufferSize
=CONVERTER_BUFFER_SIZE
;
37 nsAutoCString encoding
;
38 if (label
.EqualsLiteral("UTF-16")) {
39 // Compat with old test cases. Unclear if any extensions really care.
40 encoding
.Assign(label
);
41 } else if (!EncodingUtils::FindEncodingForLabelNoReplacement(label
,
43 return NS_ERROR_UCONV_NOCONV
;
45 mConverter
= EncodingUtils::DecoderForEncoding(encoding
);
48 if (!mByteData
.SetCapacity(aBufferSize
) ||
49 !mUnicharData
.SetCapacity(aBufferSize
)) {
50 return NS_ERROR_OUT_OF_MEMORY
;
54 mReplacementChar
= aReplacementChar
;
55 if (!aReplacementChar
||
56 aReplacementChar
!= mConverter
->GetCharacterForUnMapped()) {
57 mConverter
->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal
);
64 nsConverterInputStream::Close()
66 nsresult rv
= mInput
? mInput
->Close() : NS_OK
;
67 mLineBuffer
= nullptr;
76 nsConverterInputStream::Read(char16_t
* aBuf
,
80 NS_ASSERTION(mUnicharDataLength
>= mUnicharDataOffset
, "unsigned madness");
81 uint32_t readCount
= mUnicharDataLength
- mUnicharDataOffset
;
83 // Fill the unichar buffer
84 readCount
= Fill(&mLastErrorCode
);
87 return mLastErrorCode
;
90 if (readCount
> aCount
) {
93 memcpy(aBuf
, mUnicharData
.Elements() + mUnicharDataOffset
,
94 readCount
* sizeof(char16_t
));
95 mUnicharDataOffset
+= readCount
;
96 *aReadCount
= readCount
;
101 nsConverterInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter
,
103 uint32_t aCount
, uint32_t *aReadCount
)
105 NS_ASSERTION(mUnicharDataLength
>= mUnicharDataOffset
, "unsigned madness");
106 uint32_t bytesToWrite
= mUnicharDataLength
- mUnicharDataOffset
;
108 if (0 == bytesToWrite
) {
109 // Fill the unichar buffer
110 bytesToWrite
= Fill(&rv
);
111 if (bytesToWrite
<= 0) {
117 if (bytesToWrite
> aCount
)
118 bytesToWrite
= aCount
;
120 uint32_t bytesWritten
;
121 uint32_t totalBytesWritten
= 0;
123 while (bytesToWrite
) {
124 rv
= aWriter(this, aClosure
,
125 mUnicharData
.Elements() + mUnicharDataOffset
,
126 totalBytesWritten
, bytesToWrite
, &bytesWritten
);
128 // don't propagate errors to the caller
132 bytesToWrite
-= bytesWritten
;
133 totalBytesWritten
+= bytesWritten
;
134 mUnicharDataOffset
+= bytesWritten
;
138 *aReadCount
= totalBytesWritten
;
144 nsConverterInputStream::ReadString(uint32_t aCount
, nsAString
& aString
,
145 uint32_t* aReadCount
)
147 NS_ASSERTION(mUnicharDataLength
>= mUnicharDataOffset
, "unsigned madness");
148 uint32_t readCount
= mUnicharDataLength
- mUnicharDataOffset
;
149 if (0 == readCount
) {
150 // Fill the unichar buffer
151 readCount
= Fill(&mLastErrorCode
);
152 if (readCount
== 0) {
154 return mLastErrorCode
;
157 if (readCount
> aCount
) {
160 const char16_t
* buf
= mUnicharData
.Elements() + mUnicharDataOffset
;
161 aString
.Assign(buf
, readCount
);
162 mUnicharDataOffset
+= readCount
;
163 *aReadCount
= readCount
;
168 nsConverterInputStream::Fill(nsresult
* aErrorCode
)
170 if (nullptr == mInput
) {
171 // We already closed the stream!
172 *aErrorCode
= NS_BASE_STREAM_CLOSED
;
176 if (NS_FAILED(mLastErrorCode
)) {
177 // We failed to completely convert last time, and error-recovery
178 // is disabled. We will fare no better this time, so...
179 *aErrorCode
= mLastErrorCode
;
183 // We assume a many to one conversion and are using equal sizes for
184 // the two buffers. However if an error happens at the very start
185 // of a byte buffer we may end up in a situation where n bytes lead
186 // to n+1 unicode chars. Thus we need to keep track of the leftover
187 // bytes as we convert.
190 *aErrorCode
= NS_FillArray(mByteData
, mInput
, mLeftOverBytes
, &nb
);
191 if (nb
== 0 && mLeftOverBytes
== 0) {
197 NS_ASSERTION(uint32_t(nb
) + mLeftOverBytes
== mByteData
.Length(),
198 "mByteData is lying to us somewhere");
200 // Now convert as much of the byte buffer to unicode as possible
201 mUnicharDataOffset
= 0;
202 mUnicharDataLength
= 0;
203 uint32_t srcConsumed
= 0;
205 int32_t srcLen
= mByteData
.Length() - srcConsumed
;
206 int32_t dstLen
= mUnicharData
.Capacity() - mUnicharDataLength
;
207 *aErrorCode
= mConverter
->Convert(mByteData
.Elements()+srcConsumed
,
209 mUnicharData
.Elements()+mUnicharDataLength
,
211 mUnicharDataLength
+= dstLen
;
212 // XXX if srcLen is negative, we want to drop the _first_ byte in
213 // the erroneous byte sequence and try again. This is not quite
214 // possible right now -- see bug 160784
215 srcConsumed
+= srcLen
;
216 if (NS_FAILED(*aErrorCode
) && mReplacementChar
) {
217 NS_ASSERTION(0 < mUnicharData
.Capacity() - mUnicharDataLength
,
218 "Decoder returned an error but filled the output buffer! "
219 "Should not happen.");
220 mUnicharData
.Elements()[mUnicharDataLength
++] = mReplacementChar
;
222 // XXX this is needed to make sure we don't underrun our buffer;
224 srcConsumed
= std::max
<uint32_t>(srcConsumed
, 0);
227 NS_ASSERTION(srcConsumed
<= mByteData
.Length(),
228 "Whoa. The converter should have returned NS_OK_UDEC_MOREINPUT before this point!");
229 } while (mReplacementChar
&&
230 NS_FAILED(*aErrorCode
) &&
231 mUnicharData
.Capacity() > mUnicharDataLength
);
233 mLeftOverBytes
= mByteData
.Length() - srcConsumed
;
235 return mUnicharDataLength
;
239 nsConverterInputStream::ReadLine(nsAString
& aLine
, bool* aResult
)
242 mLineBuffer
= new nsLineBuffer
<char16_t
>;
244 return NS_ReadLine(this, mLineBuffer
.get(), aLine
, aResult
);