Bumping manifests a=b2g-bump
[gecko.git] / xpcom / io / nsUnicharInputStream.cpp
blob59c4456f19a22e6068cc329173c661d2ae4ab0bf
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 "nsUnicharInputStream.h"
8 #include "nsIInputStream.h"
9 #include "nsIServiceManager.h"
10 #include "nsString.h"
11 #include "nsTArray.h"
12 #include "nsAutoPtr.h"
13 #include "nsCRT.h"
14 #include "nsStreamUtils.h"
15 #include "nsUTF8Utils.h"
16 #include "mozilla/Attributes.h"
17 #include <fcntl.h>
18 #if defined(XP_WIN)
19 #include <io.h>
20 #else
21 #include <unistd.h>
22 #endif
24 #define STRING_BUFFER_SIZE 8192
26 class StringUnicharInputStream MOZ_FINAL : public nsIUnicharInputStream
28 public:
29 explicit StringUnicharInputStream(const nsAString& aString) :
30 mString(aString), mPos(0), mLen(aString.Length()) { }
32 NS_DECL_ISUPPORTS
33 NS_DECL_NSIUNICHARINPUTSTREAM
35 nsString mString;
36 uint32_t mPos;
37 uint32_t mLen;
39 private:
40 ~StringUnicharInputStream() { }
43 NS_IMETHODIMP
44 StringUnicharInputStream::Read(char16_t* aBuf,
45 uint32_t aCount,
46 uint32_t* aReadCount)
48 if (mPos >= mLen) {
49 *aReadCount = 0;
50 return NS_OK;
52 nsAString::const_iterator iter;
53 mString.BeginReading(iter);
54 const char16_t* us = iter.get();
55 uint32_t amount = mLen - mPos;
56 if (amount > aCount) {
57 amount = aCount;
59 memcpy(aBuf, us + mPos, sizeof(char16_t) * amount);
60 mPos += amount;
61 *aReadCount = amount;
62 return NS_OK;
65 NS_IMETHODIMP
66 StringUnicharInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
67 void* aClosure,
68 uint32_t aCount, uint32_t* aReadCount)
70 uint32_t bytesWritten;
71 uint32_t totalBytesWritten = 0;
73 nsresult rv;
74 aCount = XPCOM_MIN(mString.Length() - mPos, aCount);
76 nsAString::const_iterator iter;
77 mString.BeginReading(iter);
79 while (aCount) {
80 rv = aWriter(this, aClosure, iter.get() + mPos,
81 totalBytesWritten, aCount, &bytesWritten);
83 if (NS_FAILED(rv)) {
84 // don't propagate errors to the caller
85 break;
88 aCount -= bytesWritten;
89 totalBytesWritten += bytesWritten;
90 mPos += bytesWritten;
93 *aReadCount = totalBytesWritten;
95 return NS_OK;
98 NS_IMETHODIMP
99 StringUnicharInputStream::ReadString(uint32_t aCount, nsAString& aString,
100 uint32_t* aReadCount)
102 if (mPos >= mLen) {
103 *aReadCount = 0;
104 return NS_OK;
106 uint32_t amount = mLen - mPos;
107 if (amount > aCount) {
108 amount = aCount;
110 aString = Substring(mString, mPos, amount);
111 mPos += amount;
112 *aReadCount = amount;
113 return NS_OK;
116 nsresult
117 StringUnicharInputStream::Close()
119 mPos = mLen;
120 return NS_OK;
123 NS_IMPL_ISUPPORTS(StringUnicharInputStream, nsIUnicharInputStream)
125 //----------------------------------------------------------------------
127 class UTF8InputStream MOZ_FINAL : public nsIUnicharInputStream
129 public:
130 UTF8InputStream();
131 nsresult Init(nsIInputStream* aStream);
133 NS_DECL_ISUPPORTS
134 NS_DECL_NSIUNICHARINPUTSTREAM
136 private:
137 ~UTF8InputStream();
139 protected:
140 int32_t Fill(nsresult* aErrorCode);
142 static void CountValidUTF8Bytes(const char* aBuf, uint32_t aMaxBytes,
143 uint32_t& aValidUTF8bytes,
144 uint32_t& aValidUTF16CodeUnits);
146 nsCOMPtr<nsIInputStream> mInput;
147 FallibleTArray<char> mByteData;
148 FallibleTArray<char16_t> mUnicharData;
150 uint32_t mByteDataOffset;
151 uint32_t mUnicharDataOffset;
152 uint32_t mUnicharDataLength;
155 UTF8InputStream::UTF8InputStream() :
156 mByteDataOffset(0),
157 mUnicharDataOffset(0),
158 mUnicharDataLength(0)
162 nsresult
163 UTF8InputStream::Init(nsIInputStream* aStream)
165 if (!mByteData.SetCapacity(STRING_BUFFER_SIZE) ||
166 !mUnicharData.SetCapacity(STRING_BUFFER_SIZE)) {
167 return NS_ERROR_OUT_OF_MEMORY;
169 mInput = aStream;
171 return NS_OK;
174 NS_IMPL_ISUPPORTS(UTF8InputStream, nsIUnicharInputStream)
176 UTF8InputStream::~UTF8InputStream()
178 Close();
181 nsresult
182 UTF8InputStream::Close()
184 mInput = nullptr;
185 mByteData.Clear();
186 mUnicharData.Clear();
187 return NS_OK;
190 nsresult
191 UTF8InputStream::Read(char16_t* aBuf, uint32_t aCount, uint32_t* aReadCount)
193 NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
194 uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
195 nsresult errorCode;
196 if (0 == readCount) {
197 // Fill the unichar buffer
198 int32_t bytesRead = Fill(&errorCode);
199 if (bytesRead <= 0) {
200 *aReadCount = 0;
201 return errorCode;
203 readCount = bytesRead;
205 if (readCount > aCount) {
206 readCount = aCount;
208 memcpy(aBuf, mUnicharData.Elements() + mUnicharDataOffset,
209 readCount * sizeof(char16_t));
210 mUnicharDataOffset += readCount;
211 *aReadCount = readCount;
212 return NS_OK;
215 NS_IMETHODIMP
216 UTF8InputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter,
217 void* aClosure,
218 uint32_t aCount, uint32_t* aReadCount)
220 NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
221 uint32_t bytesToWrite = mUnicharDataLength - mUnicharDataOffset;
222 nsresult rv = NS_OK;
223 if (0 == bytesToWrite) {
224 // Fill the unichar buffer
225 int32_t bytesRead = Fill(&rv);
226 if (bytesRead <= 0) {
227 *aReadCount = 0;
228 return rv;
230 bytesToWrite = bytesRead;
233 if (bytesToWrite > aCount) {
234 bytesToWrite = aCount;
237 uint32_t bytesWritten;
238 uint32_t totalBytesWritten = 0;
240 while (bytesToWrite) {
241 rv = aWriter(this, aClosure,
242 mUnicharData.Elements() + mUnicharDataOffset,
243 totalBytesWritten, bytesToWrite, &bytesWritten);
245 if (NS_FAILED(rv)) {
246 // don't propagate errors to the caller
247 break;
250 bytesToWrite -= bytesWritten;
251 totalBytesWritten += bytesWritten;
252 mUnicharDataOffset += bytesWritten;
255 *aReadCount = totalBytesWritten;
257 return NS_OK;
260 NS_IMETHODIMP
261 UTF8InputStream::ReadString(uint32_t aCount, nsAString& aString,
262 uint32_t* aReadCount)
264 NS_ASSERTION(mUnicharDataLength >= mUnicharDataOffset, "unsigned madness");
265 uint32_t readCount = mUnicharDataLength - mUnicharDataOffset;
266 nsresult errorCode;
267 if (0 == readCount) {
268 // Fill the unichar buffer
269 int32_t bytesRead = Fill(&errorCode);
270 if (bytesRead <= 0) {
271 *aReadCount = 0;
272 return errorCode;
274 readCount = bytesRead;
276 if (readCount > aCount) {
277 readCount = aCount;
279 const char16_t* buf = mUnicharData.Elements() + mUnicharDataOffset;
280 aString.Assign(buf, readCount);
282 mUnicharDataOffset += readCount;
283 *aReadCount = readCount;
284 return NS_OK;
287 int32_t
288 UTF8InputStream::Fill(nsresult* aErrorCode)
290 if (!mInput) {
291 // We already closed the stream!
292 *aErrorCode = NS_BASE_STREAM_CLOSED;
293 return -1;
296 NS_ASSERTION(mByteData.Length() >= mByteDataOffset, "unsigned madness");
297 uint32_t remainder = mByteData.Length() - mByteDataOffset;
298 mByteDataOffset = remainder;
299 uint32_t nb;
300 *aErrorCode = NS_FillArray(mByteData, mInput, remainder, &nb);
301 if (nb == 0) {
302 // Because we assume a many to one conversion, the lingering data
303 // in the byte buffer must be a partial conversion
304 // fragment. Because we know that we have received no more new
305 // data to add to it, we can't convert it. Therefore, we discard
306 // it.
307 return nb;
309 NS_ASSERTION(remainder + nb == mByteData.Length(), "bad nb");
311 // Now convert as much of the byte buffer to unicode as possible
312 uint32_t srcLen, dstLen;
313 CountValidUTF8Bytes(mByteData.Elements(), remainder + nb, srcLen, dstLen);
315 // the number of UCS2 characters should always be <= the number of
316 // UTF8 chars
317 NS_ASSERTION(remainder + nb >= srcLen, "cannot be longer than out buffer");
318 NS_ASSERTION(dstLen <= mUnicharData.Capacity(),
319 "Ouch. I would overflow my buffer if I wasn't so careful.");
320 if (dstLen > mUnicharData.Capacity()) {
321 return 0;
324 ConvertUTF8toUTF16 converter(mUnicharData.Elements());
326 nsASingleFragmentCString::const_char_iterator start = mByteData.Elements();
327 nsASingleFragmentCString::const_char_iterator end = mByteData.Elements() + srcLen;
329 copy_string(start, end, converter);
330 if (converter.Length() != dstLen) {
331 *aErrorCode = NS_BASE_STREAM_BAD_CONVERSION;
332 return -1;
335 mUnicharDataOffset = 0;
336 mUnicharDataLength = dstLen;
337 mByteDataOffset = srcLen;
339 return dstLen;
342 void
343 UTF8InputStream::CountValidUTF8Bytes(const char* aBuffer, uint32_t aMaxBytes,
344 uint32_t& aValidUTF8bytes,
345 uint32_t& aValidUTF16CodeUnits)
347 const char* c = aBuffer;
348 const char* end = aBuffer + aMaxBytes;
349 const char* lastchar = c; // pre-initialize in case of 0-length buffer
350 uint32_t utf16length = 0;
351 while (c < end && *c) {
352 lastchar = c;
353 utf16length++;
355 if (UTF8traits::isASCII(*c)) {
356 c++;
357 } else if (UTF8traits::is2byte(*c)) {
358 c += 2;
359 } else if (UTF8traits::is3byte(*c)) {
360 c += 3;
361 } else if (UTF8traits::is4byte(*c)) {
362 c += 4;
363 utf16length++; // add 1 more because this will be converted to a
364 // surrogate pair.
365 } else if (UTF8traits::is5byte(*c)) {
366 c += 5;
367 } else if (UTF8traits::is6byte(*c)) {
368 c += 6;
369 } else {
370 NS_WARNING("Unrecognized UTF8 string in UTF8InputStream::CountValidUTF8Bytes()");
371 break; // Otherwise we go into an infinite loop. But what happens now?
374 if (c > end) {
375 c = lastchar;
376 utf16length--;
379 aValidUTF8bytes = c - aBuffer;
380 aValidUTF16CodeUnits = utf16length;
383 NS_IMPL_QUERY_INTERFACE(nsSimpleUnicharStreamFactory,
384 nsIFactory,
385 nsISimpleUnicharStreamFactory)
387 NS_IMETHODIMP_(MozExternalRefCountType)
388 nsSimpleUnicharStreamFactory::AddRef()
390 return 2;
392 NS_IMETHODIMP_(MozExternalRefCountType)
393 nsSimpleUnicharStreamFactory::Release()
395 return 1;
398 NS_IMETHODIMP
399 nsSimpleUnicharStreamFactory::CreateInstance(nsISupports* aOuter, REFNSIID aIID,
400 void** aResult)
402 return NS_ERROR_NOT_IMPLEMENTED;
405 NS_IMETHODIMP
406 nsSimpleUnicharStreamFactory::LockFactory(bool aLock)
408 return NS_OK;
411 NS_IMETHODIMP
412 nsSimpleUnicharStreamFactory::CreateInstanceFromString(const nsAString& aString,
413 nsIUnicharInputStream** aResult)
415 StringUnicharInputStream* it = new StringUnicharInputStream(aString);
416 if (!it) {
417 return NS_ERROR_OUT_OF_MEMORY;
420 NS_ADDREF(*aResult = it);
421 return NS_OK;
424 NS_IMETHODIMP
425 nsSimpleUnicharStreamFactory::CreateInstanceFromUTF8Stream(
426 nsIInputStream* aStreamToWrap,
427 nsIUnicharInputStream** aResult)
429 *aResult = nullptr;
431 // Create converter input stream
432 nsRefPtr<UTF8InputStream> it = new UTF8InputStream();
433 if (!it) {
434 return NS_ERROR_OUT_OF_MEMORY;
437 nsresult rv = it->Init(aStreamToWrap);
438 if (NS_FAILED(rv)) {
439 return rv;
442 NS_ADDREF(*aResult = it);
443 return NS_OK;
446 nsSimpleUnicharStreamFactory*
447 nsSimpleUnicharStreamFactory::GetInstance()
449 static const nsSimpleUnicharStreamFactory kInstance;
450 return const_cast<nsSimpleUnicharStreamFactory*>(&kInstance);