Bug 783551 - Get tooltool running on the b2g on OS X builds. r=respindola
[gecko.git] / netwerk / cache / nsCacheEntryDescriptor.cpp
blobe17f6c6fa93779e8fd1d331c5ecea81b3aabf5b5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsICache.h"
8 #include "nsCache.h"
9 #include "nsCacheService.h"
10 #include "nsCacheEntryDescriptor.h"
11 #include "nsCacheEntry.h"
12 #include "nsReadableUtils.h"
13 #include "nsIOutputStream.h"
14 #include "nsCRT.h"
16 #define kMinDecompressReadBufLen 1024
17 #define kMinCompressWriteBufLen 1024
19 NS_IMPL_THREADSAFE_ISUPPORTS2(nsCacheEntryDescriptor,
20 nsICacheEntryDescriptor,
21 nsICacheEntryInfo)
23 nsCacheEntryDescriptor::nsCacheEntryDescriptor(nsCacheEntry * entry,
24 nsCacheAccessMode accessGranted)
25 : mCacheEntry(entry),
26 mAccessGranted(accessGranted),
27 mOutput(nullptr)
29 PR_INIT_CLIST(this);
30 NS_ADDREF(nsCacheService::GlobalInstance()); // ensure it lives for the lifetime of the descriptor
34 nsCacheEntryDescriptor::~nsCacheEntryDescriptor()
36 // No need to close if the cache entry has already been severed. This
37 // helps avoid a shutdown assertion (bug 285519) that is caused when
38 // consumers end up holding onto these objects past xpcom-shutdown. It's
39 // okay for them to do that because the cache service calls our Close
40 // method during xpcom-shutdown, so we don't need to complain about it.
41 if (mCacheEntry)
42 Close();
44 nsCacheService * service = nsCacheService::GlobalInstance();
45 NS_RELEASE(service);
49 NS_IMETHODIMP
50 nsCacheEntryDescriptor::GetClientID(char ** result)
52 NS_ENSURE_ARG_POINTER(result);
54 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCLIENTID));
55 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
57 return ClientIDFromCacheKey(*(mCacheEntry->Key()), result);
61 NS_IMETHODIMP
62 nsCacheEntryDescriptor::GetDeviceID(char ** aDeviceID)
64 NS_ENSURE_ARG_POINTER(aDeviceID);
65 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDEVICEID));
66 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
68 const char* deviceID = mCacheEntry->GetDeviceID();
69 if (!deviceID) {
70 *aDeviceID = nullptr;
71 return NS_OK;
74 *aDeviceID = NS_strdup(deviceID);
75 return *aDeviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
79 NS_IMETHODIMP
80 nsCacheEntryDescriptor::GetKey(nsACString &result)
82 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETKEY));
83 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
85 return ClientKeyFromCacheKey(*(mCacheEntry->Key()), result);
89 NS_IMETHODIMP
90 nsCacheEntryDescriptor::GetFetchCount(PRInt32 *result)
92 NS_ENSURE_ARG_POINTER(result);
93 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFETCHCOUNT));
94 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
96 *result = mCacheEntry->FetchCount();
97 return NS_OK;
101 NS_IMETHODIMP
102 nsCacheEntryDescriptor::GetLastFetched(PRUint32 *result)
104 NS_ENSURE_ARG_POINTER(result);
105 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTFETCHED));
106 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
108 *result = mCacheEntry->LastFetched();
109 return NS_OK;
113 NS_IMETHODIMP
114 nsCacheEntryDescriptor::GetLastModified(PRUint32 *result)
116 NS_ENSURE_ARG_POINTER(result);
117 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETLASTMODIFIED));
118 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
120 *result = mCacheEntry->LastModified();
121 return NS_OK;
125 NS_IMETHODIMP
126 nsCacheEntryDescriptor::GetExpirationTime(PRUint32 *result)
128 NS_ENSURE_ARG_POINTER(result);
129 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETEXPIRATIONTIME));
130 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
132 *result = mCacheEntry->ExpirationTime();
133 return NS_OK;
137 NS_IMETHODIMP
138 nsCacheEntryDescriptor::SetExpirationTime(PRUint32 expirationTime)
140 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETEXPIRATIONTIME));
141 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
143 mCacheEntry->SetExpirationTime(expirationTime);
144 mCacheEntry->MarkEntryDirty();
145 return NS_OK;
149 NS_IMETHODIMP nsCacheEntryDescriptor::IsStreamBased(bool *result)
151 NS_ENSURE_ARG_POINTER(result);
152 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_ISSTREAMBASED));
153 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
155 *result = mCacheEntry->IsStreamData();
156 return NS_OK;
159 NS_IMETHODIMP nsCacheEntryDescriptor::GetPredictedDataSize(PRInt64 *result)
161 NS_ENSURE_ARG_POINTER(result);
162 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETPREDICTEDDATASIZE));
163 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
165 *result = mCacheEntry->PredictedDataSize();
166 return NS_OK;
169 NS_IMETHODIMP nsCacheEntryDescriptor::SetPredictedDataSize(PRInt64
170 predictedSize)
172 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETPREDICTEDDATASIZE));
173 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
175 mCacheEntry->SetPredictedDataSize(predictedSize);
176 return NS_OK;
179 NS_IMETHODIMP nsCacheEntryDescriptor::GetDataSize(PRUint32 *result)
181 NS_ENSURE_ARG_POINTER(result);
182 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETDATASIZE));
183 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
185 const char* val = mCacheEntry->GetMetaDataElement("uncompressed-len");
186 if (!val) {
187 *result = mCacheEntry->DataSize();
188 } else {
189 *result = atol(val);
192 return NS_OK;
196 NS_IMETHODIMP nsCacheEntryDescriptor::GetStorageDataSize(PRUint32 *result)
198 NS_ENSURE_ARG_POINTER(result);
199 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEDATASIZE));
200 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
202 *result = mCacheEntry->DataSize();
204 return NS_OK;
208 nsresult
209 nsCacheEntryDescriptor::RequestDataSizeChange(PRInt32 deltaSize)
211 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_REQUESTDATASIZECHANGE));
212 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
214 nsresult rv;
215 rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize);
216 if (NS_SUCCEEDED(rv)) {
217 // XXX review for signed/unsigned math errors
218 PRUint32 newDataSize = mCacheEntry->DataSize() + deltaSize;
219 mCacheEntry->SetDataSize(newDataSize);
220 mCacheEntry->TouchData();
222 return rv;
226 NS_IMETHODIMP
227 nsCacheEntryDescriptor::SetDataSize(PRUint32 dataSize)
229 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETDATASIZE));
230 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
232 // XXX review for signed/unsigned math errors
233 PRInt32 deltaSize = dataSize - mCacheEntry->DataSize();
235 nsresult rv;
236 rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize);
237 // this had better be NS_OK, this call instance is advisory for memory cache objects
238 if (NS_SUCCEEDED(rv)) {
239 // XXX review for signed/unsigned math errors
240 PRUint32 newDataSize = mCacheEntry->DataSize() + deltaSize;
241 mCacheEntry->SetDataSize(newDataSize);
242 mCacheEntry->TouchData();
243 } else {
244 NS_WARNING("failed SetDataSize() on memory cache object!");
247 return rv;
251 NS_IMETHODIMP
252 nsCacheEntryDescriptor::OpenInputStream(PRUint32 offset, nsIInputStream ** result)
254 NS_ENSURE_ARG_POINTER(result);
257 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENINPUTSTREAM));
258 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
259 if (!mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM;
261 // ensure valid permissions
262 if (!(mAccessGranted & nsICache::ACCESS_READ))
263 return NS_ERROR_CACHE_READ_ACCESS_DENIED;
266 nsInputStreamWrapper* cacheInput = nullptr;
267 const char *val;
268 val = mCacheEntry->GetMetaDataElement("uncompressed-len");
269 if (val) {
270 cacheInput = new nsDecompressInputStreamWrapper(this, offset);
271 } else {
272 cacheInput = new nsInputStreamWrapper(this, offset);
274 if (!cacheInput) return NS_ERROR_OUT_OF_MEMORY;
276 NS_ADDREF(*result = cacheInput);
277 return NS_OK;
280 NS_IMETHODIMP
281 nsCacheEntryDescriptor::OpenOutputStream(PRUint32 offset, nsIOutputStream ** result)
283 NS_ENSURE_ARG_POINTER(result);
286 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_OPENOUTPUTSTREAM));
287 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
288 if (!mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_NOT_STREAM;
290 // ensure valid permissions
291 if (!(mAccessGranted & nsICache::ACCESS_WRITE))
292 return NS_ERROR_CACHE_WRITE_ACCESS_DENIED;
295 nsOutputStreamWrapper* cacheOutput = nullptr;
296 PRInt32 compressionLevel = nsCacheService::CacheCompressionLevel();
297 const char *val;
298 val = mCacheEntry->GetMetaDataElement("uncompressed-len");
299 if ((compressionLevel > 0) && val) {
300 cacheOutput = new nsCompressOutputStreamWrapper(this, offset);
301 } else {
302 // clear compression flag when compression disabled - see bug #715198
303 if (val) {
304 mCacheEntry->SetMetaDataElement("uncompressed-len", nullptr);
306 cacheOutput = new nsOutputStreamWrapper(this, offset);
308 if (!cacheOutput) return NS_ERROR_OUT_OF_MEMORY;
310 NS_ADDREF(*result = cacheOutput);
311 return NS_OK;
315 NS_IMETHODIMP
316 nsCacheEntryDescriptor::GetCacheElement(nsISupports ** result)
318 NS_ENSURE_ARG_POINTER(result);
319 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETCACHEELEMENT));
320 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
321 if (mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM;
323 NS_IF_ADDREF(*result = mCacheEntry->Data());
324 return NS_OK;
328 NS_IMETHODIMP
329 nsCacheEntryDescriptor::SetCacheElement(nsISupports * cacheElement)
331 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETCACHEELEMENT));
332 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
333 if (mCacheEntry->IsStreamData()) return NS_ERROR_CACHE_DATA_IS_STREAM;
335 return nsCacheService::SetCacheElement(mCacheEntry, cacheElement);
339 NS_IMETHODIMP
340 nsCacheEntryDescriptor::GetAccessGranted(nsCacheAccessMode *result)
342 NS_ENSURE_ARG_POINTER(result);
343 *result = mAccessGranted;
344 return NS_OK;
348 NS_IMETHODIMP
349 nsCacheEntryDescriptor::GetStoragePolicy(nsCacheStoragePolicy *result)
351 NS_ENSURE_ARG_POINTER(result);
352 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSTORAGEPOLICY));
353 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
355 *result = mCacheEntry->StoragePolicy();
356 return NS_OK;
360 NS_IMETHODIMP
361 nsCacheEntryDescriptor::SetStoragePolicy(nsCacheStoragePolicy policy)
363 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSTORAGEPOLICY));
364 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
365 // XXX validate policy against session?
367 bool storageEnabled = false;
368 storageEnabled = nsCacheService::IsStorageEnabledForPolicy_Locked(policy);
369 if (!storageEnabled) return NS_ERROR_FAILURE;
371 // Don't change the storage policy of entries we can't write
372 if (!(mAccessGranted & nsICache::ACCESS_WRITE))
373 return NS_ERROR_NOT_AVAILABLE;
375 // Don't allow a cache entry to move from memory-only to anything else
376 if (mCacheEntry->StoragePolicy() == nsICache::STORE_IN_MEMORY &&
377 policy != nsICache::STORE_IN_MEMORY)
378 return NS_ERROR_NOT_AVAILABLE;
380 mCacheEntry->SetStoragePolicy(policy);
381 mCacheEntry->MarkEntryDirty();
382 return NS_OK;
386 NS_IMETHODIMP
387 nsCacheEntryDescriptor::GetFile(nsIFile ** result)
389 NS_ENSURE_ARG_POINTER(result);
390 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETFILE));
391 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
393 return nsCacheService::GetFileForEntry(mCacheEntry, result);
397 NS_IMETHODIMP
398 nsCacheEntryDescriptor::GetSecurityInfo(nsISupports ** result)
400 NS_ENSURE_ARG_POINTER(result);
401 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETSECURITYINFO));
402 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
404 *result = mCacheEntry->SecurityInfo();
405 NS_IF_ADDREF(*result);
406 return NS_OK;
410 NS_IMETHODIMP
411 nsCacheEntryDescriptor::SetSecurityInfo(nsISupports * securityInfo)
413 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETSECURITYINFO));
414 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
416 mCacheEntry->SetSecurityInfo(securityInfo);
417 mCacheEntry->MarkEntryDirty();
418 return NS_OK;
422 NS_IMETHODIMP
423 nsCacheEntryDescriptor::Doom()
425 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOM));
426 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
428 return nsCacheService::DoomEntry(mCacheEntry);
432 NS_IMETHODIMP
433 nsCacheEntryDescriptor::DoomAndFailPendingRequests(nsresult status)
435 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_DOOMANDFAILPENDINGREQUESTS));
436 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
438 return NS_ERROR_NOT_IMPLEMENTED;
442 NS_IMETHODIMP
443 nsCacheEntryDescriptor::MarkValid()
445 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_MARKVALID));
446 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
448 nsresult rv = nsCacheService::ValidateEntry(mCacheEntry);
449 return rv;
453 NS_IMETHODIMP
454 nsCacheEntryDescriptor::Close()
456 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_CLOSE));
457 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
459 // XXX perhaps closing descriptors should clear/sever transports
461 // tell nsCacheService we're going away
462 nsCacheService::CloseDescriptor(this);
463 NS_ASSERTION(mCacheEntry == nullptr, "mCacheEntry not null");
465 return NS_OK;
469 NS_IMETHODIMP
470 nsCacheEntryDescriptor::GetMetaDataElement(const char *key, char **result)
472 NS_ENSURE_ARG_POINTER(key);
473 *result = nullptr;
475 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_GETMETADATAELEMENT));
476 NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
478 const char *value;
480 value = mCacheEntry->GetMetaDataElement(key);
481 if (!value) return NS_ERROR_NOT_AVAILABLE;
483 *result = NS_strdup(value);
484 if (!*result) return NS_ERROR_OUT_OF_MEMORY;
486 return NS_OK;
490 NS_IMETHODIMP
491 nsCacheEntryDescriptor::SetMetaDataElement(const char *key, const char *value)
493 NS_ENSURE_ARG_POINTER(key);
495 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_SETMETADATAELEMENT));
496 NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
498 // XXX allow null value, for clearing key?
500 nsresult rv = mCacheEntry->SetMetaDataElement(key, value);
501 if (NS_SUCCEEDED(rv))
502 mCacheEntry->TouchMetaData();
503 return rv;
507 NS_IMETHODIMP
508 nsCacheEntryDescriptor::VisitMetaData(nsICacheMetaDataVisitor * visitor)
510 nsCacheServiceAutoLock lock(LOCK_TELEM(NSCACHEENTRYDESCRIPTOR_VISITMETADATA));
511 // XXX check callers, we're calling out of module
512 NS_ENSURE_ARG_POINTER(visitor);
513 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
515 return mCacheEntry->VisitMetaDataElements(visitor);
519 /******************************************************************************
520 * nsCacheInputStream - a wrapper for nsIInputStream keeps the cache entry
521 * open while referenced.
522 ******************************************************************************/
524 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsInputStreamWrapper,
525 nsIInputStream)
527 nsresult nsCacheEntryDescriptor::
528 nsInputStreamWrapper::LazyInit()
530 nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_LAZYINIT));
532 nsCacheAccessMode mode;
533 nsresult rv = mDescriptor->GetAccessGranted(&mode);
534 if (NS_FAILED(rv)) return rv;
536 NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED);
538 nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
539 if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
541 rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode,
542 mStartOffset,
543 getter_AddRefs(mInput));
545 CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit "
546 "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
547 mDescriptor, this, mInput.get(), int(rv)));
549 if (NS_FAILED(rv)) return rv;
551 mInitialized = true;
552 return NS_OK;
555 nsresult nsCacheEntryDescriptor::
556 nsInputStreamWrapper::Close()
558 nsresult rv = EnsureInit();
559 if (NS_FAILED(rv)) return rv;
561 return mInput->Close();
564 nsresult nsCacheEntryDescriptor::
565 nsInputStreamWrapper::Available(PRUint64 *avail)
567 nsresult rv = EnsureInit();
568 if (NS_FAILED(rv)) return rv;
570 return mInput->Available(avail);
573 nsresult nsCacheEntryDescriptor::
574 nsInputStreamWrapper::Read(char *buf, PRUint32 count, PRUint32 *countRead)
576 nsresult rv = EnsureInit();
577 if (NS_SUCCEEDED(rv))
578 rv = mInput->Read(buf, count, countRead);
580 CACHE_LOG_DEBUG(("nsInputStreamWrapper::Read "
581 "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
582 mDescriptor, this, mInput.get(), rv));
584 return rv;
587 nsresult nsCacheEntryDescriptor::
588 nsInputStreamWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure,
589 PRUint32 count, PRUint32 *countRead)
591 // cache stream not buffered
592 return NS_ERROR_NOT_IMPLEMENTED;
595 nsresult nsCacheEntryDescriptor::
596 nsInputStreamWrapper::IsNonBlocking(bool *result)
598 // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
599 *result = false;
600 return NS_OK;
604 /******************************************************************************
605 * nsDecompressInputStreamWrapper - an input stream wrapper that decompresses
606 ******************************************************************************/
608 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsDecompressInputStreamWrapper,
609 nsIInputStream)
611 NS_IMETHODIMP nsCacheEntryDescriptor::
612 nsDecompressInputStreamWrapper::Read(char * buf,
613 PRUint32 count,
614 PRUint32 *countRead)
616 int zerr = Z_OK;
617 nsresult rv = NS_OK;
619 if (!mStreamInitialized) {
620 rv = InitZstream();
621 if (NS_FAILED(rv)) {
622 return rv;
626 mZstream.next_out = (Bytef*)buf;
627 mZstream.avail_out = count;
629 if (mReadBufferLen < count) {
630 // Allocate a buffer for reading from the input stream. This will
631 // determine the max number of compressed bytes read from the
632 // input stream at one time. Making the buffer size proportional
633 // to the request size is not necessary, but helps minimize the
634 // number of read requests to the input stream.
635 PRUint32 newBufLen = NS_MAX(count, (PRUint32)kMinDecompressReadBufLen);
636 unsigned char* newBuf;
637 newBuf = (unsigned char*)nsMemory::Realloc(mReadBuffer,
638 newBufLen);
639 if (newBuf) {
640 mReadBuffer = newBuf;
641 mReadBufferLen = newBufLen;
643 if (!mReadBuffer) {
644 mReadBufferLen = 0;
645 return NS_ERROR_OUT_OF_MEMORY;
649 // read and inflate data until the output buffer is full, or
650 // there is no more data to read
651 while (NS_SUCCEEDED(rv) &&
652 zerr == Z_OK &&
653 mZstream.avail_out > 0 &&
654 count > 0) {
655 if (mZstream.avail_in == 0) {
656 rv = nsInputStreamWrapper::Read((char*)mReadBuffer,
657 mReadBufferLen,
658 &mZstream.avail_in);
659 if (NS_FAILED(rv) || !mZstream.avail_in) {
660 break;
662 mZstream.next_in = mReadBuffer;
664 zerr = inflate(&mZstream, Z_NO_FLUSH);
665 if (zerr == Z_STREAM_END) {
666 // The compressed data may have been stored in multiple
667 // chunks/streams. To allow for this case, re-initialize
668 // the inflate stream and continue decompressing from
669 // the next byte.
670 Bytef * saveNextIn = mZstream.next_in;
671 unsigned int saveAvailIn = mZstream.avail_in;
672 Bytef * saveNextOut = mZstream.next_out;
673 unsigned int saveAvailOut = mZstream.avail_out;
674 inflateReset(&mZstream);
675 mZstream.next_in = saveNextIn;
676 mZstream.avail_in = saveAvailIn;
677 mZstream.next_out = saveNextOut;
678 mZstream.avail_out = saveAvailOut;
679 zerr = Z_OK;
680 } else if (zerr != Z_OK) {
681 rv = NS_ERROR_INVALID_CONTENT_ENCODING;
684 if (NS_SUCCEEDED(rv)) {
685 *countRead = count - mZstream.avail_out;
687 return rv;
690 nsresult nsCacheEntryDescriptor::
691 nsDecompressInputStreamWrapper::Close()
693 EndZstream();
694 if (mReadBuffer) {
695 nsMemory::Free(mReadBuffer);
696 mReadBuffer = 0;
697 mReadBufferLen = 0;
699 return nsInputStreamWrapper::Close();
702 nsresult nsCacheEntryDescriptor::
703 nsDecompressInputStreamWrapper::InitZstream()
705 // Initialize zlib inflate stream
706 mZstream.zalloc = Z_NULL;
707 mZstream.zfree = Z_NULL;
708 mZstream.opaque = Z_NULL;
709 mZstream.next_out = Z_NULL;
710 mZstream.avail_out = 0;
711 mZstream.next_in = Z_NULL;
712 mZstream.avail_in = 0;
713 if (inflateInit(&mZstream) != Z_OK) {
714 return NS_ERROR_FAILURE;
716 mStreamInitialized = true;
717 return NS_OK;
720 nsresult nsCacheEntryDescriptor::
721 nsDecompressInputStreamWrapper::EndZstream()
723 if (mStreamInitialized && !mStreamEnded) {
724 inflateEnd(&mZstream);
725 mStreamEnded = true;
727 return NS_OK;
731 /******************************************************************************
732 * nsOutputStreamWrapper - a wrapper for nsIOutputstream to track the amount of
733 * data written to a cache entry.
734 * - also keeps the cache entry open while referenced.
735 ******************************************************************************/
737 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsOutputStreamWrapper,
738 nsIOutputStream)
740 nsresult nsCacheEntryDescriptor::
741 nsOutputStreamWrapper::LazyInit()
743 nsCacheServiceAutoLock lock(LOCK_TELEM(NSOUTPUTSTREAMWRAPPER_LAZYINIT));
745 nsCacheAccessMode mode;
746 nsresult rv = mDescriptor->GetAccessGranted(&mode);
747 if (NS_FAILED(rv)) return rv;
749 NS_ENSURE_TRUE(mode & nsICache::ACCESS_WRITE, NS_ERROR_UNEXPECTED);
751 nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
752 if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
754 NS_ASSERTION(mOutput == nullptr, "mOutput set in LazyInit");
756 nsCOMPtr<nsIOutputStream> stream;
757 rv = nsCacheService::OpenOutputStreamForEntry(cacheEntry, mode, mStartOffset,
758 getter_AddRefs(stream));
759 if (NS_FAILED(rv))
760 return rv;
762 nsCacheDevice* device = cacheEntry->CacheDevice();
763 if (device) {
764 // the entry has been truncated to mStartOffset bytes, inform device
765 PRInt32 size = cacheEntry->DataSize();
766 rv = device->OnDataSizeChange(cacheEntry, mStartOffset - size);
767 if (NS_SUCCEEDED(rv))
768 cacheEntry->SetDataSize(mStartOffset);
769 } else {
770 rv = NS_ERROR_NOT_AVAILABLE;
773 // If anything above failed, clean up internal state and get out of here
774 // (see bug #654926)...
775 if (NS_FAILED(rv)) {
776 mDescriptor->InternalCleanup(stream);
777 return rv;
780 // ... otherwise, set members and mark initialized
781 mDescriptor->mOutput = mOutput = stream;
782 mInitialized = true;
783 return NS_OK;
786 nsresult nsCacheEntryDescriptor::
787 nsOutputStreamWrapper::OnWrite(PRUint32 count)
789 if (count > PR_INT32_MAX) return NS_ERROR_UNEXPECTED;
790 return mDescriptor->RequestDataSizeChange((PRInt32)count);
793 NS_IMETHODIMP nsCacheEntryDescriptor::
794 nsOutputStreamWrapper::Close()
796 nsresult rv = EnsureInit();
797 if (NS_FAILED(rv)) return rv;
799 return mOutput->Close();
802 NS_IMETHODIMP nsCacheEntryDescriptor::
803 nsOutputStreamWrapper::Flush()
805 nsresult rv = EnsureInit();
806 if (NS_FAILED(rv)) return rv;
808 return mOutput->Flush();
811 NS_IMETHODIMP nsCacheEntryDescriptor::
812 nsOutputStreamWrapper::Write(const char * buf,
813 PRUint32 count,
814 PRUint32 * result)
816 nsresult rv = EnsureInit();
817 if (NS_FAILED(rv)) return rv;
819 rv = OnWrite(count);
820 if (NS_FAILED(rv)) return rv;
822 return mOutput->Write(buf, count, result);
825 NS_IMETHODIMP nsCacheEntryDescriptor::
826 nsOutputStreamWrapper::WriteFrom(nsIInputStream * inStr,
827 PRUint32 count,
828 PRUint32 * result)
830 return NS_ERROR_NOT_IMPLEMENTED;
833 NS_IMETHODIMP nsCacheEntryDescriptor::
834 nsOutputStreamWrapper::WriteSegments(nsReadSegmentFun reader,
835 void * closure,
836 PRUint32 count,
837 PRUint32 * result)
839 return NS_ERROR_NOT_IMPLEMENTED;
842 NS_IMETHODIMP nsCacheEntryDescriptor::
843 nsOutputStreamWrapper::IsNonBlocking(bool *result)
845 // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
846 *result = false;
847 return NS_OK;
851 /******************************************************************************
852 * nsCompressOutputStreamWrapper - an output stream wrapper that compresses
853 * data before it is written
854 ******************************************************************************/
856 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsCompressOutputStreamWrapper,
857 nsIOutputStream)
859 NS_IMETHODIMP nsCacheEntryDescriptor::
860 nsCompressOutputStreamWrapper::Write(const char * buf,
861 PRUint32 count,
862 PRUint32 * result)
864 int zerr = Z_OK;
865 nsresult rv = NS_OK;
867 if (!mStreamInitialized) {
868 rv = InitZstream();
869 if (NS_FAILED(rv)) {
870 return rv;
874 if (!mWriteBuffer) {
875 // Once allocated, this buffer is referenced by the zlib stream and
876 // cannot be grown. We use 2x(initial write request) to approximate
877 // a stream buffer size proportional to request buffers.
878 mWriteBufferLen = NS_MAX(count*2, (PRUint32)kMinCompressWriteBufLen);
879 mWriteBuffer = (unsigned char*)nsMemory::Alloc(mWriteBufferLen);
880 if (!mWriteBuffer) {
881 mWriteBufferLen = 0;
882 return NS_ERROR_OUT_OF_MEMORY;
884 mZstream.next_out = mWriteBuffer;
885 mZstream.avail_out = mWriteBufferLen;
888 // Compress (deflate) the requested buffer. Keep going
889 // until the entire buffer has been deflated.
890 mZstream.avail_in = count;
891 mZstream.next_in = (Bytef*)buf;
892 while (mZstream.avail_in > 0) {
893 zerr = deflate(&mZstream, Z_NO_FLUSH);
894 if (zerr == Z_STREAM_ERROR) {
895 deflateEnd(&mZstream);
896 mStreamInitialized = false;
897 return NS_ERROR_FAILURE;
899 // Note: Z_BUF_ERROR is non-fatal and sometimes expected here.
901 // If the compression stream output buffer is filled, write
902 // it out to the underlying stream wrapper.
903 if (mZstream.avail_out == 0) {
904 rv = WriteBuffer();
905 if (NS_FAILED(rv)) {
906 deflateEnd(&mZstream);
907 mStreamInitialized = false;
908 return rv;
912 *result = count;
913 mUncompressedCount += *result;
914 return NS_OK;
917 NS_IMETHODIMP nsCacheEntryDescriptor::
918 nsCompressOutputStreamWrapper::Close()
920 nsresult rv = NS_OK;
921 int zerr = 0;
923 if (mStreamInitialized) {
924 // complete compression of any data remaining in the zlib stream
925 do {
926 zerr = deflate(&mZstream, Z_FINISH);
927 rv = WriteBuffer();
928 } while (zerr == Z_OK && rv == NS_OK);
929 deflateEnd(&mZstream);
932 if (mDescriptor->CacheEntry()) {
933 nsCAutoString uncompressedLenStr;
934 rv = mDescriptor->GetMetaDataElement("uncompressed-len",
935 getter_Copies(uncompressedLenStr));
936 if (NS_SUCCEEDED(rv)) {
937 PRInt32 oldCount = uncompressedLenStr.ToInteger(&rv);
938 if (NS_SUCCEEDED(rv)) {
939 mUncompressedCount += oldCount;
942 uncompressedLenStr.Adopt(0);
943 uncompressedLenStr.AppendInt(mUncompressedCount);
944 rv = mDescriptor->SetMetaDataElement("uncompressed-len",
945 uncompressedLenStr.get());
948 if (mWriteBuffer) {
949 nsMemory::Free(mWriteBuffer);
950 mWriteBuffer = 0;
951 mWriteBufferLen = 0;
954 return nsOutputStreamWrapper::Close();
957 nsresult nsCacheEntryDescriptor::
958 nsCompressOutputStreamWrapper::InitZstream()
960 // Determine compression level: Aggressive compression
961 // may impact performance on mobile devices, while a
962 // lower compression level still provides substantial
963 // space savings for many text streams.
964 PRInt32 compressionLevel = nsCacheService::CacheCompressionLevel();
966 // Initialize zlib deflate stream
967 mZstream.zalloc = Z_NULL;
968 mZstream.zfree = Z_NULL;
969 mZstream.opaque = Z_NULL;
970 if (deflateInit2(&mZstream, compressionLevel, Z_DEFLATED,
971 MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
972 return NS_ERROR_FAILURE;
974 mZstream.next_in = Z_NULL;
975 mZstream.avail_in = 0;
977 mStreamInitialized = true;
979 return NS_OK;
982 nsresult nsCacheEntryDescriptor::
983 nsCompressOutputStreamWrapper::WriteBuffer()
985 PRUint32 bytesToWrite = mWriteBufferLen - mZstream.avail_out;
986 PRUint32 result = 0;
987 nsresult rv = nsCacheEntryDescriptor::nsOutputStreamWrapper::Write(
988 (const char *)mWriteBuffer, bytesToWrite, &result);
989 mZstream.next_out = mWriteBuffer;
990 mZstream.avail_out = mWriteBufferLen;
991 return rv;