/src/mozilla-central/xpcom/io/nsStringStream.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | /** |
8 | | * Based on original code from nsIStringStream.cpp |
9 | | */ |
10 | | |
11 | | #include "ipc/IPCMessageUtils.h" |
12 | | |
13 | | #include "nsStringStream.h" |
14 | | #include "nsStreamUtils.h" |
15 | | #include "nsReadableUtils.h" |
16 | | #include "nsICloneableInputStream.h" |
17 | | #include "nsISeekableStream.h" |
18 | | #include "nsISupportsPrimitives.h" |
19 | | #include "nsCRT.h" |
20 | | #include "prerror.h" |
21 | | #include "plstr.h" |
22 | | #include "nsIClassInfoImpl.h" |
23 | | #include "mozilla/Attributes.h" |
24 | | #include "mozilla/ipc/InputStreamUtils.h" |
25 | | #include "nsIIPCSerializableInputStream.h" |
26 | | |
27 | | using namespace mozilla::ipc; |
28 | | using mozilla::Maybe; |
29 | | using mozilla::Some; |
30 | | |
31 | | //----------------------------------------------------------------------------- |
32 | | // nsIStringInputStream implementation |
33 | | //----------------------------------------------------------------------------- |
34 | | |
35 | | class nsStringInputStream final |
36 | | : public nsIStringInputStream |
37 | | , public nsISeekableStream |
38 | | , public nsISupportsCString |
39 | | , public nsIIPCSerializableInputStream |
40 | | , public nsICloneableInputStream |
41 | | { |
42 | | public: |
43 | | NS_DECL_THREADSAFE_ISUPPORTS |
44 | | NS_DECL_NSIINPUTSTREAM |
45 | | NS_DECL_NSISTRINGINPUTSTREAM |
46 | | NS_DECL_NSISEEKABLESTREAM |
47 | | NS_DECL_NSISUPPORTSPRIMITIVE |
48 | | NS_DECL_NSISUPPORTSCSTRING |
49 | | NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM |
50 | | NS_DECL_NSICLONEABLEINPUTSTREAM |
51 | | |
52 | | nsStringInputStream() |
53 | | : mOffset(0) |
54 | 0 | { |
55 | 0 | Clear(); |
56 | 0 | } |
57 | | |
58 | | nsresult Init(nsCString&& aString); |
59 | | |
60 | | private: |
61 | | ~nsStringInputStream() |
62 | 0 | { |
63 | 0 | } |
64 | | |
65 | | uint32_t Length() const |
66 | 0 | { |
67 | 0 | return mData.Length(); |
68 | 0 | } |
69 | | |
70 | | uint32_t LengthRemaining() const |
71 | 0 | { |
72 | 0 | return Length() - mOffset; |
73 | 0 | } |
74 | | |
75 | | void Clear() |
76 | 0 | { |
77 | 0 | mData.SetIsVoid(true); |
78 | 0 | } |
79 | | |
80 | | bool Closed() |
81 | 0 | { |
82 | 0 | return mData.IsVoid(); |
83 | 0 | } |
84 | | |
85 | | nsDependentCSubstring mData; |
86 | | uint32_t mOffset; |
87 | | }; |
88 | | |
89 | | nsresult |
90 | | nsStringInputStream::Init(nsCString&& aString) |
91 | 0 | { |
92 | 0 | if (!mData.Assign(std::move(aString), fallible)) { |
93 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
94 | 0 | } |
95 | 0 | |
96 | 0 | mOffset = 0; |
97 | 0 | return NS_OK; |
98 | 0 | } |
99 | | |
100 | | // This class needs to support threadsafe refcounting since people often |
101 | | // allocate a string stream, and then read it from a background thread. |
102 | | NS_IMPL_ADDREF(nsStringInputStream) |
103 | | NS_IMPL_RELEASE(nsStringInputStream) |
104 | | |
105 | | NS_IMPL_CLASSINFO(nsStringInputStream, nullptr, nsIClassInfo::THREADSAFE, |
106 | | NS_STRINGINPUTSTREAM_CID) |
107 | | NS_IMPL_QUERY_INTERFACE_CI(nsStringInputStream, |
108 | | nsIStringInputStream, |
109 | | nsIInputStream, |
110 | | nsISupportsCString, |
111 | | nsISeekableStream, |
112 | | nsIIPCSerializableInputStream, |
113 | | nsICloneableInputStream) |
114 | | NS_IMPL_CI_INTERFACE_GETTER(nsStringInputStream, |
115 | | nsIStringInputStream, |
116 | | nsIInputStream, |
117 | | nsISupportsCString, |
118 | | nsISeekableStream, |
119 | | nsICloneableInputStream) |
120 | | |
121 | | ///////// |
122 | | // nsISupportsCString implementation |
123 | | ///////// |
124 | | |
125 | | NS_IMETHODIMP |
126 | | nsStringInputStream::GetType(uint16_t* aType) |
127 | 0 | { |
128 | 0 | *aType = TYPE_CSTRING; |
129 | 0 | return NS_OK; |
130 | 0 | } |
131 | | |
132 | | NS_IMETHODIMP |
133 | | nsStringInputStream::GetData(nsACString& data) |
134 | 0 | { |
135 | 0 | // The stream doesn't have any data when it is closed. We could fake it |
136 | 0 | // and return an empty string here, but it seems better to keep this return |
137 | 0 | // value consistent with the behavior of the other 'getter' methods. |
138 | 0 | if (NS_WARN_IF(Closed())) { |
139 | 0 | return NS_BASE_STREAM_CLOSED; |
140 | 0 | } |
141 | 0 | |
142 | 0 | data.Assign(mData); |
143 | 0 | return NS_OK; |
144 | 0 | } |
145 | | |
146 | | NS_IMETHODIMP |
147 | | nsStringInputStream::SetData(const nsACString& aData) |
148 | 0 | { |
149 | 0 | if (NS_WARN_IF(!mData.Assign(aData, fallible))) { |
150 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
151 | 0 | } |
152 | 0 | |
153 | 0 | mOffset = 0; |
154 | 0 | return NS_OK; |
155 | 0 | } |
156 | | |
157 | | NS_IMETHODIMP |
158 | | nsStringInputStream::ToString(char** aResult) |
159 | 0 | { |
160 | 0 | // NOTE: This method may result in data loss, so we do not implement it. |
161 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
162 | 0 | } |
163 | | |
164 | | ///////// |
165 | | // nsIStringInputStream implementation |
166 | | ///////// |
167 | | |
168 | | NS_IMETHODIMP |
169 | | nsStringInputStream::SetData(const char* aData, int32_t aDataLen) |
170 | 0 | { |
171 | 0 | if (NS_WARN_IF(!aData)) { |
172 | 0 | return NS_ERROR_INVALID_ARG; |
173 | 0 | } |
174 | 0 | |
175 | 0 | if (NS_WARN_IF(!mData.Assign(aData, aDataLen, fallible))) { |
176 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
177 | 0 | } |
178 | 0 | |
179 | 0 | mOffset = 0; |
180 | 0 | return NS_OK; |
181 | 0 | } |
182 | | |
183 | | NS_IMETHODIMP |
184 | | nsStringInputStream::AdoptData(char* aData, int32_t aDataLen) |
185 | 0 | { |
186 | 0 | if (NS_WARN_IF(!aData)) { |
187 | 0 | return NS_ERROR_INVALID_ARG; |
188 | 0 | } |
189 | 0 | mData.Adopt(aData, aDataLen); |
190 | 0 | mOffset = 0; |
191 | 0 | return NS_OK; |
192 | 0 | } |
193 | | |
194 | | NS_IMETHODIMP |
195 | | nsStringInputStream::ShareData(const char* aData, int32_t aDataLen) |
196 | 0 | { |
197 | 0 | if (NS_WARN_IF(!aData)) { |
198 | 0 | return NS_ERROR_INVALID_ARG; |
199 | 0 | } |
200 | 0 | |
201 | 0 | if (aDataLen < 0) { |
202 | 0 | aDataLen = strlen(aData); |
203 | 0 | } |
204 | 0 |
|
205 | 0 | mData.Rebind(aData, aDataLen); |
206 | 0 | mOffset = 0; |
207 | 0 | return NS_OK; |
208 | 0 | } |
209 | | |
210 | | NS_IMETHODIMP_(size_t) |
211 | | nsStringInputStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) |
212 | 0 | { |
213 | 0 | size_t n = aMallocSizeOf(this); |
214 | 0 | n += mData.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
215 | 0 | return n; |
216 | 0 | } |
217 | | |
218 | | ///////// |
219 | | // nsIInputStream implementation |
220 | | ///////// |
221 | | |
222 | | NS_IMETHODIMP |
223 | | nsStringInputStream::Close() |
224 | 0 | { |
225 | 0 | Clear(); |
226 | 0 | return NS_OK; |
227 | 0 | } |
228 | | |
229 | | NS_IMETHODIMP |
230 | | nsStringInputStream::Available(uint64_t* aLength) |
231 | 0 | { |
232 | 0 | NS_ASSERTION(aLength, "null ptr"); |
233 | 0 |
|
234 | 0 | if (Closed()) { |
235 | 0 | return NS_BASE_STREAM_CLOSED; |
236 | 0 | } |
237 | 0 | |
238 | 0 | *aLength = LengthRemaining(); |
239 | 0 | return NS_OK; |
240 | 0 | } |
241 | | |
242 | | NS_IMETHODIMP |
243 | | nsStringInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aReadCount) |
244 | 0 | { |
245 | 0 | NS_ASSERTION(aBuf, "null ptr"); |
246 | 0 | return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount); |
247 | 0 | } |
248 | | |
249 | | NS_IMETHODIMP |
250 | | nsStringInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, |
251 | | uint32_t aCount, uint32_t* aResult) |
252 | 0 | { |
253 | 0 | NS_ASSERTION(aResult, "null ptr"); |
254 | 0 | NS_ASSERTION(Length() >= mOffset, "bad stream state"); |
255 | 0 |
|
256 | 0 | if (Closed()) { |
257 | 0 | return NS_BASE_STREAM_CLOSED; |
258 | 0 | } |
259 | 0 | |
260 | 0 | // We may be at end-of-file |
261 | 0 | uint32_t maxCount = LengthRemaining(); |
262 | 0 | if (maxCount == 0) { |
263 | 0 | *aResult = 0; |
264 | 0 | return NS_OK; |
265 | 0 | } |
266 | 0 | |
267 | 0 | if (aCount > maxCount) { |
268 | 0 | aCount = maxCount; |
269 | 0 | } |
270 | 0 | nsresult rv = aWriter(this, aClosure, mData.BeginReading() + mOffset, 0, |
271 | 0 | aCount, aResult); |
272 | 0 | if (NS_SUCCEEDED(rv)) { |
273 | 0 | NS_ASSERTION(*aResult <= aCount, |
274 | 0 | "writer should not write more than we asked it to write"); |
275 | 0 | mOffset += *aResult; |
276 | 0 | } |
277 | 0 |
|
278 | 0 | // errors returned from the writer end here! |
279 | 0 | return NS_OK; |
280 | 0 | } |
281 | | |
282 | | NS_IMETHODIMP |
283 | | nsStringInputStream::IsNonBlocking(bool* aNonBlocking) |
284 | 0 | { |
285 | 0 | *aNonBlocking = true; |
286 | 0 | return NS_OK; |
287 | 0 | } |
288 | | |
289 | | ///////// |
290 | | // nsISeekableStream implementation |
291 | | ///////// |
292 | | |
293 | | NS_IMETHODIMP |
294 | | nsStringInputStream::Seek(int32_t aWhence, int64_t aOffset) |
295 | 0 | { |
296 | 0 | if (Closed()) { |
297 | 0 | return NS_BASE_STREAM_CLOSED; |
298 | 0 | } |
299 | 0 | |
300 | 0 | // Compute new stream position. The given offset may be a negative value. |
301 | 0 | |
302 | 0 | int64_t newPos = aOffset; |
303 | 0 | switch (aWhence) { |
304 | 0 | case NS_SEEK_SET: |
305 | 0 | break; |
306 | 0 | case NS_SEEK_CUR: |
307 | 0 | newPos += mOffset; |
308 | 0 | break; |
309 | 0 | case NS_SEEK_END: |
310 | 0 | newPos += Length(); |
311 | 0 | break; |
312 | 0 | default: |
313 | 0 | NS_ERROR("invalid aWhence"); |
314 | 0 | return NS_ERROR_INVALID_ARG; |
315 | 0 | } |
316 | 0 |
|
317 | 0 | if (NS_WARN_IF(newPos < 0) || NS_WARN_IF(newPos > Length())) { |
318 | 0 | return NS_ERROR_INVALID_ARG; |
319 | 0 | } |
320 | 0 | |
321 | 0 | mOffset = (uint32_t)newPos; |
322 | 0 | return NS_OK; |
323 | 0 | } |
324 | | |
325 | | NS_IMETHODIMP |
326 | | nsStringInputStream::Tell(int64_t* aOutWhere) |
327 | 0 | { |
328 | 0 | if (Closed()) { |
329 | 0 | return NS_BASE_STREAM_CLOSED; |
330 | 0 | } |
331 | 0 | |
332 | 0 | *aOutWhere = mOffset; |
333 | 0 | return NS_OK; |
334 | 0 | } |
335 | | |
336 | | NS_IMETHODIMP |
337 | | nsStringInputStream::SetEOF() |
338 | 0 | { |
339 | 0 | if (Closed()) { |
340 | 0 | return NS_BASE_STREAM_CLOSED; |
341 | 0 | } |
342 | 0 | |
343 | 0 | mOffset = Length(); |
344 | 0 | return NS_OK; |
345 | 0 | } |
346 | | |
347 | | ///////// |
348 | | // nsIIPCSerializableInputStream implementation |
349 | | ///////// |
350 | | |
351 | | void |
352 | | nsStringInputStream::Serialize(InputStreamParams& aParams, |
353 | | FileDescriptorArray& /* aFDs */) |
354 | 0 | { |
355 | 0 | StringInputStreamParams params; |
356 | 0 | params.data() = PromiseFlatCString(mData); |
357 | 0 | aParams = params; |
358 | 0 | } |
359 | | |
360 | | bool |
361 | | nsStringInputStream::Deserialize(const InputStreamParams& aParams, |
362 | | const FileDescriptorArray& /* aFDs */) |
363 | 0 | { |
364 | 0 | if (aParams.type() != InputStreamParams::TStringInputStreamParams) { |
365 | 0 | NS_ERROR("Received unknown parameters from the other process!"); |
366 | 0 | return false; |
367 | 0 | } |
368 | 0 |
|
369 | 0 | const StringInputStreamParams& params = |
370 | 0 | aParams.get_StringInputStreamParams(); |
371 | 0 |
|
372 | 0 | if (NS_FAILED(SetData(params.data()))) { |
373 | 0 | NS_WARNING("SetData failed!"); |
374 | 0 | return false; |
375 | 0 | } |
376 | 0 |
|
377 | 0 | return true; |
378 | 0 | } |
379 | | |
380 | | Maybe<uint64_t> |
381 | | nsStringInputStream::ExpectedSerializedLength() |
382 | 0 | { |
383 | 0 | return Some(static_cast<uint64_t>(Length())); |
384 | 0 | } |
385 | | |
386 | | ///////// |
387 | | // nsICloneableInputStream implementation |
388 | | ///////// |
389 | | |
390 | | NS_IMETHODIMP |
391 | | nsStringInputStream::GetCloneable(bool* aCloneableOut) |
392 | 0 | { |
393 | 0 | *aCloneableOut = true; |
394 | 0 | return NS_OK; |
395 | 0 | } |
396 | | |
397 | | NS_IMETHODIMP |
398 | | nsStringInputStream::Clone(nsIInputStream** aCloneOut) |
399 | 0 | { |
400 | 0 | RefPtr<nsStringInputStream> ref = new nsStringInputStream(); |
401 | 0 | nsresult rv = ref->SetData(mData); |
402 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
403 | 0 | return rv; |
404 | 0 | } |
405 | 0 | |
406 | 0 | // mOffset is overwritten by SetData(). |
407 | 0 | ref->mOffset = mOffset; |
408 | 0 |
|
409 | 0 | ref.forget(aCloneOut); |
410 | 0 | return NS_OK; |
411 | 0 | } |
412 | | |
413 | | nsresult |
414 | | NS_NewByteInputStream(nsIInputStream** aStreamResult, |
415 | | const char* aStringToRead, int32_t aLength, |
416 | | nsAssignmentType aAssignment) |
417 | 0 | { |
418 | 0 | MOZ_ASSERT(aStreamResult, "null out ptr"); |
419 | 0 |
|
420 | 0 | RefPtr<nsStringInputStream> stream = new nsStringInputStream(); |
421 | 0 |
|
422 | 0 | nsresult rv; |
423 | 0 | switch (aAssignment) { |
424 | 0 | case NS_ASSIGNMENT_COPY: |
425 | 0 | rv = stream->SetData(aStringToRead, aLength); |
426 | 0 | break; |
427 | 0 | case NS_ASSIGNMENT_DEPEND: |
428 | 0 | rv = stream->ShareData(aStringToRead, aLength); |
429 | 0 | break; |
430 | 0 | case NS_ASSIGNMENT_ADOPT: |
431 | 0 | rv = stream->AdoptData(const_cast<char*>(aStringToRead), aLength); |
432 | 0 | break; |
433 | 0 | default: |
434 | 0 | NS_ERROR("invalid assignment type"); |
435 | 0 | rv = NS_ERROR_INVALID_ARG; |
436 | 0 | } |
437 | 0 |
|
438 | 0 | if (NS_FAILED(rv)) { |
439 | 0 | return rv; |
440 | 0 | } |
441 | 0 | |
442 | 0 | stream.forget(aStreamResult); |
443 | 0 | return NS_OK; |
444 | 0 | } |
445 | | |
446 | | nsresult |
447 | | NS_NewCStringInputStream(nsIInputStream** aStreamResult, |
448 | | const nsACString& aStringToRead) |
449 | 0 | { |
450 | 0 | MOZ_ASSERT(aStreamResult, "null out ptr"); |
451 | 0 |
|
452 | 0 | RefPtr<nsStringInputStream> stream = new nsStringInputStream(); |
453 | 0 |
|
454 | 0 | nsresult rv = stream->SetData(aStringToRead); |
455 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
456 | 0 | return rv; |
457 | 0 | } |
458 | 0 | |
459 | 0 | stream.forget(aStreamResult); |
460 | 0 | return NS_OK; |
461 | 0 | } |
462 | | |
463 | | nsresult |
464 | | NS_NewCStringInputStream(nsIInputStream** aStreamResult, |
465 | | nsCString&& aStringToRead) |
466 | 0 | { |
467 | 0 | MOZ_ASSERT(aStreamResult, "null out ptr"); |
468 | 0 |
|
469 | 0 | RefPtr<nsStringInputStream> stream = new nsStringInputStream(); |
470 | 0 |
|
471 | 0 | nsresult rv = stream->Init(std::move(aStringToRead)); |
472 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
473 | 0 | return rv; |
474 | 0 | } |
475 | 0 | |
476 | 0 | stream.forget(aStreamResult); |
477 | 0 | return NS_OK; |
478 | 0 | } |
479 | | |
480 | | // factory method for constructing a nsStringInputStream object |
481 | | nsresult |
482 | | nsStringInputStreamConstructor(nsISupports* aOuter, REFNSIID aIID, |
483 | | void** aResult) |
484 | 0 | { |
485 | 0 | *aResult = nullptr; |
486 | 0 |
|
487 | 0 | if (NS_WARN_IF(aOuter)) { |
488 | 0 | return NS_ERROR_NO_AGGREGATION; |
489 | 0 | } |
490 | 0 | |
491 | 0 | RefPtr<nsStringInputStream> inst = new nsStringInputStream(); |
492 | 0 | return inst->QueryInterface(aIID, aResult); |
493 | 0 | } |