nsBufferedStreams.cpp 23 KB

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