Line data Source code
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/. */
5 :
6 : #include "nsConverterInputStream.h"
7 : #include "nsIInputStream.h"
8 : #include "nsReadLine.h"
9 : #include "nsStreamUtils.h"
10 : #include <algorithm>
11 : #include "mozilla/Unused.h"
12 :
13 : using namespace mozilla;
14 :
15 : #define CONVERTER_BUFFER_SIZE 8192
16 :
17 51 : NS_IMPL_ISUPPORTS(nsConverterInputStream, nsIConverterInputStream,
18 : nsIUnicharInputStream, nsIUnicharLineInputStream)
19 :
20 :
21 : NS_IMETHODIMP
22 3 : nsConverterInputStream::Init(nsIInputStream* aStream,
23 : const char *aCharset,
24 : int32_t aBufferSize,
25 : char16_t aReplacementChar)
26 : {
27 6 : nsAutoCString label;
28 3 : if (!aCharset) {
29 0 : label.AssignLiteral("UTF-8");
30 : } else {
31 3 : label = aCharset;
32 : }
33 :
34 3 : auto encoding = Encoding::ForLabelNoReplacement(label);
35 3 : if (!encoding) {
36 0 : return NS_ERROR_UCONV_NOCONV;
37 : }
38 : // Previously, the implementation auto-switched only
39 : // between the two UTF-16 variants and only when
40 : // initialized with an endianness-unspecific label.
41 3 : mConverter = encoding->NewDecoder();
42 :
43 : size_t outputBufferSize;
44 3 : if (aBufferSize <= 0) {
45 0 : aBufferSize = CONVERTER_BUFFER_SIZE;
46 0 : outputBufferSize = CONVERTER_BUFFER_SIZE;
47 : } else {
48 : // NetUtil.jsm assumes that if buffer size equals
49 : // the input size, the whole stream will be processed
50 : // as one readString. This is not true with encoding_rs,
51 : // because encoding_rs might want to see space for a
52 : // surrogate pair, so let's compute a larger output
53 : // buffer length.
54 3 : CheckedInt<size_t> needed = mConverter->MaxUTF16BufferLength(aBufferSize);
55 3 : if (!needed.isValid()) {
56 0 : return NS_ERROR_OUT_OF_MEMORY;
57 : }
58 3 : outputBufferSize = needed.value();
59 : }
60 :
61 : // set up our buffers.
62 6 : if (!mByteData.SetCapacity(aBufferSize, mozilla::fallible) ||
63 3 : !mUnicharData.SetLength(outputBufferSize, mozilla::fallible)) {
64 0 : return NS_ERROR_OUT_OF_MEMORY;
65 : }
66 :
67 3 : mInput = aStream;
68 3 : mErrorsAreFatal = !aReplacementChar;
69 3 : return NS_OK;
70 : }
71 :
72 : NS_IMETHODIMP
73 3 : nsConverterInputStream::Close()
74 : {
75 3 : nsresult rv = mInput ? mInput->Close() : NS_OK;
76 3 : mLineBuffer = nullptr;
77 3 : mInput = nullptr;
78 3 : mConverter = nullptr;
79 3 : mByteData.Clear();
80 3 : mUnicharData.Clear();
81 3 : return rv;
82 : }
83 :
84 : NS_IMETHODIMP
85 0 : nsConverterInputStream::Read(char16_t* aBuf,
86 : uint32_t aCount,
87 : uint32_t *aReadCount)
88 : {
89 0 : NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
90 0 : uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
91 0 : if (0 == readCount) {
92 : // Fill the unichar buffer
93 0 : readCount = Fill(&mLastErrorCode);
94 0 : if (readCount == 0) {
95 0 : *aReadCount = 0;
96 0 : return mLastErrorCode;
97 : }
98 : }
99 0 : if (readCount > aCount) {
100 0 : readCount = aCount;
101 : }
102 0 : memcpy(aBuf, mUnicharData.Elements() + mUnicharDataOffset,
103 0 : readCount * sizeof(char16_t));
104 0 : mUnicharDataOffset += readCount;
105 0 : *aReadCount = readCount;
106 0 : return NS_OK;
107 : }
108 :
109 : NS_IMETHODIMP
110 0 : nsConverterInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
111 : void* aClosure,
112 : uint32_t aCount, uint32_t *aReadCount)
113 : {
114 0 : NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
115 0 : uint32_t bytesToWrite = mUnicharDataLength - mUnicharDataOffset;
116 : nsresult rv;
117 0 : if (0 == bytesToWrite) {
118 : // Fill the unichar buffer
119 0 : bytesToWrite = Fill(&rv);
120 0 : if (bytesToWrite <= 0) {
121 0 : *aReadCount = 0;
122 0 : return rv;
123 : }
124 : }
125 :
126 0 : if (bytesToWrite > aCount)
127 0 : bytesToWrite = aCount;
128 :
129 : uint32_t bytesWritten;
130 0 : uint32_t totalBytesWritten = 0;
131 :
132 0 : while (bytesToWrite) {
133 0 : rv = aWriter(this, aClosure,
134 0 : mUnicharData.Elements() + mUnicharDataOffset,
135 : totalBytesWritten, bytesToWrite, &bytesWritten);
136 0 : if (NS_FAILED(rv)) {
137 : // don't propagate errors to the caller
138 0 : break;
139 : }
140 :
141 0 : bytesToWrite -= bytesWritten;
142 0 : totalBytesWritten += bytesWritten;
143 0 : mUnicharDataOffset += bytesWritten;
144 : }
145 :
146 0 : *aReadCount = totalBytesWritten;
147 :
148 0 : return NS_OK;
149 : }
150 :
151 : NS_IMETHODIMP
152 3 : nsConverterInputStream::ReadString(uint32_t aCount, nsAString& aString,
153 : uint32_t* aReadCount)
154 : {
155 3 : NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
156 3 : uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
157 3 : if (0 == readCount) {
158 : // Fill the unichar buffer
159 3 : readCount = Fill(&mLastErrorCode);
160 3 : if (readCount == 0) {
161 0 : *aReadCount = 0;
162 0 : return mLastErrorCode;
163 : }
164 : }
165 3 : if (readCount > aCount) {
166 0 : readCount = aCount;
167 : }
168 3 : const char16_t* buf = mUnicharData.Elements() + mUnicharDataOffset;
169 3 : aString.Assign(buf, readCount);
170 3 : mUnicharDataOffset += readCount;
171 3 : *aReadCount = readCount;
172 3 : return NS_OK;
173 : }
174 :
175 : uint32_t
176 3 : nsConverterInputStream::Fill(nsresult * aErrorCode)
177 : {
178 3 : if (nullptr == mInput) {
179 : // We already closed the stream!
180 0 : *aErrorCode = NS_BASE_STREAM_CLOSED;
181 0 : return 0;
182 : }
183 :
184 3 : if (NS_FAILED(mLastErrorCode)) {
185 : // We failed to completely convert last time, and error-recovery
186 : // is disabled. We will fare no better this time, so...
187 0 : *aErrorCode = mLastErrorCode;
188 0 : return 0;
189 : }
190 :
191 : // We assume a many to one conversion and are using equal sizes for
192 : // the two buffers. However if an error happens at the very start
193 : // of a byte buffer we may end up in a situation where n bytes lead
194 : // to n+1 unicode chars. Thus we need to keep track of the leftover
195 : // bytes as we convert.
196 :
197 : uint32_t nb;
198 3 : *aErrorCode = NS_FillArray(mByteData, mInput, mLeftOverBytes, &nb);
199 3 : if (nb == 0 && mLeftOverBytes == 0) {
200 : // No more data
201 0 : *aErrorCode = NS_OK;
202 0 : return 0;
203 : }
204 :
205 3 : NS_ASSERTION(uint32_t(nb) + mLeftOverBytes == mByteData.Length(),
206 : "mByteData is lying to us somewhere");
207 :
208 : // Now convert as much of the byte buffer to unicode as possible
209 3 : auto src = AsBytes(MakeSpan(mByteData));
210 3 : auto dst = MakeSpan(mUnicharData);
211 : // mUnicharData.Length() is the buffer length, not the fill status.
212 : // mUnicharDataLength reflects the current fill status.
213 3 : mUnicharDataLength = 0;
214 : // Whenever we convert, mUnicharData is logically empty.
215 3 : mUnicharDataOffset = 0;
216 : // Truncation from size_t to uint32_t below is OK, because the sizes
217 : // are bounded by the lengths of mByteData and mUnicharData.
218 : uint32_t result;
219 : size_t read;
220 : size_t written;
221 : bool hadErrors;
222 : // The design of this class is fundamentally bogus in that trailing
223 : // errors are ignored. Always passing false as the last argument to
224 : // Decode* calls below.
225 3 : if (mErrorsAreFatal) {
226 6 : Tie(result, read, written) =
227 9 : mConverter->DecodeToUTF16WithoutReplacement(src, dst, false);
228 : } else {
229 0 : Tie(result, read, written, hadErrors) =
230 0 : mConverter->DecodeToUTF16(src, dst, false);
231 : }
232 : Unused << hadErrors;
233 3 : mLeftOverBytes = mByteData.Length() - read;
234 3 : mUnicharDataLength = written;
235 3 : if (result == kInputEmpty || result == kOutputFull) {
236 3 : *aErrorCode = NS_OK;
237 : } else {
238 0 : MOZ_ASSERT(mErrorsAreFatal, "How come DecodeToUTF16() reported error?");
239 0 : *aErrorCode = NS_ERROR_UDEC_ILLEGALINPUT;
240 : }
241 3 : return mUnicharDataLength;
242 : }
243 :
244 : NS_IMETHODIMP
245 0 : nsConverterInputStream::ReadLine(nsAString& aLine, bool* aResult)
246 : {
247 0 : if (!mLineBuffer) {
248 0 : mLineBuffer = new nsLineBuffer<char16_t>;
249 : }
250 0 : return NS_ReadLine(this, mLineBuffer.get(), aLine, aResult);
251 : }
|