Bumping manifests a=b2g-bump
[gecko.git] / intl / uconv / nsConverterInputStream.cpp
blobdaeda3cc0b54983c6ea06ca2eb7d572dc61202e7
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"
10 #include <algorithm>
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)
21 NS_IMETHODIMP
22 nsConverterInputStream::Init(nsIInputStream* aStream,
23 const char *aCharset,
24 int32_t aBufferSize,
25 char16_t aReplacementChar)
27 nsAutoCString label;
28 if (!aCharset) {
29 label.AssignLiteral("UTF-8");
30 } else {
31 label = aCharset;
34 if (aBufferSize <=0) aBufferSize=CONVERTER_BUFFER_SIZE;
36 // get the decoder
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,
42 encoding)) {
43 return NS_ERROR_UCONV_NOCONV;
45 mConverter = EncodingUtils::DecoderForEncoding(encoding);
47 // set up our buffers
48 if (!mByteData.SetCapacity(aBufferSize) ||
49 !mUnicharData.SetCapacity(aBufferSize)) {
50 return NS_ERROR_OUT_OF_MEMORY;
53 mInput = aStream;
54 mReplacementChar = aReplacementChar;
55 if (!aReplacementChar ||
56 aReplacementChar != mConverter->GetCharacterForUnMapped()) {
57 mConverter->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
60 return NS_OK;
63 NS_IMETHODIMP
64 nsConverterInputStream::Close()
66 nsresult rv = mInput ? mInput->Close() : NS_OK;
67 mLineBuffer = nullptr;
68 mInput = nullptr;
69 mConverter = nullptr;
70 mByteData.Clear();
71 mUnicharData.Clear();
72 return rv;
75 NS_IMETHODIMP
76 nsConverterInputStream::Read(char16_t* aBuf,
77 uint32_t aCount,
78 uint32_t *aReadCount)
80 NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
81 uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
82 if (0 == readCount) {
83 // Fill the unichar buffer
84 readCount = Fill(&mLastErrorCode);
85 if (readCount == 0) {
86 *aReadCount = 0;
87 return mLastErrorCode;
90 if (readCount > aCount) {
91 readCount = aCount;
93 memcpy(aBuf, mUnicharData.Elements() + mUnicharDataOffset,
94 readCount * sizeof(char16_t));
95 mUnicharDataOffset += readCount;
96 *aReadCount = readCount;
97 return NS_OK;
100 NS_IMETHODIMP
101 nsConverterInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
102 void* aClosure,
103 uint32_t aCount, uint32_t *aReadCount)
105 NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
106 uint32_t bytesToWrite = mUnicharDataLength - mUnicharDataOffset;
107 nsresult rv;
108 if (0 == bytesToWrite) {
109 // Fill the unichar buffer
110 bytesToWrite = Fill(&rv);
111 if (bytesToWrite <= 0) {
112 *aReadCount = 0;
113 return rv;
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);
127 if (NS_FAILED(rv)) {
128 // don't propagate errors to the caller
129 break;
132 bytesToWrite -= bytesWritten;
133 totalBytesWritten += bytesWritten;
134 mUnicharDataOffset += bytesWritten;
138 *aReadCount = totalBytesWritten;
140 return NS_OK;
143 NS_IMETHODIMP
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) {
153 *aReadCount = 0;
154 return mLastErrorCode;
157 if (readCount > aCount) {
158 readCount = aCount;
160 const char16_t* buf = mUnicharData.Elements() + mUnicharDataOffset;
161 aString.Assign(buf, readCount);
162 mUnicharDataOffset += readCount;
163 *aReadCount = readCount;
164 return NS_OK;
167 uint32_t
168 nsConverterInputStream::Fill(nsresult * aErrorCode)
170 if (nullptr == mInput) {
171 // We already closed the stream!
172 *aErrorCode = NS_BASE_STREAM_CLOSED;
173 return 0;
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;
180 return 0;
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.
189 uint32_t nb;
190 *aErrorCode = NS_FillArray(mByteData, mInput, mLeftOverBytes, &nb);
191 if (nb == 0 && mLeftOverBytes == 0) {
192 // No more data
193 *aErrorCode = NS_OK;
194 return 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;
204 do {
205 int32_t srcLen = mByteData.Length() - srcConsumed;
206 int32_t dstLen = mUnicharData.Capacity() - mUnicharDataLength;
207 *aErrorCode = mConverter->Convert(mByteData.Elements()+srcConsumed,
208 &srcLen,
209 mUnicharData.Elements()+mUnicharDataLength,
210 &dstLen);
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;
221 ++srcConsumed;
222 // XXX this is needed to make sure we don't underrun our buffer;
223 // bug 160784 again
224 srcConsumed = std::max<uint32_t>(srcConsumed, 0);
225 mConverter->Reset();
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;
238 NS_IMETHODIMP
239 nsConverterInputStream::ReadLine(nsAString& aLine, bool* aResult)
241 if (!mLineBuffer) {
242 mLineBuffer = new nsLineBuffer<char16_t>;
244 return NS_ReadLine(this, mLineBuffer.get(), aLine, aResult);