1 /* vim:set expandtab ts=4 sw=2 sts=2 cin: */
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 "nsCOMPtr.h"
7 
8 #include "nsIOutputStream.h"
9 #include "nsString.h"
10 
11 #include "nsConverterOutputStream.h"
12 #include "mozilla/Encoding.h"
13 #include "mozilla/Unused.h"
14 
15 using namespace mozilla;
16 
NS_IMPL_ISUPPORTS(nsConverterOutputStream,nsIUnicharOutputStream,nsIConverterOutputStream)17 NS_IMPL_ISUPPORTS(nsConverterOutputStream, nsIUnicharOutputStream,
18                   nsIConverterOutputStream)
19 
20 nsConverterOutputStream::~nsConverterOutputStream() { Close(); }
21 
22 NS_IMETHODIMP
Init(nsIOutputStream * aOutStream,const char * aCharset)23 nsConverterOutputStream::Init(nsIOutputStream* aOutStream,
24                               const char* aCharset) {
25   MOZ_ASSERT(aOutStream, "Null output stream!");
26 
27   const Encoding* encoding;
28   if (!aCharset) {
29     encoding = UTF_8_ENCODING;
30   } else {
31     encoding = Encoding::ForLabelNoReplacement(MakeStringSpan(aCharset));
32     if (!encoding || encoding == UTF_16LE_ENCODING ||
33         encoding == UTF_16BE_ENCODING) {
34       return NS_ERROR_UCONV_NOCONV;
35     }
36   }
37 
38   mConverter = encoding->NewEncoder();
39 
40   mOutStream = aOutStream;
41 
42   return NS_OK;
43 }
44 
45 NS_IMETHODIMP
Write(uint32_t aCount,const char16_t * aChars,bool * aSuccess)46 nsConverterOutputStream::Write(uint32_t aCount, const char16_t* aChars,
47                                bool* aSuccess) {
48   if (!mOutStream) {
49     NS_ASSERTION(!mConverter, "Closed streams shouldn't have converters");
50     return NS_BASE_STREAM_CLOSED;
51   }
52   MOZ_ASSERT(mConverter, "Must have a converter when not closed");
53   uint8_t buffer[4096];
54   auto dst = Span(buffer);
55   auto src = Span(aChars, aCount);
56   for (;;) {
57     uint32_t result;
58     size_t read;
59     size_t written;
60     bool hadErrors;
61     Tie(result, read, written, hadErrors) =
62         mConverter->EncodeFromUTF16(src, dst, false);
63     Unused << hadErrors;
64     src = src.From(read);
65     uint32_t streamWritten;
66     nsresult rv = mOutStream->Write(reinterpret_cast<char*>(dst.Elements()),
67                                     written, &streamWritten);
68     *aSuccess = NS_SUCCEEDED(rv) && written == streamWritten;
69     if (!(*aSuccess)) {
70       return rv;
71     }
72     if (result == kInputEmpty) {
73       return NS_OK;
74     }
75   }
76 }
77 
78 NS_IMETHODIMP
WriteString(const nsAString & aString,bool * aSuccess)79 nsConverterOutputStream::WriteString(const nsAString& aString, bool* aSuccess) {
80   int32_t inLen = aString.Length();
81   nsAString::const_iterator i;
82   aString.BeginReading(i);
83   return Write(inLen, i.get(), aSuccess);
84 }
85 
86 NS_IMETHODIMP
Flush()87 nsConverterOutputStream::Flush() {
88   if (!mOutStream) return NS_OK;  // Already closed.
89 
90   // If we are encoding to ISO-2022-JP, potentially
91   // transition back to the ASCII state. The buffer
92   // needs to be large enough for an additional NCR,
93   // though.
94   uint8_t buffer[12];
95   auto dst = Span(buffer);
96   Span<char16_t> src(nullptr);
97   uint32_t result;
98   size_t read;
99   size_t written;
100   bool hadErrors;
101   Tie(result, read, written, hadErrors) =
102       mConverter->EncodeFromUTF16(src, dst, true);
103   Unused << hadErrors;
104   MOZ_ASSERT(result == kInputEmpty);
105   uint32_t streamWritten;
106   if (!written) {
107     return NS_OK;
108   }
109   return mOutStream->Write(reinterpret_cast<char*>(dst.Elements()), written,
110                            &streamWritten);
111 }
112 
113 NS_IMETHODIMP
Close()114 nsConverterOutputStream::Close() {
115   if (!mOutStream) return NS_OK;  // Already closed.
116 
117   nsresult rv1 = Flush();
118 
119   nsresult rv2 = mOutStream->Close();
120   mOutStream = nullptr;
121   mConverter = nullptr;
122   return NS_FAILED(rv1) ? rv1 : rv2;
123 }
124