CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / netwerk / cache / nsDiskCacheStreams.cpp
blobfd2695c05d586552609763293198f91ed32f5c03
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is nsDiskCacheStreams.cpp, released
17 * June 13, 2001.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2001
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Gordon Sheridan <gordon@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 #include "nsDiskCache.h"
43 #include "nsDiskCacheDevice.h"
44 #include "nsDiskCacheStreams.h"
45 #include "nsCacheService.h"
46 #include "mozilla/FileUtils.h"
47 #include "nsIDiskCacheStreamInternal.h"
51 // Assumptions:
52 // - cache descriptors live for life of streams
53 // - streams will only be used by FileTransport,
54 // they will not be directly accessible to clients
55 // - overlapped I/O is NOT supported
58 /******************************************************************************
59 * nsDiskCacheInputStream
60 *****************************************************************************/
61 #ifdef XP_MAC
62 #pragma mark nsDiskCacheInputStream
63 #endif
64 class nsDiskCacheInputStream : public nsIInputStream {
66 public:
68 nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
69 PRFileDesc * fileDesc,
70 const char * buffer,
71 PRUint32 endOfStream);
73 virtual ~nsDiskCacheInputStream();
75 NS_DECL_ISUPPORTS
76 NS_DECL_NSIINPUTSTREAM
78 private:
79 nsDiskCacheStreamIO * mStreamIO; // backpointer to parent
80 PRFileDesc * mFD;
81 const char * mBuffer;
82 PRUint32 mStreamEnd;
83 PRUint32 mPos; // stream position
84 PRBool mClosed;
88 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDiskCacheInputStream, nsIInputStream)
91 nsDiskCacheInputStream::nsDiskCacheInputStream( nsDiskCacheStreamIO * parent,
92 PRFileDesc * fileDesc,
93 const char * buffer,
94 PRUint32 endOfStream)
95 : mStreamIO(parent)
96 , mFD(fileDesc)
97 , mBuffer(buffer)
98 , mStreamEnd(endOfStream)
99 , mPos(0)
100 , mClosed(PR_FALSE)
102 NS_ADDREF(mStreamIO);
103 mStreamIO->IncrementInputStreamCount();
107 nsDiskCacheInputStream::~nsDiskCacheInputStream()
109 Close();
110 mStreamIO->DecrementInputStreamCount();
111 NS_RELEASE(mStreamIO);
115 NS_IMETHODIMP
116 nsDiskCacheInputStream::Close()
118 if (!mClosed) {
119 if (mFD) {
120 (void) PR_Close(mFD);
121 mFD = nsnull;
123 mClosed = PR_TRUE;
125 return NS_OK;
129 NS_IMETHODIMP
130 nsDiskCacheInputStream::Available(PRUint32 * bytesAvailable)
132 if (mClosed) return NS_BASE_STREAM_CLOSED;
133 if (mStreamEnd < mPos) return NS_ERROR_UNEXPECTED;
135 *bytesAvailable = mStreamEnd - mPos;
136 return NS_OK;
140 NS_IMETHODIMP
141 nsDiskCacheInputStream::Read(char * buffer, PRUint32 count, PRUint32 * bytesRead)
143 *bytesRead = 0;
145 if (mClosed)
146 return NS_OK;
148 if (mPos == mStreamEnd) return NS_OK;
149 if (mPos > mStreamEnd) return NS_ERROR_UNEXPECTED;
151 if (mFD) {
152 // just read from file
153 PRInt32 result = PR_Read(mFD, buffer, count);
154 if (result < 0) return NS_ErrorAccordingToNSPR();
156 mPos += (PRUint32)result;
157 *bytesRead = (PRUint32)result;
159 } else if (mBuffer) {
160 // read data from mBuffer
161 if (count > mStreamEnd - mPos)
162 count = mStreamEnd - mPos;
164 memcpy(buffer, mBuffer + mPos, count);
165 mPos += count;
166 *bytesRead = count;
167 } else {
168 // no data source for input stream
171 return NS_OK;
175 NS_IMETHODIMP
176 nsDiskCacheInputStream::ReadSegments(nsWriteSegmentFun writer,
177 void * closure,
178 PRUint32 count,
179 PRUint32 * bytesRead)
181 return NS_ERROR_NOT_IMPLEMENTED;
185 NS_IMETHODIMP
186 nsDiskCacheInputStream::IsNonBlocking(PRBool * nonBlocking)
188 *nonBlocking = PR_FALSE;
189 return NS_OK;
193 /******************************************************************************
194 * nsDiskCacheOutputStream
195 *****************************************************************************/
196 #ifdef XP_MAC
197 #pragma mark -
198 #pragma mark nsDiskCacheOutputStream
199 #endif
200 class nsDiskCacheOutputStream : public nsIOutputStream
201 , public nsIDiskCacheStreamInternal
203 public:
204 nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent);
205 virtual ~nsDiskCacheOutputStream();
207 NS_DECL_ISUPPORTS
208 NS_DECL_NSIOUTPUTSTREAM
209 NS_DECL_NSIDISKCACHESTREAMINTERNAL
211 void ReleaseStreamIO() { NS_IF_RELEASE(mStreamIO); }
213 private:
214 nsDiskCacheStreamIO * mStreamIO; // backpointer to parent
215 PRBool mClosed;
219 NS_IMPL_THREADSAFE_ISUPPORTS2(nsDiskCacheOutputStream,
220 nsIOutputStream,
221 nsIDiskCacheStreamInternal)
223 nsDiskCacheOutputStream::nsDiskCacheOutputStream( nsDiskCacheStreamIO * parent)
224 : mStreamIO(parent)
225 , mClosed(PR_FALSE)
227 NS_ADDREF(mStreamIO);
231 nsDiskCacheOutputStream::~nsDiskCacheOutputStream()
233 Close();
234 ReleaseStreamIO();
238 NS_IMETHODIMP
239 nsDiskCacheOutputStream::Close()
241 if (!mClosed) {
242 mClosed = PR_TRUE;
243 // tell parent streamIO we are closing
244 mStreamIO->CloseOutputStream(this);
246 return NS_OK;
249 NS_IMETHODIMP
250 nsDiskCacheOutputStream::CloseInternal()
252 if (!mClosed) {
253 mClosed = PR_TRUE;
254 // tell parent streamIO we are closing
255 mStreamIO->CloseOutputStreamInternal(this);
257 return NS_OK;
260 NS_IMETHODIMP
261 nsDiskCacheOutputStream::Flush()
263 if (mClosed) return NS_BASE_STREAM_CLOSED;
264 // yeah, yeah, well get to it...eventually...
265 return NS_OK;
269 NS_IMETHODIMP
270 nsDiskCacheOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *bytesWritten)
272 if (mClosed) return NS_BASE_STREAM_CLOSED;
273 return mStreamIO->Write(buf, count, bytesWritten);
277 NS_IMETHODIMP
278 nsDiskCacheOutputStream::WriteFrom(nsIInputStream *inStream, PRUint32 count, PRUint32 *bytesWritten)
280 NS_NOTREACHED("WriteFrom");
281 return NS_ERROR_NOT_IMPLEMENTED;
285 NS_IMETHODIMP
286 nsDiskCacheOutputStream::WriteSegments( nsReadSegmentFun reader,
287 void * closure,
288 PRUint32 count,
289 PRUint32 * bytesWritten)
291 NS_NOTREACHED("WriteSegments");
292 return NS_ERROR_NOT_IMPLEMENTED;
296 NS_IMETHODIMP
297 nsDiskCacheOutputStream::IsNonBlocking(PRBool * nonBlocking)
299 *nonBlocking = PR_FALSE;
300 return NS_OK;
305 /******************************************************************************
306 * nsDiskCacheStreamIO
307 *****************************************************************************/
308 #ifdef XP_MAC
309 #pragma mark -
310 #pragma mark nsDiskCacheStreamIO
311 #endif
313 NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheStreamIO)
315 // we pick 16k as the max buffer size because that is the threshold above which
316 // we are unable to store the data in the cache block files
317 // see nsDiskCacheMap.[cpp,h]
318 #define kMaxBufferSize (16 * 1024)
320 nsDiskCacheStreamIO::nsDiskCacheStreamIO(nsDiskCacheBinding * binding)
321 : mBinding(binding)
322 , mOutStream(nsnull)
323 , mInStreamCount(0)
324 , mFD(nsnull)
325 , mStreamPos(0)
326 , mStreamEnd(0)
327 , mBufPos(0)
328 , mBufEnd(0)
329 , mBufSize(0)
330 , mBufDirty(PR_FALSE)
331 , mBuffer(nsnull)
333 mDevice = (nsDiskCacheDevice *)mBinding->mCacheEntry->CacheDevice();
335 // acquire "death grip" on cache service
336 nsCacheService *service = nsCacheService::GlobalInstance();
337 NS_ADDREF(service);
341 nsDiskCacheStreamIO::~nsDiskCacheStreamIO()
343 Close();
345 // release "death grip" on cache service
346 nsCacheService *service = nsCacheService::GlobalInstance();
347 NS_RELEASE(service);
351 void
352 nsDiskCacheStreamIO::Close()
354 // this should only be called from our destructor
355 // no one is interested in us anymore, so we don't need to grab any locks
357 // assert streams closed
358 NS_ASSERTION(!mOutStream, "output stream still open");
359 NS_ASSERTION(mInStreamCount == 0, "input stream still open");
360 NS_ASSERTION(!mFD, "file descriptor not closed");
362 DeleteBuffer();
366 // NOTE: called with service lock held
367 nsresult
368 nsDiskCacheStreamIO::GetInputStream(PRUint32 offset, nsIInputStream ** inputStream)
370 NS_ENSURE_ARG_POINTER(inputStream);
371 NS_ENSURE_TRUE(offset == 0, NS_ERROR_NOT_IMPLEMENTED);
373 *inputStream = nsnull;
375 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
377 if (mOutStream) {
378 NS_WARNING("already have an output stream open");
379 return NS_ERROR_NOT_AVAILABLE;
382 nsresult rv;
383 PRFileDesc * fd = nsnull;
385 mStreamEnd = mBinding->mCacheEntry->DataSize();
386 if (mStreamEnd == 0) {
387 // there's no data to read
388 NS_ASSERTION(!mBinding->mRecord.DataLocationInitialized(), "storage allocated for zero data size");
389 } else if (mBinding->mRecord.DataFile() == 0) {
390 // open file desc for data
391 rv = OpenCacheFile(PR_RDONLY, &fd);
392 if (NS_FAILED(rv)) return rv; // unable to open file
393 NS_ASSERTION(fd, "cache stream lacking open file.");
395 } else if (!mBuffer) {
396 // read block file for data
397 rv = ReadCacheBlocks();
398 if (NS_FAILED(rv)) return rv;
400 // else, mBuffer already contains all of the data (left over from a
401 // previous block-file read or write).
403 NS_ASSERTION(!(fd && mBuffer), "ambiguous data sources for input stream");
405 // create a new input stream
406 nsDiskCacheInputStream * inStream = new nsDiskCacheInputStream(this, fd, mBuffer, mStreamEnd);
407 if (!inStream) return NS_ERROR_OUT_OF_MEMORY;
409 NS_ADDREF(*inputStream = inStream);
410 return NS_OK;
414 // NOTE: called with service lock held
415 nsresult
416 nsDiskCacheStreamIO::GetOutputStream(PRUint32 offset, nsIOutputStream ** outputStream)
418 NS_ENSURE_ARG_POINTER(outputStream);
419 *outputStream = nsnull;
421 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
423 NS_ASSERTION(!mOutStream, "already have an output stream open");
424 NS_ASSERTION(mInStreamCount == 0, "we already have input streams open");
425 if (mOutStream || mInStreamCount) return NS_ERROR_NOT_AVAILABLE;
427 // mBuffer lazily allocated, but might exist if a previous stream already
428 // created one.
429 mBufPos = 0;
430 mStreamPos = 0;
431 mStreamEnd = mBinding->mCacheEntry->DataSize();
433 nsresult rv;
434 if (offset) {
435 rv = Seek(PR_SEEK_SET, offset);
436 if (NS_FAILED(rv)) return rv;
438 rv = SetEOF();
439 if (NS_FAILED(rv)) return rv;
441 // create a new output stream
442 mOutStream = new nsDiskCacheOutputStream(this);
443 if (!mOutStream) return NS_ERROR_OUT_OF_MEMORY;
445 NS_ADDREF(*outputStream = mOutStream);
446 return NS_OK;
449 void
450 nsDiskCacheStreamIO::ClearBinding()
452 if (mBinding && mOutStream)
453 Flush();
454 mBinding = nsnull;
457 nsresult
458 nsDiskCacheStreamIO::CloseOutputStream(nsDiskCacheOutputStream * outputStream)
460 nsCacheServiceAutoLock lock; // grab service lock
461 return CloseOutputStreamInternal(outputStream);
464 nsresult
465 nsDiskCacheStreamIO::CloseOutputStreamInternal(
466 nsDiskCacheOutputStream * outputStream)
468 nsresult rv;
470 if (outputStream != mOutStream) {
471 NS_WARNING("mismatched output streams");
472 return NS_ERROR_UNEXPECTED;
475 // output stream is closing
476 if (!mBinding) { // if we're severed, just clear member variables
477 NS_ASSERTION(!mBufDirty, "oops");
478 mOutStream = nsnull;
479 outputStream->ReleaseStreamIO();
480 return NS_ERROR_NOT_AVAILABLE;
483 rv = Flush();
484 if (NS_FAILED(rv))
485 NS_WARNING("Flush() failed");
487 mOutStream = nsnull;
488 return rv;
491 nsresult
492 nsDiskCacheStreamIO::Flush()
494 NS_ASSERTION(mBinding, "oops");
496 CACHE_LOG_DEBUG(("CACHE: Flush [%x doomed=%u]\n",
497 mBinding->mRecord.HashNumber(), mBinding->mDoomed));
499 if (!mBufDirty)
500 return NS_OK;
502 // write data to cache blocks, or flush mBuffer to file
503 nsDiskCacheMap *cacheMap = mDevice->CacheMap(); // get map reference
504 nsresult rv;
506 PRBool written = PR_FALSE;
508 if ((mStreamEnd <= kMaxBufferSize) &&
509 (mBinding->mCacheEntry->StoragePolicy() != nsICache::STORE_ON_DISK_AS_FILE)) {
510 // store data (if any) in cache block files
512 mBufDirty = PR_FALSE;
514 // delete existing storage
515 nsDiskCacheRecord * record = &mBinding->mRecord;
516 if (record->DataLocationInitialized()) {
517 rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
518 if (NS_FAILED(rv)) {
519 NS_WARNING("cacheMap->DeleteStorage() failed.");
520 cacheMap->DeleteRecord(record);
521 return rv;
525 // flush buffer to block files
526 written = PR_TRUE;
527 if (mStreamEnd > 0) {
528 rv = cacheMap->WriteDataCacheBlocks(mBinding, mBuffer, mBufEnd);
529 if (NS_FAILED(rv)) {
530 NS_WARNING("WriteDataCacheBlocks() failed.");
531 written = PR_FALSE;
536 if (!written) {
537 // make sure we save as separate file
538 rv = FlushBufferToFile(); // initializes DataFileLocation() if necessary
540 if (mFD) {
541 // Update the file size of the disk file in the cache
542 UpdateFileSize();
544 // close file descriptor
545 (void) PR_Close(mFD);
546 mFD = nsnull;
548 else
549 NS_WARNING("no file descriptor");
551 // close mFD first if possible before returning if FlushBufferToFile
552 // failed
553 NS_ENSURE_SUCCESS(rv, rv);
555 // since the data location is on disk as a single file, the only value
556 // in keeping mBuffer around is to avoid an extra malloc the next time
557 // we need to write to this file. reading will use a file descriptor.
558 // therefore, it's probably not worth optimizing for the subsequent
559 // write, so we unconditionally delete mBuffer here.
560 DeleteBuffer();
563 // XXX do we need this here? WriteDataCacheBlocks() calls UpdateRecord()
564 // update cache map if entry isn't doomed
565 if (!mBinding->mDoomed) {
566 rv = cacheMap->UpdateRecord(&mBinding->mRecord);
567 if (NS_FAILED(rv)) {
568 NS_WARNING("cacheMap->UpdateRecord() failed.");
569 return rv; // XXX doom cache entry
573 return NS_OK;
577 // assumptions:
578 // only one thread writing at a time
579 // never have both output and input streams open
580 // OnDataSizeChanged() will have already been called to update entry->DataSize()
582 nsresult
583 nsDiskCacheStreamIO::Write( const char * buffer,
584 PRUint32 count,
585 PRUint32 * bytesWritten)
587 nsresult rv = NS_OK;
588 nsCacheServiceAutoLock lock; // grab service lock
589 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
591 if (mInStreamCount) {
592 // we have open input streams already
593 // this is an error until we support overlapped I/O
594 NS_WARNING("Attempting to write to cache entry with open input streams.\n");
595 return NS_ERROR_NOT_AVAILABLE;
598 NS_ASSERTION(count, "Write called with count of zero");
599 NS_ASSERTION(mBufPos <= mBufEnd, "streamIO buffer corrupted");
601 PRUint32 bytesLeft = count;
602 PRBool flushed = PR_FALSE;
604 while (bytesLeft) {
605 if (mBufPos == mBufSize) {
606 if (mBufSize < kMaxBufferSize) {
607 mBufSize = kMaxBufferSize;
608 char *buffer = mBuffer;
610 mBuffer = (char *) realloc(mBuffer, mBufSize);
611 if (!mBuffer) {
612 free(buffer);
613 mBufSize = 0;
614 break;
616 } else {
617 nsresult rv = FlushBufferToFile();
618 if (NS_FAILED(rv)) break;
619 flushed = PR_TRUE;
623 PRUint32 chunkSize = bytesLeft;
624 if (chunkSize > (mBufSize - mBufPos))
625 chunkSize = mBufSize - mBufPos;
627 memcpy(mBuffer + mBufPos, buffer, chunkSize);
628 mBufDirty = PR_TRUE;
629 mBufPos += chunkSize;
630 bytesLeft -= chunkSize;
631 buffer += chunkSize;
633 if (mBufEnd < mBufPos)
634 mBufEnd = mBufPos;
636 if (bytesLeft) {
637 *bytesWritten = 0;
638 return NS_ERROR_FAILURE;
640 *bytesWritten = count;
642 // update mStreamPos, mStreamEnd
643 mStreamPos += count;
644 if (mStreamEnd < mStreamPos) {
645 mStreamEnd = mStreamPos;
646 NS_ASSERTION(mBinding->mCacheEntry->DataSize() == mStreamEnd, "bad stream");
648 // If we have flushed to a file, update the file size
649 if (flushed && mFD) {
650 UpdateFileSize();
654 return rv;
658 void
659 nsDiskCacheStreamIO::UpdateFileSize()
661 NS_ASSERTION(mFD, "nsDiskCacheStreamIO::UpdateFileSize should not have been called");
663 nsDiskCacheRecord * record = &mBinding->mRecord;
664 const PRUint32 oldSizeK = record->DataFileSize();
665 const PRUint32 newSizeK = (mStreamEnd + 0x03FF) >> 10;
667 if (newSizeK == oldSizeK) return;
669 record->SetDataFileSize(newSizeK);
671 // update cache size totals
672 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
673 cacheMap->DecrementTotalSize(oldSizeK); // decrement old size
674 cacheMap->IncrementTotalSize(newSizeK); // increment new size
676 if (!mBinding->mDoomed) {
677 nsresult rv = cacheMap->UpdateRecord(record);
678 if (NS_FAILED(rv)) {
679 NS_WARNING("cacheMap->UpdateRecord() failed.");
680 // XXX doom cache entry?
686 nsresult
687 nsDiskCacheStreamIO::OpenCacheFile(PRIntn flags, PRFileDesc ** fd)
689 NS_ENSURE_ARG_POINTER(fd);
691 nsresult rv;
692 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
694 rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord,
695 nsDiskCache::kData,
696 !!(flags & PR_CREATE_FILE),
697 getter_AddRefs(mLocalFile));
698 if (NS_FAILED(rv)) return rv;
700 // create PRFileDesc for input stream - the 00600 is just for consistency
701 rv = mLocalFile->OpenNSPRFileDesc(flags, 00600, fd);
702 if (NS_FAILED(rv)) return rv; // unable to open file
704 return NS_OK;
708 nsresult
709 nsDiskCacheStreamIO::ReadCacheBlocks()
711 NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "bad stream");
712 NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "data too large for buffer");
714 nsDiskCacheRecord * record = &mBinding->mRecord;
715 if (!record->DataLocationInitialized()) return NS_OK;
717 NS_ASSERTION(record->DataFile() != kSeparateFile, "attempt to read cache blocks on separate file");
719 if (!mBuffer) {
720 // allocate buffer
721 mBuffer = (char *) malloc(mStreamEnd);
722 if (!mBuffer) {
723 return NS_ERROR_OUT_OF_MEMORY;
725 mBufSize = mStreamEnd;
728 // read data stored in cache block files
729 nsDiskCacheMap *map = mDevice->CacheMap(); // get map reference
730 nsresult rv = map->ReadDataCacheBlocks(mBinding, mBuffer, mStreamEnd);
731 if (NS_FAILED(rv)) return rv;
733 // update streamIO variables
734 mBufPos = 0;
735 mBufEnd = mStreamEnd;
737 return NS_OK;
741 nsresult
742 nsDiskCacheStreamIO::FlushBufferToFile()
744 nsresult rv;
745 nsDiskCacheRecord * record = &mBinding->mRecord;
747 if (!mFD) {
748 if (record->DataLocationInitialized() && (record->DataFile() > 0)) {
749 // remove cache block storage
750 nsDiskCacheMap * cacheMap = mDevice->CacheMap();
751 rv = cacheMap->DeleteStorage(record, nsDiskCache::kData);
752 if (NS_FAILED(rv)) return rv;
754 record->SetDataFileGeneration(mBinding->mGeneration);
756 // allocate file
757 rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
758 if (NS_FAILED(rv)) return rv;
760 PRInt64 dataSize = mBinding->mCacheEntry->PredictedDataSize();
761 // Appears to cause bug 617123? Disabled for now.
762 #if 0
763 if (dataSize != -1)
764 mozilla::fallocate(mFD, PR_MIN(dataSize, kPreallocateLimit));
765 #endif
768 // write buffer
769 PRInt32 bytesWritten = PR_Write(mFD, mBuffer, mBufEnd);
770 if (PRUint32(bytesWritten) != mBufEnd) {
771 NS_WARNING("failed to flush all data");
772 return NS_ERROR_UNEXPECTED; // NS_ErrorAccordingToNSPR()
774 mBufDirty = PR_FALSE;
776 // reset buffer
777 mBufPos = 0;
778 mBufEnd = 0;
780 return NS_OK;
784 void
785 nsDiskCacheStreamIO::DeleteBuffer()
787 if (mBuffer) {
788 NS_ASSERTION(!mBufDirty, "deleting dirty buffer");
789 free(mBuffer);
790 mBuffer = nsnull;
791 mBufPos = 0;
792 mBufEnd = 0;
793 mBufSize = 0;
798 // NOTE: called with service lock held
799 nsresult
800 nsDiskCacheStreamIO::Seek(PRInt32 whence, PRInt32 offset)
802 PRInt32 newPos;
803 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
805 if (PRUint32(offset) > mStreamEnd) return NS_ERROR_FAILURE;
807 if (mBinding->mRecord.DataLocationInitialized()) {
808 if (mBinding->mRecord.DataFile() == 0) {
809 if (!mFD) {
810 // we need an mFD, we better open it now
811 nsresult rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
812 if (NS_FAILED(rv)) return rv;
817 if (mFD) {
818 // do we have data in the buffer that needs to be flushed?
819 if (mBufDirty) {
820 // XXX optimization: are we just moving within the current buffer?
821 nsresult rv = FlushBufferToFile();
822 if (NS_FAILED(rv)) return rv;
825 newPos = PR_Seek(mFD, offset, (PRSeekWhence)whence);
826 if (newPos == -1)
827 return NS_ErrorAccordingToNSPR();
829 mStreamPos = (PRUint32) newPos;
830 mBufPos = 0;
831 mBufEnd = 0;
832 return NS_OK;
835 // else, seek in mBuffer
837 switch(whence) {
838 case PR_SEEK_SET:
839 newPos = offset;
840 break;
842 case PR_SEEK_CUR: // relative from current posistion
843 newPos = offset + (PRUint32)mStreamPos;
844 break;
846 case PR_SEEK_END: // relative from end
847 newPos = offset + (PRUint32)mBufEnd;
848 break;
850 default:
851 return NS_ERROR_INVALID_ARG;
854 // read data into mBuffer if not read yet.
855 if (mStreamEnd && !mBufEnd) {
856 if (newPos > 0) {
857 nsresult rv = ReadCacheBlocks();
858 if (NS_FAILED(rv)) return rv;
862 // stream buffer sanity checks
863 NS_ASSERTION(mBufEnd <= kMaxBufferSize, "bad stream");
864 NS_ASSERTION(mBufPos <= mBufEnd, "bad stream");
865 NS_ASSERTION(mStreamPos == mBufPos, "bad stream");
866 NS_ASSERTION(mStreamEnd == mBufEnd, "bad stream");
868 if ((newPos < 0) || (PRUint32(newPos) > mBufEnd)) {
869 NS_WARNING("seek offset out of range");
870 return NS_ERROR_INVALID_ARG;
873 mStreamPos = newPos;
874 mBufPos = newPos;
875 return NS_OK;
879 // called only from nsDiskCacheOutputStream::Tell
880 nsresult
881 nsDiskCacheStreamIO::Tell(PRUint32 * result)
883 NS_ENSURE_ARG_POINTER(result);
884 *result = mStreamPos;
885 return NS_OK;
889 // NOTE: called with service lock held
890 nsresult
891 nsDiskCacheStreamIO::SetEOF()
893 nsresult rv;
894 PRBool needToCloseFD = PR_FALSE;
896 NS_ASSERTION(mStreamPos <= mStreamEnd, "bad stream");
897 if (!mBinding) return NS_ERROR_NOT_AVAILABLE;
899 if (mBinding->mRecord.DataLocationInitialized()) {
900 if (mBinding->mRecord.DataFile() == 0) {
901 if (!mFD) {
902 // we need an mFD, we better open it now
903 rv = OpenCacheFile(PR_RDWR | PR_CREATE_FILE, &mFD);
904 if (NS_FAILED(rv)) return rv;
905 needToCloseFD = PR_TRUE;
907 } else {
908 // data in cache block files
909 if ((mStreamPos != 0) && (mStreamPos != mBufPos)) {
910 // only read data if there will be some left after truncation
911 rv = ReadCacheBlocks();
912 if (NS_FAILED(rv)) return rv;
915 // We need to make sure we reflect this change in Flush().
916 // In particular, if mStreamPos is 0 and we never write to
917 // the buffer, we want the storage to be deleted.
918 mBufDirty = PR_TRUE;
922 if (mFD) {
923 rv = nsDiskCache::Truncate(mFD, mStreamPos);
924 #ifdef DEBUG
925 PRUint32 oldSizeK = (mStreamEnd + 0x03FF) >> 10;
926 NS_ASSERTION(mBinding->mRecord.DataFileSize() == oldSizeK, "bad disk cache entry size");
927 } else {
928 // data stored in buffer.
929 NS_ASSERTION(mStreamEnd <= kMaxBufferSize, "buffer truncation inadequate");
930 NS_ASSERTION(mBufPos == mStreamPos, "bad stream");
931 NS_ASSERTION(mBuffer ? mBufEnd == mStreamEnd : PR_TRUE, "bad stream");
932 #endif
935 NS_ASSERTION(mStreamEnd == mBinding->mCacheEntry->DataSize(), "cache entry not updated");
936 // we expect nsCacheEntryDescriptor::TransportWrapper::OpenOutputStream()
937 // to eventually update the cache entry
939 mStreamEnd = mStreamPos;
940 mBufEnd = mBufPos;
942 if (mFD) {
943 UpdateFileSize();
944 if (needToCloseFD) {
945 (void) PR_Close(mFD);
946 mFD = nsnull;
950 return NS_OK;