1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "CacheFileOutputStream.h"
9 #include "CacheEntry.h"
10 #include "nsStreamUtils.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/DebugOnly.h"
15 namespace mozilla::net
{
17 NS_IMPL_ADDREF(CacheFileOutputStream
)
18 NS_IMETHODIMP_(MozExternalRefCountType
)
19 CacheFileOutputStream::Release() {
20 MOZ_ASSERT(0 != mRefCnt
, "dup release");
21 nsrefcnt count
= --mRefCnt
;
22 NS_LOG_RELEASE(this, count
, "CacheFileOutputStream");
27 CacheFileAutoLock
lock(mFile
);
28 mFile
->RemoveOutput(this, mStatus
);
37 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream
)
38 NS_INTERFACE_MAP_ENTRY(nsIOutputStream
)
39 NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream
)
40 NS_INTERFACE_MAP_ENTRY(nsISeekableStream
)
41 NS_INTERFACE_MAP_ENTRY(nsITellableStream
)
42 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener
)
43 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIOutputStream
)
46 CacheFileOutputStream::CacheFileOutputStream(
47 CacheFile
* aFile
, CacheOutputCloseListener
* aCloseListener
,
48 bool aAlternativeData
)
50 mCloseListener(aCloseListener
),
53 mAlternativeData(aAlternativeData
),
56 LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
58 if (mAlternativeData
) {
59 mPos
= mFile
->mAltDataOffset
;
63 CacheFileOutputStream::~CacheFileOutputStream() {
64 LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
69 CacheFileOutputStream::Close() {
70 LOG(("CacheFileOutputStream::Close() [this=%p]", this));
71 return CloseWithStatus(NS_OK
);
75 CacheFileOutputStream::Flush() {
76 // TODO do we need to implement flush ???
77 LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
82 CacheFileOutputStream::StreamStatus() {
83 CacheFileAutoLock
lock(mFile
);
84 mFile
->AssertOwnsLock(); // For thread-safety analysis
86 LOG(("CacheFileOutputStream::Close() [this=%p]", this));
88 return NS_FAILED(mStatus
) ? mStatus
: NS_BASE_STREAM_CLOSED
;
94 CacheFileOutputStream::Write(const char* aBuf
, uint32_t aCount
,
96 CacheFileAutoLock
lock(mFile
);
97 mFile
->AssertOwnsLock(); // For thread-safety analysis
99 LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount
));
103 ("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
104 "status=0x%08" PRIx32
"]",
105 this, static_cast<uint32_t>(mStatus
)));
107 return NS_FAILED(mStatus
) ? mStatus
: NS_BASE_STREAM_CLOSED
;
110 if (!mFile
->mSkipSizeCheck
&&
111 CacheObserver::EntryIsTooBig(mPos
+ aCount
, !mFile
->mMemoryOnly
)) {
112 LOG(("CacheFileOutputStream::Write() - Entry is too big. [this=%p]", this));
114 CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG
);
115 return NS_ERROR_FILE_TOO_BIG
;
118 // We use 64-bit offset when accessing the file, unfortunately we use 32-bit
119 // metadata offset, so we cannot handle data bigger than 4GB.
120 if (mPos
+ aCount
> PR_UINT32_MAX
) {
121 LOG(("CacheFileOutputStream::Write() - Entry's size exceeds 4GB. [this=%p]",
124 CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG
);
125 return NS_ERROR_FILE_TOO_BIG
;
131 EnsureCorrectChunk(false);
132 if (NS_FAILED(mStatus
)) {
137 if (NS_FAILED(mStatus
)) {
141 uint32_t chunkOffset
= mPos
- (mPos
/ kChunkSize
) * kChunkSize
;
142 uint32_t canWrite
= kChunkSize
- chunkOffset
;
143 uint32_t thisWrite
= std::min(static_cast<uint32_t>(canWrite
), aCount
);
145 CacheFileChunkWriteHandle hnd
=
146 mChunk
->GetWriteHandle(chunkOffset
+ thisWrite
);
148 CloseWithStatusLocked(NS_ERROR_OUT_OF_MEMORY
);
149 return NS_ERROR_OUT_OF_MEMORY
;
152 memcpy(hnd
.Buf() + chunkOffset
, aBuf
, thisWrite
);
153 hnd
.UpdateDataSize(chunkOffset
, thisWrite
);
160 EnsureCorrectChunk(true);
162 LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]", *_retval
,
169 CacheFileOutputStream::WriteFrom(nsIInputStream
* aFromStream
, uint32_t aCount
,
172 ("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
174 this, aFromStream
, aCount
));
176 return NS_ERROR_NOT_IMPLEMENTED
;
180 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader
, void* aClosure
,
181 uint32_t aCount
, uint32_t* _retval
) {
183 ("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
187 return NS_ERROR_NOT_IMPLEMENTED
;
191 CacheFileOutputStream::IsNonBlocking(bool* _retval
) {
196 // nsIAsyncOutputStream
198 CacheFileOutputStream::CloseWithStatus(nsresult aStatus
) {
199 CacheFileAutoLock
lock(mFile
);
201 LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32
203 this, static_cast<uint32_t>(aStatus
)));
205 return CloseWithStatusLocked(aStatus
);
208 nsresult
CacheFileOutputStream::CloseWithStatusLocked(nsresult aStatus
) {
210 ("CacheFileOutputStream::CloseWithStatusLocked() [this=%p, "
211 "aStatus=0x%08" PRIx32
"]",
212 this, static_cast<uint32_t>(aStatus
)));
215 MOZ_ASSERT(!mCallback
);
220 mStatus
= NS_FAILED(aStatus
) ? aStatus
: NS_BASE_STREAM_CLOSED
;
230 mFile
->RemoveOutput(this, mStatus
);
236 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback
* aCallback
,
237 uint32_t aFlags
, uint32_t aRequestedCount
,
238 nsIEventTarget
* aEventTarget
) {
239 CacheFileAutoLock
lock(mFile
);
242 ("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
243 "requestedCount=%d, eventTarget=%p]",
244 this, aCallback
, aFlags
, aRequestedCount
, aEventTarget
));
246 mCallback
= aCallback
;
247 mCallbackFlags
= aFlags
;
248 mCallbackTarget
= aEventTarget
;
250 if (!mCallback
) return NS_OK
;
252 // The stream is blocking so it is writable at any time
253 if (mClosed
|| !(aFlags
& WAIT_CLOSURE_ONLY
)) NotifyListener();
260 CacheFileOutputStream::Seek(int32_t whence
, int64_t offset
) {
261 CacheFileAutoLock
lock(mFile
);
262 mFile
->AssertOwnsLock(); // For thread-safety analysis
264 LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%" PRId64
"]",
265 this, whence
, offset
));
268 LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
269 return NS_BASE_STREAM_CLOSED
;
272 int64_t newPos
= offset
;
275 if (mAlternativeData
) {
276 newPos
+= mFile
->mAltDataOffset
;
283 if (mAlternativeData
) {
284 newPos
+= mFile
->mDataSize
;
286 newPos
+= mFile
->mAltDataOffset
;
290 NS_ERROR("invalid whence");
291 return NS_ERROR_INVALID_ARG
;
294 EnsureCorrectChunk(true);
296 LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%" PRId64
"]", this, mPos
));
301 CacheFileOutputStream::SetEOF() {
302 MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
303 // Right now we don't use SetEOF(). If we ever need this method, we need
304 // to think about what to do with input streams that already points beyond
306 return NS_ERROR_NOT_IMPLEMENTED
;
311 CacheFileOutputStream::Tell(int64_t* _retval
) {
312 CacheFileAutoLock
lock(mFile
);
313 mFile
->AssertOwnsLock(); // For thread-safety analysis
316 LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
317 return NS_BASE_STREAM_CLOSED
;
322 if (mAlternativeData
) {
323 *_retval
-= mFile
->mAltDataOffset
;
326 LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%" PRId64
"]", this,
331 // CacheFileChunkListener
332 nsresult
CacheFileOutputStream::OnChunkRead(nsresult aResult
,
333 CacheFileChunk
* aChunk
) {
334 MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
335 return NS_ERROR_UNEXPECTED
;
338 nsresult
CacheFileOutputStream::OnChunkWritten(nsresult aResult
,
339 CacheFileChunk
* aChunk
) {
340 MOZ_CRASH("CacheFileOutputStream::OnChunkWritten should not be called!");
341 return NS_ERROR_UNEXPECTED
;
344 nsresult
CacheFileOutputStream::OnChunkAvailable(nsresult aResult
,
346 CacheFileChunk
* aChunk
) {
347 MOZ_CRASH("CacheFileOutputStream::OnChunkAvailable should not be called!");
348 return NS_ERROR_UNEXPECTED
;
351 nsresult
CacheFileOutputStream::OnChunkUpdated(CacheFileChunk
* aChunk
) {
352 MOZ_CRASH("CacheFileOutputStream::OnChunkUpdated should not be called!");
353 return NS_ERROR_UNEXPECTED
;
356 void CacheFileOutputStream::NotifyCloseListener() {
357 RefPtr
<CacheOutputCloseListener
> listener
;
358 listener
.swap(mCloseListener
);
359 if (!listener
) return;
361 listener
->OnOutputClosed();
364 void CacheFileOutputStream::ReleaseChunk() {
365 mFile
->AssertOwnsLock();
367 LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]", this,
370 // If the chunk didn't write any data we need to remove hash for this chunk
371 // that was added when the chunk was created in CacheFile::GetChunkLocked.
372 if (mChunk
->DataSize() == 0) {
373 // It must be due to a failure, we don't create a new chunk when we don't
374 // have data to write.
375 MOZ_ASSERT(NS_FAILED(mChunk
->GetStatus()));
376 mFile
->mMetadata
->RemoveHash(mChunk
->Index());
379 mFile
->ReleaseOutsideLock(std::move(mChunk
));
382 void CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly
) {
383 mFile
->AssertOwnsLock();
385 LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
386 this, aReleaseOnly
));
388 uint32_t chunkIdx
= mPos
/ kChunkSize
;
391 if (mChunk
->Index() == chunkIdx
) {
392 // we have a correct chunk
394 ("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
403 if (aReleaseOnly
) return;
406 rv
= mFile
->GetChunkLocked(chunkIdx
, CacheFile::WRITER
, nullptr,
407 getter_AddRefs(mChunk
));
410 ("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
411 "[this=%p, idx=%d, rv=0x%08" PRIx32
"]",
412 this, chunkIdx
, static_cast<uint32_t>(rv
)));
413 CloseWithStatusLocked(rv
);
417 void CacheFileOutputStream::FillHole() {
418 mFile
->AssertOwnsLock();
421 MOZ_ASSERT(mPos
/ kChunkSize
== mChunk
->Index());
423 uint32_t pos
= mPos
- (mPos
/ kChunkSize
) * kChunkSize
;
424 if (mChunk
->DataSize() >= pos
) return;
427 ("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
429 mChunk
->Index(), mChunk
->DataSize(), pos
- 1, this));
431 CacheFileChunkWriteHandle hnd
= mChunk
->GetWriteHandle(pos
);
433 CloseWithStatusLocked(NS_ERROR_OUT_OF_MEMORY
);
437 uint32_t offset
= hnd
.DataSize();
438 memset(hnd
.Buf() + offset
, 0, pos
- offset
);
439 hnd
.UpdateDataSize(offset
, pos
- offset
);
442 void CacheFileOutputStream::NotifyListener() {
443 mFile
->AssertOwnsLock();
445 LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
447 MOZ_ASSERT(mCallback
);
449 if (!mCallbackTarget
) {
450 mCallbackTarget
= CacheFileIOManager::IOTarget();
451 if (!mCallbackTarget
) {
453 ("CacheFileOutputStream::NotifyListener() - Cannot get Cache I/O "
454 "thread! Using main thread for callback."));
455 mCallbackTarget
= GetMainThreadSerialEventTarget();
459 nsCOMPtr
<nsIOutputStreamCallback
> asyncCallback
=
460 NS_NewOutputStreamReadyEvent(mCallback
, mCallbackTarget
);
463 mCallbackTarget
= nullptr;
465 asyncCallback
->OnOutputStreamReady(this);
470 size_t CacheFileOutputStream::SizeOfIncludingThis(
471 mozilla::MallocSizeOf mallocSizeOf
) const {
472 // Everything the stream keeps a reference to is already reported somewhere
474 // mFile reports itself.
475 // mChunk reported as part of CacheFile.
476 // mCloseListener is CacheEntry, already reported.
477 // mCallback is usually CacheFile or a class that is reported elsewhere.
478 return mallocSizeOf(this);
481 } // namespace mozilla::net