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