|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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 "ipc/IPCMessageUtils.h" |
|
7 |
|
8 #include "nsBufferedStreams.h" |
|
9 #include "nsStreamUtils.h" |
|
10 #include "nsNetCID.h" |
|
11 #include "nsIClassInfoImpl.h" |
|
12 #include "mozilla/ipc/InputStreamUtils.h" |
|
13 #include <algorithm> |
|
14 |
|
15 #ifdef DEBUG_brendan |
|
16 # define METERING |
|
17 #endif |
|
18 |
|
19 #ifdef METERING |
|
20 # include <stdio.h> |
|
21 # define METER(x) x |
|
22 # define MAX_BIG_SEEKS 20 |
|
23 |
|
24 static struct { |
|
25 uint32_t mSeeksWithinBuffer; |
|
26 uint32_t mSeeksOutsideBuffer; |
|
27 uint32_t mBufferReadUponSeek; |
|
28 uint32_t mBufferUnreadUponSeek; |
|
29 uint32_t mBytesReadFromBuffer; |
|
30 uint32_t mBigSeekIndex; |
|
31 struct { |
|
32 int64_t mOldOffset; |
|
33 int64_t mNewOffset; |
|
34 } mBigSeek[MAX_BIG_SEEKS]; |
|
35 } bufstats; |
|
36 #else |
|
37 # define METER(x) /* nothing */ |
|
38 #endif |
|
39 |
|
40 using namespace mozilla::ipc; |
|
41 |
|
42 //////////////////////////////////////////////////////////////////////////////// |
|
43 // nsBufferedStream |
|
44 |
|
45 nsBufferedStream::nsBufferedStream() |
|
46 : mBuffer(nullptr), |
|
47 mBufferStartOffset(0), |
|
48 mCursor(0), |
|
49 mFillPoint(0), |
|
50 mStream(nullptr), |
|
51 mBufferDisabled(false), |
|
52 mEOF(false), |
|
53 mGetBufferCount(0) |
|
54 { |
|
55 } |
|
56 |
|
57 nsBufferedStream::~nsBufferedStream() |
|
58 { |
|
59 Close(); |
|
60 } |
|
61 |
|
62 NS_IMPL_ISUPPORTS(nsBufferedStream, nsISeekableStream) |
|
63 |
|
64 nsresult |
|
65 nsBufferedStream::Init(nsISupports* stream, uint32_t bufferSize) |
|
66 { |
|
67 NS_ASSERTION(stream, "need to supply a stream"); |
|
68 NS_ASSERTION(mStream == nullptr, "already inited"); |
|
69 mStream = stream; |
|
70 NS_IF_ADDREF(mStream); |
|
71 mBufferSize = bufferSize; |
|
72 mBufferStartOffset = 0; |
|
73 mCursor = 0; |
|
74 const mozilla::fallible_t fallible = mozilla::fallible_t(); |
|
75 mBuffer = new (fallible) char[bufferSize]; |
|
76 if (mBuffer == nullptr) |
|
77 return NS_ERROR_OUT_OF_MEMORY; |
|
78 return NS_OK; |
|
79 } |
|
80 |
|
81 nsresult |
|
82 nsBufferedStream::Close() |
|
83 { |
|
84 NS_IF_RELEASE(mStream); |
|
85 if (mBuffer) { |
|
86 delete[] mBuffer; |
|
87 mBuffer = nullptr; |
|
88 mBufferSize = 0; |
|
89 mBufferStartOffset = 0; |
|
90 mCursor = 0; |
|
91 mFillPoint = 0; |
|
92 } |
|
93 #ifdef METERING |
|
94 { |
|
95 static FILE *tfp; |
|
96 if (!tfp) { |
|
97 tfp = fopen("/tmp/bufstats", "w"); |
|
98 if (tfp) |
|
99 setvbuf(tfp, nullptr, _IOLBF, 0); |
|
100 } |
|
101 if (tfp) { |
|
102 fprintf(tfp, "seeks within buffer: %u\n", |
|
103 bufstats.mSeeksWithinBuffer); |
|
104 fprintf(tfp, "seeks outside buffer: %u\n", |
|
105 bufstats.mSeeksOutsideBuffer); |
|
106 fprintf(tfp, "buffer read on seek: %u\n", |
|
107 bufstats.mBufferReadUponSeek); |
|
108 fprintf(tfp, "buffer unread on seek: %u\n", |
|
109 bufstats.mBufferUnreadUponSeek); |
|
110 fprintf(tfp, "bytes read from buffer: %u\n", |
|
111 bufstats.mBytesReadFromBuffer); |
|
112 for (uint32_t i = 0; i < bufstats.mBigSeekIndex; i++) { |
|
113 fprintf(tfp, "bigseek[%u] = {old: %u, new: %u}\n", |
|
114 i, |
|
115 bufstats.mBigSeek[i].mOldOffset, |
|
116 bufstats.mBigSeek[i].mNewOffset); |
|
117 } |
|
118 } |
|
119 } |
|
120 #endif |
|
121 return NS_OK; |
|
122 } |
|
123 |
|
124 NS_IMETHODIMP |
|
125 nsBufferedStream::Seek(int32_t whence, int64_t offset) |
|
126 { |
|
127 if (mStream == nullptr) |
|
128 return NS_BASE_STREAM_CLOSED; |
|
129 |
|
130 // If the underlying stream isn't a random access store, then fail early. |
|
131 // We could possibly succeed for the case where the seek position denotes |
|
132 // something that happens to be read into the buffer, but that would make |
|
133 // the failure data-dependent. |
|
134 nsresult rv; |
|
135 nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv); |
|
136 if (NS_FAILED(rv)) return rv; |
|
137 |
|
138 int64_t absPos = 0; |
|
139 switch (whence) { |
|
140 case nsISeekableStream::NS_SEEK_SET: |
|
141 absPos = offset; |
|
142 break; |
|
143 case nsISeekableStream::NS_SEEK_CUR: |
|
144 absPos = mBufferStartOffset; |
|
145 absPos += mCursor; |
|
146 absPos += offset; |
|
147 break; |
|
148 case nsISeekableStream::NS_SEEK_END: |
|
149 absPos = -1; |
|
150 break; |
|
151 default: |
|
152 NS_NOTREACHED("bogus seek whence parameter"); |
|
153 return NS_ERROR_UNEXPECTED; |
|
154 } |
|
155 |
|
156 // Let mCursor point into the existing buffer if the new position is |
|
157 // between the current cursor and the mFillPoint "fencepost" -- the |
|
158 // client may never get around to a Read or Write after this Seek. |
|
159 // Read and Write worry about flushing and filling in that event. |
|
160 // But if we're at EOF, make sure to pass the seek through to the |
|
161 // underlying stream, because it may have auto-closed itself and |
|
162 // needs to reopen. |
|
163 uint32_t offsetInBuffer = uint32_t(absPos - mBufferStartOffset); |
|
164 if (offsetInBuffer <= mFillPoint && !mEOF) { |
|
165 METER(bufstats.mSeeksWithinBuffer++); |
|
166 mCursor = offsetInBuffer; |
|
167 return NS_OK; |
|
168 } |
|
169 |
|
170 METER(bufstats.mSeeksOutsideBuffer++); |
|
171 METER(bufstats.mBufferReadUponSeek += mCursor); |
|
172 METER(bufstats.mBufferUnreadUponSeek += mFillPoint - mCursor); |
|
173 rv = Flush(); |
|
174 if (NS_FAILED(rv)) return rv; |
|
175 |
|
176 rv = ras->Seek(whence, offset); |
|
177 if (NS_FAILED(rv)) return rv; |
|
178 |
|
179 mEOF = false; |
|
180 |
|
181 // Recompute whether the offset we're seeking to is in our buffer. |
|
182 // Note that we need to recompute because Flush() might have |
|
183 // changed mBufferStartOffset. |
|
184 offsetInBuffer = uint32_t(absPos - mBufferStartOffset); |
|
185 if (offsetInBuffer <= mFillPoint) { |
|
186 // It's safe to just set mCursor to offsetInBuffer. In particular, we |
|
187 // want to avoid calling Fill() here since we already have the data that |
|
188 // was seeked to and calling Fill() might auto-close our underlying |
|
189 // stream in some cases. |
|
190 mCursor = offsetInBuffer; |
|
191 return NS_OK; |
|
192 } |
|
193 |
|
194 METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS) |
|
195 bufstats.mBigSeek[bufstats.mBigSeekIndex].mOldOffset = |
|
196 mBufferStartOffset + int64_t(mCursor)); |
|
197 const int64_t minus1 = -1; |
|
198 if (absPos == minus1) { |
|
199 // then we had the SEEK_END case, above |
|
200 int64_t tellPos; |
|
201 rv = ras->Tell(&tellPos); |
|
202 mBufferStartOffset = tellPos; |
|
203 if (NS_FAILED(rv)) return rv; |
|
204 } |
|
205 else { |
|
206 mBufferStartOffset = absPos; |
|
207 } |
|
208 METER(if (bufstats.mBigSeekIndex < MAX_BIG_SEEKS) |
|
209 bufstats.mBigSeek[bufstats.mBigSeekIndex++].mNewOffset = |
|
210 mBufferStartOffset); |
|
211 |
|
212 mFillPoint = mCursor = 0; |
|
213 return Fill(); |
|
214 } |
|
215 |
|
216 NS_IMETHODIMP |
|
217 nsBufferedStream::Tell(int64_t *result) |
|
218 { |
|
219 if (mStream == nullptr) |
|
220 return NS_BASE_STREAM_CLOSED; |
|
221 |
|
222 int64_t result64 = mBufferStartOffset; |
|
223 result64 += mCursor; |
|
224 *result = result64; |
|
225 return NS_OK; |
|
226 } |
|
227 |
|
228 NS_IMETHODIMP |
|
229 nsBufferedStream::SetEOF() |
|
230 { |
|
231 if (mStream == nullptr) |
|
232 return NS_BASE_STREAM_CLOSED; |
|
233 |
|
234 nsresult rv; |
|
235 nsCOMPtr<nsISeekableStream> ras = do_QueryInterface(mStream, &rv); |
|
236 if (NS_FAILED(rv)) return rv; |
|
237 |
|
238 rv = ras->SetEOF(); |
|
239 if (NS_SUCCEEDED(rv)) |
|
240 mEOF = true; |
|
241 return rv; |
|
242 } |
|
243 |
|
244 //////////////////////////////////////////////////////////////////////////////// |
|
245 // nsBufferedInputStream |
|
246 |
|
247 NS_IMPL_ADDREF_INHERITED(nsBufferedInputStream, nsBufferedStream) |
|
248 NS_IMPL_RELEASE_INHERITED(nsBufferedInputStream, nsBufferedStream) |
|
249 |
|
250 NS_IMPL_CLASSINFO(nsBufferedInputStream, nullptr, nsIClassInfo::THREADSAFE, |
|
251 NS_BUFFEREDINPUTSTREAM_CID) |
|
252 |
|
253 NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream) |
|
254 NS_INTERFACE_MAP_ENTRY(nsIInputStream) |
|
255 NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream) |
|
256 NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess) |
|
257 NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream) |
|
258 NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream) |
|
259 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream) |
|
260 |
|
261 NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream, |
|
262 nsIInputStream, |
|
263 nsIBufferedInputStream, |
|
264 nsISeekableStream, |
|
265 nsIStreamBufferAccess) |
|
266 |
|
267 nsresult |
|
268 nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) |
|
269 { |
|
270 NS_ENSURE_NO_AGGREGATION(aOuter); |
|
271 |
|
272 nsBufferedInputStream* stream = new nsBufferedInputStream(); |
|
273 if (stream == nullptr) |
|
274 return NS_ERROR_OUT_OF_MEMORY; |
|
275 NS_ADDREF(stream); |
|
276 nsresult rv = stream->QueryInterface(aIID, aResult); |
|
277 NS_RELEASE(stream); |
|
278 return rv; |
|
279 } |
|
280 |
|
281 NS_IMETHODIMP |
|
282 nsBufferedInputStream::Init(nsIInputStream* stream, uint32_t bufferSize) |
|
283 { |
|
284 return nsBufferedStream::Init(stream, bufferSize); |
|
285 } |
|
286 |
|
287 NS_IMETHODIMP |
|
288 nsBufferedInputStream::Close() |
|
289 { |
|
290 nsresult rv1 = NS_OK, rv2; |
|
291 if (mStream) { |
|
292 rv1 = Source()->Close(); |
|
293 NS_RELEASE(mStream); |
|
294 } |
|
295 rv2 = nsBufferedStream::Close(); |
|
296 if (NS_FAILED(rv1)) return rv1; |
|
297 return rv2; |
|
298 } |
|
299 |
|
300 NS_IMETHODIMP |
|
301 nsBufferedInputStream::Available(uint64_t *result) |
|
302 { |
|
303 nsresult rv = NS_OK; |
|
304 *result = 0; |
|
305 if (mStream) { |
|
306 rv = Source()->Available(result); |
|
307 } |
|
308 *result += (mFillPoint - mCursor); |
|
309 return rv; |
|
310 } |
|
311 |
|
312 NS_IMETHODIMP |
|
313 nsBufferedInputStream::Read(char * buf, uint32_t count, uint32_t *result) |
|
314 { |
|
315 if (mBufferDisabled) { |
|
316 if (!mStream) { |
|
317 *result = 0; |
|
318 return NS_OK; |
|
319 } |
|
320 nsresult rv = Source()->Read(buf, count, result); |
|
321 if (NS_SUCCEEDED(rv)) { |
|
322 mBufferStartOffset += *result; // so nsBufferedStream::Tell works |
|
323 if (*result == 0) { |
|
324 mEOF = true; |
|
325 } |
|
326 } |
|
327 return rv; |
|
328 } |
|
329 |
|
330 return ReadSegments(NS_CopySegmentToBuffer, buf, count, result); |
|
331 } |
|
332 |
|
333 NS_IMETHODIMP |
|
334 nsBufferedInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure, |
|
335 uint32_t count, uint32_t *result) |
|
336 { |
|
337 *result = 0; |
|
338 |
|
339 if (!mStream) |
|
340 return NS_OK; |
|
341 |
|
342 nsresult rv = NS_OK; |
|
343 while (count > 0) { |
|
344 uint32_t amt = std::min(count, mFillPoint - mCursor); |
|
345 if (amt > 0) { |
|
346 uint32_t read = 0; |
|
347 rv = writer(this, closure, mBuffer + mCursor, *result, amt, &read); |
|
348 if (NS_FAILED(rv)) { |
|
349 // errors returned from the writer end here! |
|
350 rv = NS_OK; |
|
351 break; |
|
352 } |
|
353 *result += read; |
|
354 count -= read; |
|
355 mCursor += read; |
|
356 } |
|
357 else { |
|
358 rv = Fill(); |
|
359 if (NS_FAILED(rv) || mFillPoint == mCursor) |
|
360 break; |
|
361 } |
|
362 } |
|
363 return (*result > 0) ? NS_OK : rv; |
|
364 } |
|
365 |
|
366 NS_IMETHODIMP |
|
367 nsBufferedInputStream::IsNonBlocking(bool *aNonBlocking) |
|
368 { |
|
369 if (mStream) |
|
370 return Source()->IsNonBlocking(aNonBlocking); |
|
371 return NS_ERROR_NOT_INITIALIZED; |
|
372 } |
|
373 |
|
374 NS_IMETHODIMP |
|
375 nsBufferedInputStream::Fill() |
|
376 { |
|
377 if (mBufferDisabled) |
|
378 return NS_OK; |
|
379 NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_INITIALIZED); |
|
380 |
|
381 nsresult rv; |
|
382 int32_t rem = int32_t(mFillPoint - mCursor); |
|
383 if (rem > 0) { |
|
384 // slide the remainder down to the start of the buffer |
|
385 // |<------------->|<--rem-->|<--->| |
|
386 // b c f s |
|
387 memcpy(mBuffer, mBuffer + mCursor, rem); |
|
388 } |
|
389 mBufferStartOffset += mCursor; |
|
390 mFillPoint = rem; |
|
391 mCursor = 0; |
|
392 |
|
393 uint32_t amt; |
|
394 rv = Source()->Read(mBuffer + mFillPoint, mBufferSize - mFillPoint, &amt); |
|
395 if (NS_FAILED(rv)) return rv; |
|
396 |
|
397 if (amt == 0) |
|
398 mEOF = true; |
|
399 |
|
400 mFillPoint += amt; |
|
401 return NS_OK; |
|
402 } |
|
403 |
|
404 NS_IMETHODIMP_(char*) |
|
405 nsBufferedInputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) |
|
406 { |
|
407 NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!"); |
|
408 if (mGetBufferCount != 0) |
|
409 return nullptr; |
|
410 |
|
411 if (mBufferDisabled) |
|
412 return nullptr; |
|
413 |
|
414 char* buf = mBuffer + mCursor; |
|
415 uint32_t rem = mFillPoint - mCursor; |
|
416 if (rem == 0) { |
|
417 if (NS_FAILED(Fill())) |
|
418 return nullptr; |
|
419 buf = mBuffer + mCursor; |
|
420 rem = mFillPoint - mCursor; |
|
421 } |
|
422 |
|
423 uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask); |
|
424 if (mod) { |
|
425 uint32_t pad = aAlignMask + 1 - mod; |
|
426 if (pad > rem) |
|
427 return nullptr; |
|
428 |
|
429 memset(buf, 0, pad); |
|
430 mCursor += pad; |
|
431 buf += pad; |
|
432 rem -= pad; |
|
433 } |
|
434 |
|
435 if (aLength > rem) |
|
436 return nullptr; |
|
437 mGetBufferCount++; |
|
438 return buf; |
|
439 } |
|
440 |
|
441 NS_IMETHODIMP_(void) |
|
442 nsBufferedInputStream::PutBuffer(char* aBuffer, uint32_t aLength) |
|
443 { |
|
444 NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!"); |
|
445 if (--mGetBufferCount != 0) |
|
446 return; |
|
447 |
|
448 NS_ASSERTION(mCursor + aLength <= mFillPoint, "PutBuffer botch"); |
|
449 mCursor += aLength; |
|
450 } |
|
451 |
|
452 NS_IMETHODIMP |
|
453 nsBufferedInputStream::DisableBuffering() |
|
454 { |
|
455 NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!"); |
|
456 NS_ASSERTION(mGetBufferCount == 0, |
|
457 "DisableBuffer call between GetBuffer and PutBuffer!"); |
|
458 if (mGetBufferCount != 0) |
|
459 return NS_ERROR_UNEXPECTED; |
|
460 |
|
461 // Empty the buffer so nsBufferedStream::Tell works. |
|
462 mBufferStartOffset += mCursor; |
|
463 mFillPoint = mCursor = 0; |
|
464 mBufferDisabled = true; |
|
465 return NS_OK; |
|
466 } |
|
467 |
|
468 NS_IMETHODIMP |
|
469 nsBufferedInputStream::EnableBuffering() |
|
470 { |
|
471 NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!"); |
|
472 mBufferDisabled = false; |
|
473 return NS_OK; |
|
474 } |
|
475 |
|
476 NS_IMETHODIMP |
|
477 nsBufferedInputStream::GetUnbufferedStream(nsISupports* *aStream) |
|
478 { |
|
479 // Empty the buffer so subsequent i/o trumps any buffered data. |
|
480 mBufferStartOffset += mCursor; |
|
481 mFillPoint = mCursor = 0; |
|
482 |
|
483 *aStream = mStream; |
|
484 NS_IF_ADDREF(*aStream); |
|
485 return NS_OK; |
|
486 } |
|
487 |
|
488 void |
|
489 nsBufferedInputStream::Serialize(InputStreamParams& aParams, |
|
490 FileDescriptorArray& aFileDescriptors) |
|
491 { |
|
492 BufferedInputStreamParams params; |
|
493 |
|
494 if (mStream) { |
|
495 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream); |
|
496 MOZ_ASSERT(stream); |
|
497 |
|
498 InputStreamParams wrappedParams; |
|
499 SerializeInputStream(stream, wrappedParams, aFileDescriptors); |
|
500 |
|
501 params.optionalStream() = wrappedParams; |
|
502 } |
|
503 else { |
|
504 params.optionalStream() = mozilla::void_t(); |
|
505 } |
|
506 |
|
507 params.bufferSize() = mBufferSize; |
|
508 |
|
509 aParams = params; |
|
510 } |
|
511 |
|
512 bool |
|
513 nsBufferedInputStream::Deserialize(const InputStreamParams& aParams, |
|
514 const FileDescriptorArray& aFileDescriptors) |
|
515 { |
|
516 if (aParams.type() != InputStreamParams::TBufferedInputStreamParams) { |
|
517 NS_ERROR("Received unknown parameters from the other process!"); |
|
518 return false; |
|
519 } |
|
520 |
|
521 const BufferedInputStreamParams& params = |
|
522 aParams.get_BufferedInputStreamParams(); |
|
523 const OptionalInputStreamParams& wrappedParams = params.optionalStream(); |
|
524 |
|
525 nsCOMPtr<nsIInputStream> stream; |
|
526 if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) { |
|
527 stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(), |
|
528 aFileDescriptors); |
|
529 if (!stream) { |
|
530 NS_WARNING("Failed to deserialize wrapped stream!"); |
|
531 return false; |
|
532 } |
|
533 } |
|
534 else { |
|
535 NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t, |
|
536 "Unknown type for OptionalInputStreamParams!"); |
|
537 } |
|
538 |
|
539 nsresult rv = Init(stream, params.bufferSize()); |
|
540 NS_ENSURE_SUCCESS(rv, false); |
|
541 |
|
542 return true; |
|
543 } |
|
544 |
|
545 //////////////////////////////////////////////////////////////////////////////// |
|
546 // nsBufferedOutputStream |
|
547 |
|
548 NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream) |
|
549 NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream) |
|
550 // This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for |
|
551 // non-nullness of mSafeStream. |
|
552 NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream) |
|
553 NS_INTERFACE_MAP_ENTRY(nsIOutputStream) |
|
554 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISafeOutputStream, mSafeStream) |
|
555 NS_INTERFACE_MAP_ENTRY(nsIBufferedOutputStream) |
|
556 NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess) |
|
557 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream) |
|
558 |
|
559 nsresult |
|
560 nsBufferedOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) |
|
561 { |
|
562 NS_ENSURE_NO_AGGREGATION(aOuter); |
|
563 |
|
564 nsBufferedOutputStream* stream = new nsBufferedOutputStream(); |
|
565 if (stream == nullptr) |
|
566 return NS_ERROR_OUT_OF_MEMORY; |
|
567 NS_ADDREF(stream); |
|
568 nsresult rv = stream->QueryInterface(aIID, aResult); |
|
569 NS_RELEASE(stream); |
|
570 return rv; |
|
571 } |
|
572 |
|
573 NS_IMETHODIMP |
|
574 nsBufferedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize) |
|
575 { |
|
576 // QI stream to an nsISafeOutputStream, to see if we should support it |
|
577 mSafeStream = do_QueryInterface(stream); |
|
578 |
|
579 return nsBufferedStream::Init(stream, bufferSize); |
|
580 } |
|
581 |
|
582 NS_IMETHODIMP |
|
583 nsBufferedOutputStream::Close() |
|
584 { |
|
585 nsresult rv1, rv2 = NS_OK, rv3; |
|
586 rv1 = Flush(); |
|
587 // If we fail to Flush all the data, then we close anyway and drop the |
|
588 // remaining data in the buffer. We do this because it's what Unix does |
|
589 // for fclose and close. However, we report the error from Flush anyway. |
|
590 if (mStream) { |
|
591 rv2 = Sink()->Close(); |
|
592 NS_RELEASE(mStream); |
|
593 } |
|
594 rv3 = nsBufferedStream::Close(); |
|
595 if (NS_FAILED(rv1)) return rv1; |
|
596 if (NS_FAILED(rv2)) return rv2; |
|
597 return rv3; |
|
598 } |
|
599 |
|
600 NS_IMETHODIMP |
|
601 nsBufferedOutputStream::Write(const char *buf, uint32_t count, uint32_t *result) |
|
602 { |
|
603 nsresult rv = NS_OK; |
|
604 uint32_t written = 0; |
|
605 while (count > 0) { |
|
606 uint32_t amt = std::min(count, mBufferSize - mCursor); |
|
607 if (amt > 0) { |
|
608 memcpy(mBuffer + mCursor, buf + written, amt); |
|
609 written += amt; |
|
610 count -= amt; |
|
611 mCursor += amt; |
|
612 if (mFillPoint < mCursor) |
|
613 mFillPoint = mCursor; |
|
614 } |
|
615 else { |
|
616 NS_ASSERTION(mFillPoint, "iloop in nsBufferedOutputStream::Write!"); |
|
617 rv = Flush(); |
|
618 if (NS_FAILED(rv)) break; |
|
619 } |
|
620 } |
|
621 *result = written; |
|
622 return (written > 0) ? NS_OK : rv; |
|
623 } |
|
624 |
|
625 NS_IMETHODIMP |
|
626 nsBufferedOutputStream::Flush() |
|
627 { |
|
628 nsresult rv; |
|
629 uint32_t amt; |
|
630 if (!mStream) { |
|
631 // Stream already cancelled/flushed; probably because of previous error. |
|
632 return NS_OK; |
|
633 } |
|
634 rv = Sink()->Write(mBuffer, mFillPoint, &amt); |
|
635 if (NS_FAILED(rv)) return rv; |
|
636 mBufferStartOffset += amt; |
|
637 if (amt == mFillPoint) { |
|
638 mFillPoint = mCursor = 0; |
|
639 return NS_OK; // flushed everything |
|
640 } |
|
641 |
|
642 // slide the remainder down to the start of the buffer |
|
643 // |<-------------->|<---|----->| |
|
644 // b a c s |
|
645 uint32_t rem = mFillPoint - amt; |
|
646 memcpy(mBuffer, mBuffer + amt, rem); |
|
647 mFillPoint = mCursor = rem; |
|
648 return NS_ERROR_FAILURE; // didn't flush all |
|
649 } |
|
650 |
|
651 // nsISafeOutputStream |
|
652 NS_IMETHODIMP |
|
653 nsBufferedOutputStream::Finish() |
|
654 { |
|
655 // flush the stream, to write out any buffered data... |
|
656 nsresult rv = nsBufferedOutputStream::Flush(); |
|
657 if (NS_FAILED(rv)) |
|
658 NS_WARNING("failed to flush buffered data! possible dataloss"); |
|
659 |
|
660 // ... and finish the underlying stream... |
|
661 if (NS_SUCCEEDED(rv)) |
|
662 rv = mSafeStream->Finish(); |
|
663 else |
|
664 Sink()->Close(); |
|
665 |
|
666 // ... and close the buffered stream, so any further attempts to flush/close |
|
667 // the buffered stream won't cause errors. |
|
668 nsBufferedStream::Close(); |
|
669 |
|
670 return rv; |
|
671 } |
|
672 |
|
673 static NS_METHOD |
|
674 nsReadFromInputStream(nsIOutputStream* outStr, |
|
675 void* closure, |
|
676 char* toRawSegment, |
|
677 uint32_t offset, |
|
678 uint32_t count, |
|
679 uint32_t *readCount) |
|
680 { |
|
681 nsIInputStream* fromStream = (nsIInputStream*)closure; |
|
682 return fromStream->Read(toRawSegment, count, readCount); |
|
683 } |
|
684 |
|
685 NS_IMETHODIMP |
|
686 nsBufferedOutputStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) |
|
687 { |
|
688 return WriteSegments(nsReadFromInputStream, inStr, count, _retval); |
|
689 } |
|
690 |
|
691 NS_IMETHODIMP |
|
692 nsBufferedOutputStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) |
|
693 { |
|
694 *_retval = 0; |
|
695 nsresult rv; |
|
696 while (count > 0) { |
|
697 uint32_t left = std::min(count, mBufferSize - mCursor); |
|
698 if (left == 0) { |
|
699 rv = Flush(); |
|
700 if (NS_FAILED(rv)) |
|
701 return rv; |
|
702 |
|
703 continue; |
|
704 } |
|
705 |
|
706 uint32_t read = 0; |
|
707 rv = reader(this, closure, mBuffer + mCursor, *_retval, left, &read); |
|
708 |
|
709 if (NS_FAILED(rv)) // If we have written some data, return ok |
|
710 return (*_retval > 0) ? NS_OK : rv; |
|
711 mCursor += read; |
|
712 *_retval += read; |
|
713 count -= read; |
|
714 mFillPoint = std::max(mFillPoint, mCursor); |
|
715 } |
|
716 return NS_OK; |
|
717 } |
|
718 |
|
719 NS_IMETHODIMP |
|
720 nsBufferedOutputStream::IsNonBlocking(bool *aNonBlocking) |
|
721 { |
|
722 if (mStream) |
|
723 return Sink()->IsNonBlocking(aNonBlocking); |
|
724 return NS_ERROR_NOT_INITIALIZED; |
|
725 } |
|
726 |
|
727 NS_IMETHODIMP_(char*) |
|
728 nsBufferedOutputStream::GetBuffer(uint32_t aLength, uint32_t aAlignMask) |
|
729 { |
|
730 NS_ASSERTION(mGetBufferCount == 0, "nested GetBuffer!"); |
|
731 if (mGetBufferCount != 0) |
|
732 return nullptr; |
|
733 |
|
734 if (mBufferDisabled) |
|
735 return nullptr; |
|
736 |
|
737 char* buf = mBuffer + mCursor; |
|
738 uint32_t rem = mBufferSize - mCursor; |
|
739 if (rem == 0) { |
|
740 if (NS_FAILED(Flush())) |
|
741 return nullptr; |
|
742 buf = mBuffer + mCursor; |
|
743 rem = mBufferSize - mCursor; |
|
744 } |
|
745 |
|
746 uint32_t mod = (NS_PTR_TO_INT32(buf) & aAlignMask); |
|
747 if (mod) { |
|
748 uint32_t pad = aAlignMask + 1 - mod; |
|
749 if (pad > rem) |
|
750 return nullptr; |
|
751 |
|
752 memset(buf, 0, pad); |
|
753 mCursor += pad; |
|
754 buf += pad; |
|
755 rem -= pad; |
|
756 } |
|
757 |
|
758 if (aLength > rem) |
|
759 return nullptr; |
|
760 mGetBufferCount++; |
|
761 return buf; |
|
762 } |
|
763 |
|
764 NS_IMETHODIMP_(void) |
|
765 nsBufferedOutputStream::PutBuffer(char* aBuffer, uint32_t aLength) |
|
766 { |
|
767 NS_ASSERTION(mGetBufferCount == 1, "stray PutBuffer!"); |
|
768 if (--mGetBufferCount != 0) |
|
769 return; |
|
770 |
|
771 NS_ASSERTION(mCursor + aLength <= mBufferSize, "PutBuffer botch"); |
|
772 mCursor += aLength; |
|
773 if (mFillPoint < mCursor) |
|
774 mFillPoint = mCursor; |
|
775 } |
|
776 |
|
777 NS_IMETHODIMP |
|
778 nsBufferedOutputStream::DisableBuffering() |
|
779 { |
|
780 NS_ASSERTION(!mBufferDisabled, "redundant call to DisableBuffering!"); |
|
781 NS_ASSERTION(mGetBufferCount == 0, |
|
782 "DisableBuffer call between GetBuffer and PutBuffer!"); |
|
783 if (mGetBufferCount != 0) |
|
784 return NS_ERROR_UNEXPECTED; |
|
785 |
|
786 // Empty the buffer so nsBufferedStream::Tell works. |
|
787 nsresult rv = Flush(); |
|
788 if (NS_FAILED(rv)) |
|
789 return rv; |
|
790 |
|
791 mBufferDisabled = true; |
|
792 return NS_OK; |
|
793 } |
|
794 |
|
795 NS_IMETHODIMP |
|
796 nsBufferedOutputStream::EnableBuffering() |
|
797 { |
|
798 NS_ASSERTION(mBufferDisabled, "gratuitous call to EnableBuffering!"); |
|
799 mBufferDisabled = false; |
|
800 return NS_OK; |
|
801 } |
|
802 |
|
803 NS_IMETHODIMP |
|
804 nsBufferedOutputStream::GetUnbufferedStream(nsISupports* *aStream) |
|
805 { |
|
806 // Empty the buffer so subsequent i/o trumps any buffered data. |
|
807 if (mFillPoint) { |
|
808 nsresult rv = Flush(); |
|
809 if (NS_FAILED(rv)) |
|
810 return rv; |
|
811 } |
|
812 |
|
813 *aStream = mStream; |
|
814 NS_IF_ADDREF(*aStream); |
|
815 return NS_OK; |
|
816 } |
|
817 |
|
818 //////////////////////////////////////////////////////////////////////////////// |