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"
12 #include "nsAutoPtr.h"
14 #include "nsStreamUtils.h"
15 #include "nsUTF8Utils.h"
16 #include "mozilla/Attributes.h"
24 #define STRING_BUFFER_SIZE 8192
26 class StringUnicharInputStream final
: public nsIUnicharInputStream
29 explicit StringUnicharInputStream(const nsAString
& aString
) :
30 mString(aString
), mPos(0), mLen(aString
.Length()) { }
33 NS_DECL_NSIUNICHARINPUTSTREAM
40 ~StringUnicharInputStream() { }
44 StringUnicharInputStream::Read(char16_t
* aBuf
,
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
) {
59 memcpy(aBuf
, us
+ mPos
, sizeof(char16_t
) * amount
);
66 StringUnicharInputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter
,
68 uint32_t aCount
, uint32_t* aReadCount
)
70 uint32_t bytesWritten
;
71 uint32_t totalBytesWritten
= 0;
74 aCount
= XPCOM_MIN(mString
.Length() - mPos
, aCount
);
76 nsAString::const_iterator iter
;
77 mString
.BeginReading(iter
);
80 rv
= aWriter(this, aClosure
, iter
.get() + mPos
,
81 totalBytesWritten
, aCount
, &bytesWritten
);
84 // don't propagate errors to the caller
88 aCount
-= bytesWritten
;
89 totalBytesWritten
+= bytesWritten
;
93 *aReadCount
= totalBytesWritten
;
99 StringUnicharInputStream::ReadString(uint32_t aCount
, nsAString
& aString
,
100 uint32_t* aReadCount
)
106 uint32_t amount
= mLen
- mPos
;
107 if (amount
> aCount
) {
110 aString
= Substring(mString
, mPos
, amount
);
112 *aReadCount
= amount
;
117 StringUnicharInputStream::Close()
123 NS_IMPL_ISUPPORTS(StringUnicharInputStream
, nsIUnicharInputStream
)
125 //----------------------------------------------------------------------
127 class UTF8InputStream final
: public nsIUnicharInputStream
131 nsresult
Init(nsIInputStream
* aStream
);
134 NS_DECL_NSIUNICHARINPUTSTREAM
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() :
157 mUnicharDataOffset(0),
158 mUnicharDataLength(0)
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
;
174 NS_IMPL_ISUPPORTS(UTF8InputStream
, nsIUnicharInputStream
)
176 UTF8InputStream::~UTF8InputStream()
182 UTF8InputStream::Close()
186 mUnicharData
.Clear();
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
;
196 if (0 == readCount
) {
197 // Fill the unichar buffer
198 int32_t bytesRead
= Fill(&errorCode
);
199 if (bytesRead
<= 0) {
203 readCount
= bytesRead
;
205 if (readCount
> aCount
) {
208 memcpy(aBuf
, mUnicharData
.Elements() + mUnicharDataOffset
,
209 readCount
* sizeof(char16_t
));
210 mUnicharDataOffset
+= readCount
;
211 *aReadCount
= readCount
;
216 UTF8InputStream::ReadSegments(nsWriteUnicharSegmentFun aWriter
,
218 uint32_t aCount
, uint32_t* aReadCount
)
220 NS_ASSERTION(mUnicharDataLength
>= mUnicharDataOffset
, "unsigned madness");
221 uint32_t bytesToWrite
= mUnicharDataLength
- mUnicharDataOffset
;
223 if (0 == bytesToWrite
) {
224 // Fill the unichar buffer
225 int32_t bytesRead
= Fill(&rv
);
226 if (bytesRead
<= 0) {
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
);
246 // don't propagate errors to the caller
250 bytesToWrite
-= bytesWritten
;
251 totalBytesWritten
+= bytesWritten
;
252 mUnicharDataOffset
+= bytesWritten
;
255 *aReadCount
= totalBytesWritten
;
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
;
267 if (0 == readCount
) {
268 // Fill the unichar buffer
269 int32_t bytesRead
= Fill(&errorCode
);
270 if (bytesRead
<= 0) {
274 readCount
= bytesRead
;
276 if (readCount
> aCount
) {
279 const char16_t
* buf
= mUnicharData
.Elements() + mUnicharDataOffset
;
280 aString
.Assign(buf
, readCount
);
282 mUnicharDataOffset
+= readCount
;
283 *aReadCount
= readCount
;
288 UTF8InputStream::Fill(nsresult
* aErrorCode
)
291 // We already closed the stream!
292 *aErrorCode
= NS_BASE_STREAM_CLOSED
;
296 NS_ASSERTION(mByteData
.Length() >= mByteDataOffset
, "unsigned madness");
297 uint32_t remainder
= mByteData
.Length() - mByteDataOffset
;
298 mByteDataOffset
= remainder
;
300 *aErrorCode
= NS_FillArray(mByteData
, mInput
, remainder
, &nb
);
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
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
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()) {
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
;
335 mUnicharDataOffset
= 0;
336 mUnicharDataLength
= dstLen
;
337 mByteDataOffset
= srcLen
;
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
) {
355 if (UTF8traits::isASCII(*c
)) {
357 } else if (UTF8traits::is2byte(*c
)) {
359 } else if (UTF8traits::is3byte(*c
)) {
361 } else if (UTF8traits::is4byte(*c
)) {
363 utf16length
++; // add 1 more because this will be converted to a
365 } else if (UTF8traits::is5byte(*c
)) {
367 } else if (UTF8traits::is6byte(*c
)) {
370 NS_WARNING("Unrecognized UTF8 string in UTF8InputStream::CountValidUTF8Bytes()");
371 break; // Otherwise we go into an infinite loop. But what happens now?
379 aValidUTF8bytes
= c
- aBuffer
;
380 aValidUTF16CodeUnits
= utf16length
;
383 NS_IMPL_QUERY_INTERFACE(nsSimpleUnicharStreamFactory
,
385 nsISimpleUnicharStreamFactory
)
387 NS_IMETHODIMP_(MozExternalRefCountType
)
388 nsSimpleUnicharStreamFactory::AddRef()
392 NS_IMETHODIMP_(MozExternalRefCountType
)
393 nsSimpleUnicharStreamFactory::Release()
399 nsSimpleUnicharStreamFactory::CreateInstance(nsISupports
* aOuter
, REFNSIID aIID
,
402 return NS_ERROR_NOT_IMPLEMENTED
;
406 nsSimpleUnicharStreamFactory::LockFactory(bool aLock
)
412 nsSimpleUnicharStreamFactory::CreateInstanceFromString(const nsAString
& aString
,
413 nsIUnicharInputStream
** aResult
)
415 StringUnicharInputStream
* it
= new StringUnicharInputStream(aString
);
417 return NS_ERROR_OUT_OF_MEMORY
;
420 NS_ADDREF(*aResult
= it
);
425 nsSimpleUnicharStreamFactory::CreateInstanceFromUTF8Stream(
426 nsIInputStream
* aStreamToWrap
,
427 nsIUnicharInputStream
** aResult
)
431 // Create converter input stream
432 nsRefPtr
<UTF8InputStream
> it
= new UTF8InputStream();
434 return NS_ERROR_OUT_OF_MEMORY
;
437 nsresult rv
= it
->Init(aStreamToWrap
);
442 NS_ADDREF(*aResult
= it
);
446 nsSimpleUnicharStreamFactory
*
447 nsSimpleUnicharStreamFactory::GetInstance()
449 static const nsSimpleUnicharStreamFactory kInstance
;
450 return const_cast<nsSimpleUnicharStreamFactory
*>(&kInstance
);