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"
18 NS_IMPL_ADDREF(CacheFileOutputStream
)
19 NS_IMETHODIMP_(MozExternalRefCountType
)
20 CacheFileOutputStream::Release() {
21 MOZ_ASSERT(0 != mRefCnt
, "dup release");
22 nsrefcnt count
= --mRefCnt
;
23 NS_LOG_RELEASE(this, count
, "CacheFileOutputStream");
28 CacheFileAutoLock
lock(mFile
);
29 mFile
->RemoveOutput(this, mStatus
);
38 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream
)
39 NS_INTERFACE_MAP_ENTRY(nsIOutputStream
)
40 NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream
)
41 NS_INTERFACE_MAP_ENTRY(nsISeekableStream
)
42 NS_INTERFACE_MAP_ENTRY(nsITellableStream
)
43 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener
)
44 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIOutputStream
)
47 CacheFileOutputStream::CacheFileOutputStream(
48 CacheFile
* aFile
, CacheOutputCloseListener
* aCloseListener
,
49 bool aAlternativeData
)
51 mCloseListener(aCloseListener
),
54 mAlternativeData(aAlternativeData
),
57 LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
59 if (mAlternativeData
) {
60 mPos
= mFile
->mAltDataOffset
;
64 CacheFileOutputStream::~CacheFileOutputStream() {
65 LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
70 CacheFileOutputStream::Close() {
71 LOG(("CacheFileOutputStream::Close() [this=%p]", this));
72 return CloseWithStatus(NS_OK
);
76 CacheFileOutputStream::Flush() {
77 // TODO do we need to implement flush ???
78 LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
83 CacheFileOutputStream::Write(const char* aBuf
, uint32_t aCount
,
85 CacheFileAutoLock
lock(mFile
);
87 LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount
));
91 ("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
92 "status=0x%08" PRIx32
"]",
93 this, static_cast<uint32_t>(mStatus
)));
95 return NS_FAILED(mStatus
) ? mStatus
: NS_BASE_STREAM_CLOSED
;
98 if (!mFile
->mSkipSizeCheck
&&
99 CacheObserver::EntryIsTooBig(mPos
+ aCount
, !mFile
->mMemoryOnly
)) {
100 LOG(("CacheFileOutputStream::Write() - Entry is too big. [this=%p]", this));
102 CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG
);
103 return NS_ERROR_FILE_TOO_BIG
;
106 // We use 64-bit offset when accessing the file, unfortunately we use 32-bit
107 // metadata offset, so we cannot handle data bigger than 4GB.
108 if (mPos
+ aCount
> PR_UINT32_MAX
) {
109 LOG(("CacheFileOutputStream::Write() - Entry's size exceeds 4GB. [this=%p]",
112 CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG
);
113 return NS_ERROR_FILE_TOO_BIG
;
119 EnsureCorrectChunk(false);
120 if (NS_FAILED(mStatus
)) {
125 if (NS_FAILED(mStatus
)) {
129 uint32_t chunkOffset
= mPos
- (mPos
/ kChunkSize
) * kChunkSize
;
130 uint32_t canWrite
= kChunkSize
- chunkOffset
;
131 uint32_t thisWrite
= std::min(static_cast<uint32_t>(canWrite
), aCount
);
133 CacheFileChunkWriteHandle hnd
=
134 mChunk
->GetWriteHandle(chunkOffset
+ thisWrite
);
136 CloseWithStatusLocked(NS_ERROR_OUT_OF_MEMORY
);
137 return NS_ERROR_OUT_OF_MEMORY
;
140 memcpy(hnd
.Buf() + chunkOffset
, aBuf
, thisWrite
);
141 hnd
.UpdateDataSize(chunkOffset
, thisWrite
);
148 EnsureCorrectChunk(true);
150 LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]", *_retval
,
157 CacheFileOutputStream::WriteFrom(nsIInputStream
* aFromStream
, uint32_t aCount
,
160 ("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
162 this, aFromStream
, aCount
));
164 return NS_ERROR_NOT_IMPLEMENTED
;
168 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader
, void* aClosure
,
169 uint32_t aCount
, uint32_t* _retval
) {
171 ("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
175 return NS_ERROR_NOT_IMPLEMENTED
;
179 CacheFileOutputStream::IsNonBlocking(bool* _retval
) {
184 // nsIAsyncOutputStream
186 CacheFileOutputStream::CloseWithStatus(nsresult aStatus
) {
187 CacheFileAutoLock
lock(mFile
);
189 LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32
191 this, static_cast<uint32_t>(aStatus
)));
193 return CloseWithStatusLocked(aStatus
);
196 nsresult
CacheFileOutputStream::CloseWithStatusLocked(nsresult aStatus
) {
198 ("CacheFileOutputStream::CloseWithStatusLocked() [this=%p, "
199 "aStatus=0x%08" PRIx32
"]",
200 this, static_cast<uint32_t>(aStatus
)));
203 MOZ_ASSERT(!mCallback
);
208 mStatus
= NS_FAILED(aStatus
) ? aStatus
: NS_BASE_STREAM_CLOSED
;
218 mFile
->RemoveOutput(this, mStatus
);
224 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback
* aCallback
,
225 uint32_t aFlags
, uint32_t aRequestedCount
,
226 nsIEventTarget
* aEventTarget
) {
227 CacheFileAutoLock
lock(mFile
);
230 ("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
231 "requestedCount=%d, eventTarget=%p]",
232 this, aCallback
, aFlags
, aRequestedCount
, aEventTarget
));
234 mCallback
= aCallback
;
235 mCallbackFlags
= aFlags
;
236 mCallbackTarget
= aEventTarget
;
238 if (!mCallback
) return NS_OK
;
240 // The stream is blocking so it is writable at any time
241 if (mClosed
|| !(aFlags
& WAIT_CLOSURE_ONLY
)) NotifyListener();
248 CacheFileOutputStream::Seek(int32_t whence
, int64_t offset
) {
249 CacheFileAutoLock
lock(mFile
);
251 LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%" PRId64
"]",
252 this, whence
, offset
));
255 LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
256 return NS_BASE_STREAM_CLOSED
;
259 int64_t newPos
= offset
;
262 if (mAlternativeData
) {
263 newPos
+= mFile
->mAltDataOffset
;
270 if (mAlternativeData
) {
271 newPos
+= mFile
->mDataSize
;
273 newPos
+= mFile
->mAltDataOffset
;
277 NS_ERROR("invalid whence");
278 return NS_ERROR_INVALID_ARG
;
281 EnsureCorrectChunk(true);
283 LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%" PRId64
"]", this, mPos
));
288 CacheFileOutputStream::SetEOF() {
289 MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
290 // Right now we don't use SetEOF(). If we ever need this method, we need
291 // to think about what to do with input streams that already points beyond
293 return NS_ERROR_NOT_IMPLEMENTED
;
298 CacheFileOutputStream::Tell(int64_t* _retval
) {
299 CacheFileAutoLock
lock(mFile
);
302 LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
303 return NS_BASE_STREAM_CLOSED
;
308 if (mAlternativeData
) {
309 *_retval
-= mFile
->mAltDataOffset
;
312 LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%" PRId64
"]", this,
317 // CacheFileChunkListener
318 nsresult
CacheFileOutputStream::OnChunkRead(nsresult aResult
,
319 CacheFileChunk
* aChunk
) {
320 MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
321 return NS_ERROR_UNEXPECTED
;
324 nsresult
CacheFileOutputStream::OnChunkWritten(nsresult aResult
,
325 CacheFileChunk
* aChunk
) {
326 MOZ_CRASH("CacheFileOutputStream::OnChunkWritten should not be called!");
327 return NS_ERROR_UNEXPECTED
;
330 nsresult
CacheFileOutputStream::OnChunkAvailable(nsresult aResult
,
332 CacheFileChunk
* aChunk
) {
333 MOZ_CRASH("CacheFileOutputStream::OnChunkAvailable should not be called!");
334 return NS_ERROR_UNEXPECTED
;
337 nsresult
CacheFileOutputStream::OnChunkUpdated(CacheFileChunk
* aChunk
) {
338 MOZ_CRASH("CacheFileOutputStream::OnChunkUpdated should not be called!");
339 return NS_ERROR_UNEXPECTED
;
342 void CacheFileOutputStream::NotifyCloseListener() {
343 RefPtr
<CacheOutputCloseListener
> listener
;
344 listener
.swap(mCloseListener
);
345 if (!listener
) return;
347 listener
->OnOutputClosed();
350 void CacheFileOutputStream::ReleaseChunk() {
351 LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]", this,
354 // If the chunk didn't write any data we need to remove hash for this chunk
355 // that was added when the chunk was created in CacheFile::GetChunkLocked.
356 if (mChunk
->DataSize() == 0) {
357 // It must be due to a failure, we don't create a new chunk when we don't
358 // have data to write.
359 MOZ_ASSERT(NS_FAILED(mChunk
->GetStatus()));
360 mFile
->mMetadata
->RemoveHash(mChunk
->Index());
363 mFile
->ReleaseOutsideLock(std::move(mChunk
));
366 void CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly
) {
367 mFile
->AssertOwnsLock();
369 LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
370 this, aReleaseOnly
));
372 uint32_t chunkIdx
= mPos
/ kChunkSize
;
375 if (mChunk
->Index() == chunkIdx
) {
376 // we have a correct chunk
378 ("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
387 if (aReleaseOnly
) return;
390 rv
= mFile
->GetChunkLocked(chunkIdx
, CacheFile::WRITER
, nullptr,
391 getter_AddRefs(mChunk
));
394 ("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
395 "[this=%p, idx=%d, rv=0x%08" PRIx32
"]",
396 this, chunkIdx
, static_cast<uint32_t>(rv
)));
397 CloseWithStatusLocked(rv
);
401 void CacheFileOutputStream::FillHole() {
402 mFile
->AssertOwnsLock();
405 MOZ_ASSERT(mPos
/ kChunkSize
== mChunk
->Index());
407 uint32_t pos
= mPos
- (mPos
/ kChunkSize
) * kChunkSize
;
408 if (mChunk
->DataSize() >= pos
) return;
411 ("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
413 mChunk
->Index(), mChunk
->DataSize(), pos
- 1, this));
415 CacheFileChunkWriteHandle hnd
= mChunk
->GetWriteHandle(pos
);
417 CloseWithStatusLocked(NS_ERROR_OUT_OF_MEMORY
);
421 uint32_t offset
= hnd
.DataSize();
422 memset(hnd
.Buf() + offset
, 0, pos
- offset
);
423 hnd
.UpdateDataSize(offset
, pos
- offset
);
426 void CacheFileOutputStream::NotifyListener() {
427 mFile
->AssertOwnsLock();
429 LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
431 MOZ_ASSERT(mCallback
);
433 if (!mCallbackTarget
) {
434 mCallbackTarget
= CacheFileIOManager::IOTarget();
435 if (!mCallbackTarget
) {
437 ("CacheFileOutputStream::NotifyListener() - Cannot get Cache I/O "
438 "thread! Using main thread for callback."));
439 mCallbackTarget
= GetMainThreadEventTarget();
443 nsCOMPtr
<nsIOutputStreamCallback
> asyncCallback
=
444 NS_NewOutputStreamReadyEvent(mCallback
, mCallbackTarget
);
447 mCallbackTarget
= nullptr;
449 asyncCallback
->OnOutputStreamReady(this);
454 size_t CacheFileOutputStream::SizeOfIncludingThis(
455 mozilla::MallocSizeOf mallocSizeOf
) const {
456 // Everything the stream keeps a reference to is already reported somewhere
458 // mFile reports itself.
459 // mChunk reported as part of CacheFile.
460 // mCloseListener is CacheEntry, already reported.
461 // mCallback is usually CacheFile or a class that is reported elsewhere.
462 return mallocSizeOf(this);
466 } // namespace mozilla