1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
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/
10 : *
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.
15 : *
16 : * The Original Code is nsMemoryCacheDevice.cpp, released
17 : * February 22, 2001.
18 : *
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.
23 : *
24 : * Contributor(s):
25 : * Gordon Sheridan, <gordon@netscape.com>
26 : * Patrick C. Beard <beard@netscape.com>
27 : * Brian Ryner <bryner@brianryner.com>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either the GNU General Public License Version 2 or later (the "GPL"), or
31 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "nsMemoryCacheDevice.h"
44 : #include "nsCacheService.h"
45 : #include "nsICacheService.h"
46 : #include "nsIStorageStream.h"
47 : #include "nsICacheVisitor.h"
48 : #include "nsCRT.h"
49 : #include "nsCache.h"
50 : #include "nsReadableUtils.h"
51 : #include "mozilla/Telemetry.h"
52 :
53 : // The memory cache implements the "LRU-SP" caching algorithm
54 : // described in "LRU-SP: A Size-Adjusted and Popularity-Aware LRU Replacement
55 : // Algorithm for Web Caching" by Kai Cheng and Yahiko Kambayashi.
56 :
57 : // We keep kQueueCount LRU queues, which should be about ceil(log2(mHardLimit))
58 : // The queues hold exponentially increasing ranges of floor(log2((size/nref)))
59 : // values for entries.
60 : // Entries larger than 2^(kQueueCount-1) go in the last queue.
61 : // Entries with no expiration go in the first queue.
62 :
63 : const char *gMemoryDeviceID = "memory";
64 :
65 90 : nsMemoryCacheDevice::nsMemoryCacheDevice()
66 : : mInitialized(false),
67 : mEvictionThreshold(PR_INT32_MAX),
68 : mHardLimit(4 * 1024 * 1024), // default, if no pref
69 : mSoftLimit((mHardLimit * 9) / 10), // default, if no pref
70 : mTotalSize(0),
71 : mInactiveSize(0),
72 : mEntryCount(0),
73 : mMaxEntryCount(0),
74 90 : mMaxEntrySize(-1) // -1 means "no limit"
75 : {
76 2250 : for (int i=0; i<kQueueCount; ++i)
77 2160 : PR_INIT_CLIST(&mEvictionList[i]);
78 90 : }
79 :
80 :
81 270 : nsMemoryCacheDevice::~nsMemoryCacheDevice()
82 : {
83 90 : Shutdown();
84 360 : }
85 :
86 :
87 : nsresult
88 90 : nsMemoryCacheDevice::Init()
89 : {
90 90 : if (mInitialized) return NS_ERROR_ALREADY_INITIALIZED;
91 :
92 90 : nsresult rv = mMemCacheEntries.Init();
93 90 : mInitialized = NS_SUCCEEDED(rv);
94 90 : return rv;
95 : }
96 :
97 :
98 : nsresult
99 90 : nsMemoryCacheDevice::Shutdown()
100 : {
101 90 : NS_ASSERTION(mInitialized, "### attempting shutdown while not initialized");
102 90 : NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
103 :
104 90 : mMemCacheEntries.Shutdown();
105 :
106 : // evict all entries
107 : nsCacheEntry * entry, * next;
108 :
109 2250 : for (int i = kQueueCount - 1; i >= 0; --i) {
110 2160 : entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
111 4516 : while (entry != &mEvictionList[i]) {
112 196 : NS_ASSERTION(!entry->IsInUse(), "### shutting down with active entries");
113 196 : next = (nsCacheEntry *)PR_NEXT_LINK(entry);
114 196 : PR_REMOVE_AND_INIT_LINK(entry);
115 :
116 : // update statistics
117 196 : PRInt32 memoryRecovered = (PRInt32)entry->Size();
118 196 : mTotalSize -= memoryRecovered;
119 196 : mInactiveSize -= memoryRecovered;
120 196 : --mEntryCount;
121 :
122 196 : delete entry;
123 196 : entry = next;
124 : }
125 : }
126 :
127 : /*
128 : * we're not factoring in changes to meta data yet...
129 : * NS_ASSERTION(mTotalSize == 0, "### mem cache leaking entries?");
130 : */
131 90 : NS_ASSERTION(mInactiveSize == 0, "### mem cache leaking entries?");
132 90 : NS_ASSERTION(mEntryCount == 0, "### mem cache leaking entries?");
133 :
134 90 : mInitialized = false;
135 :
136 90 : return NS_OK;
137 : }
138 :
139 :
140 : const char *
141 410 : nsMemoryCacheDevice::GetDeviceID()
142 : {
143 410 : return gMemoryDeviceID;
144 : }
145 :
146 :
147 : nsCacheEntry *
148 409 : nsMemoryCacheDevice::FindEntry(nsCString * key, bool *collision)
149 : {
150 818 : mozilla::Telemetry::AutoTimer<mozilla::Telemetry::CACHE_MEMORY_SEARCH> timer;
151 409 : nsCacheEntry * entry = mMemCacheEntries.GetEntry(key);
152 409 : if (!entry) return nsnull;
153 :
154 : // move entry to the tail of an eviction list
155 207 : PR_REMOVE_AND_INIT_LINK(entry);
156 207 : PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]);
157 :
158 207 : mInactiveSize -= entry->Size();
159 :
160 207 : return entry;
161 : }
162 :
163 :
164 : nsresult
165 443 : nsMemoryCacheDevice::DeactivateEntry(nsCacheEntry * entry)
166 : {
167 443 : CACHE_LOG_DEBUG(("nsMemoryCacheDevice::DeactivateEntry for entry 0x%p\n",
168 : entry));
169 443 : if (entry->IsDoomed()) {
170 : #ifdef DEBUG
171 : // XXX verify we've removed it from mMemCacheEntries & eviction list
172 : #endif
173 34 : delete entry;
174 34 : CACHE_LOG_DEBUG(("deleted doomed entry 0x%p\n", entry));
175 34 : return NS_OK;
176 : }
177 :
178 : #ifdef DEBUG
179 409 : nsCacheEntry * ourEntry = mMemCacheEntries.GetEntry(entry->Key());
180 409 : NS_ASSERTION(ourEntry, "DeactivateEntry called for an entry we don't have!");
181 409 : NS_ASSERTION(entry == ourEntry, "entry doesn't match ourEntry");
182 409 : if (ourEntry != entry)
183 0 : return NS_ERROR_INVALID_POINTER;
184 : #endif
185 :
186 409 : mInactiveSize += entry->Size();
187 409 : EvictEntriesIfNecessary();
188 :
189 409 : return NS_OK;
190 : }
191 :
192 :
193 : nsresult
194 236 : nsMemoryCacheDevice::BindEntry(nsCacheEntry * entry)
195 : {
196 236 : if (!entry->IsDoomed()) {
197 236 : NS_ASSERTION(PR_CLIST_IS_EMPTY(entry),"entry is already on a list!");
198 :
199 : // append entry to the eviction list
200 236 : PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]);
201 :
202 : // add entry to hashtable of mem cache entries
203 236 : nsresult rv = mMemCacheEntries.AddEntry(entry);
204 236 : if (NS_FAILED(rv)) {
205 0 : PR_REMOVE_AND_INIT_LINK(entry);
206 0 : return rv;
207 : }
208 :
209 : // add size of entry to memory totals
210 236 : ++mEntryCount;
211 236 : if (mMaxEntryCount < mEntryCount) mMaxEntryCount = mEntryCount;
212 :
213 236 : mTotalSize += entry->Size();
214 236 : EvictEntriesIfNecessary();
215 : }
216 :
217 236 : return NS_OK;
218 : }
219 :
220 :
221 : void
222 34 : nsMemoryCacheDevice::DoomEntry(nsCacheEntry * entry)
223 : {
224 : #ifdef DEBUG
225 : // debug code to verify we have entry
226 34 : nsCacheEntry * hashEntry = mMemCacheEntries.GetEntry(entry->Key());
227 34 : if (!hashEntry) NS_WARNING("no entry for key");
228 34 : else if (entry != hashEntry) NS_WARNING("entry != hashEntry");
229 : #endif
230 34 : CACHE_LOG_DEBUG(("Dooming entry 0x%p in memory cache\n", entry));
231 34 : EvictEntry(entry, DO_NOT_DELETE_ENTRY);
232 34 : }
233 :
234 :
235 : nsresult
236 64 : nsMemoryCacheDevice::OpenInputStreamForEntry( nsCacheEntry * entry,
237 : nsCacheAccessMode mode,
238 : PRUint32 offset,
239 : nsIInputStream ** result)
240 : {
241 64 : NS_ENSURE_ARG_POINTER(entry);
242 64 : NS_ENSURE_ARG_POINTER(result);
243 :
244 128 : nsCOMPtr<nsIStorageStream> storage;
245 : nsresult rv;
246 :
247 64 : nsISupports *data = entry->Data();
248 64 : if (data) {
249 64 : storage = do_QueryInterface(data, &rv);
250 64 : if (NS_FAILED(rv))
251 0 : return rv;
252 : }
253 : else {
254 0 : rv = NS_NewStorageStream(4096, PRUint32(-1), getter_AddRefs(storage));
255 0 : if (NS_FAILED(rv))
256 0 : return rv;
257 0 : entry->SetData(storage);
258 : }
259 :
260 64 : return storage->NewInputStream(offset, result);
261 : }
262 :
263 :
264 : nsresult
265 552 : nsMemoryCacheDevice::OpenOutputStreamForEntry( nsCacheEntry * entry,
266 : nsCacheAccessMode mode,
267 : PRUint32 offset,
268 : nsIOutputStream ** result)
269 : {
270 552 : NS_ENSURE_ARG_POINTER(entry);
271 552 : NS_ENSURE_ARG_POINTER(result);
272 :
273 1104 : nsCOMPtr<nsIStorageStream> storage;
274 : nsresult rv;
275 :
276 552 : nsISupports *data = entry->Data();
277 552 : if (data) {
278 337 : storage = do_QueryInterface(data, &rv);
279 337 : if (NS_FAILED(rv))
280 0 : return rv;
281 : }
282 : else {
283 215 : rv = NS_NewStorageStream(4096, PRUint32(-1), getter_AddRefs(storage));
284 215 : if (NS_FAILED(rv))
285 0 : return rv;
286 215 : entry->SetData(storage);
287 : }
288 :
289 552 : return storage->GetOutputStream(offset, result);
290 : }
291 :
292 :
293 : nsresult
294 0 : nsMemoryCacheDevice::GetFileForEntry( nsCacheEntry * entry,
295 : nsIFile ** result )
296 : {
297 0 : return NS_ERROR_NOT_IMPLEMENTED;
298 : }
299 :
300 : bool
301 1461 : nsMemoryCacheDevice::EntryIsTooBig(PRInt64 entrySize)
302 : {
303 1461 : CACHE_LOG_DEBUG(("nsMemoryCacheDevice::EntryIsTooBig "
304 : "[size=%d max=%d soft=%d]\n",
305 : entrySize, mMaxEntrySize, mSoftLimit));
306 1461 : if (mMaxEntrySize == -1)
307 3 : return entrySize > mSoftLimit;
308 : else
309 1458 : return (entrySize > mSoftLimit || entrySize > mMaxEntrySize);
310 : }
311 :
312 : size_t
313 0 : nsMemoryCacheDevice::TotalSize()
314 : {
315 0 : return mTotalSize;
316 : }
317 :
318 : nsresult
319 1265 : nsMemoryCacheDevice::OnDataSizeChange( nsCacheEntry * entry, PRInt32 deltaSize)
320 : {
321 1265 : if (entry->IsStreamData()) {
322 : // we have the right to refuse or pre-evict
323 1265 : PRUint32 newSize = entry->DataSize() + deltaSize;
324 1265 : if (EntryIsTooBig(newSize)) {
325 : #ifdef DEBUG
326 : nsresult rv =
327 : #endif
328 0 : nsCacheService::DoomEntry(entry);
329 0 : NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
330 0 : return NS_ERROR_ABORT;
331 : }
332 : }
333 :
334 : // adjust our totals
335 1265 : mTotalSize += deltaSize;
336 :
337 1265 : if (!entry->IsDoomed()) {
338 : // move entry to the tail of the appropriate eviction list
339 1265 : PR_REMOVE_AND_INIT_LINK(entry);
340 1265 : PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, deltaSize)]);
341 : }
342 :
343 1265 : EvictEntriesIfNecessary();
344 1265 : return NS_OK;
345 : }
346 :
347 :
348 : void
349 91 : nsMemoryCacheDevice::AdjustMemoryLimits(PRInt32 softLimit, PRInt32 hardLimit)
350 : {
351 91 : mSoftLimit = softLimit;
352 91 : mHardLimit = hardLimit;
353 :
354 : // First, evict entries that won't fit into the new cache size.
355 91 : EvictEntriesIfNecessary();
356 91 : }
357 :
358 :
359 : void
360 40 : nsMemoryCacheDevice::EvictEntry(nsCacheEntry * entry, bool deleteEntry)
361 : {
362 40 : CACHE_LOG_DEBUG(("Evicting entry 0x%p from memory cache, deleting: %d\n",
363 : entry, deleteEntry));
364 : // remove entry from our hashtable
365 40 : mMemCacheEntries.RemoveEntry(entry);
366 :
367 : // remove entry from the eviction list
368 40 : PR_REMOVE_AND_INIT_LINK(entry);
369 :
370 : // update statistics
371 40 : PRInt32 memoryRecovered = (PRInt32)entry->Size();
372 40 : mTotalSize -= memoryRecovered;
373 40 : if (!entry->IsDoomed())
374 6 : mInactiveSize -= memoryRecovered;
375 40 : --mEntryCount;
376 :
377 40 : if (deleteEntry) delete entry;
378 40 : }
379 :
380 :
381 : void
382 2001 : nsMemoryCacheDevice::EvictEntriesIfNecessary(void)
383 : {
384 : nsCacheEntry * entry;
385 : nsCacheEntry * maxEntry;
386 2001 : CACHE_LOG_DEBUG(("EvictEntriesIfNecessary. mTotalSize: %d, mHardLimit: %d,"
387 : "mInactiveSize: %d, mSoftLimit: %d\n",
388 : mTotalSize, mHardLimit, mInactiveSize, mSoftLimit));
389 :
390 2001 : if ((mTotalSize < mHardLimit) && (mInactiveSize < mSoftLimit))
391 2000 : return;
392 :
393 1 : PRUint32 now = SecondsFromPRTime(PR_Now());
394 1 : PRUint64 entryCost = 0;
395 1 : PRUint64 maxCost = 0;
396 0 : do {
397 : // LRU-SP eviction selection: Check the head of each segment (each
398 : // eviction list, kept in LRU order) and select the maximal-cost
399 : // entry for eviction. Cost is time-since-accessed * size / nref.
400 1 : maxEntry = 0;
401 25 : for (int i = kQueueCount - 1; i >= 0; --i) {
402 24 : entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
403 :
404 : // If the head of a list is in use, check the next available entry
405 48 : while ((entry != &mEvictionList[i]) &&
406 0 : (entry->IsInUse())) {
407 0 : entry = (nsCacheEntry *)PR_NEXT_LINK(entry);
408 : }
409 :
410 24 : if (entry != &mEvictionList[i]) {
411 : entryCost = (PRUint64)
412 0 : (now - entry->LastFetched()) * entry->Size() /
413 0 : PR_MAX(1, entry->FetchCount());
414 0 : if (!maxEntry || (entryCost > maxCost)) {
415 0 : maxEntry = entry;
416 0 : maxCost = entryCost;
417 : }
418 : }
419 : }
420 1 : if (maxEntry) {
421 0 : EvictEntry(maxEntry, DELETE_ENTRY);
422 : } else {
423 1 : break;
424 : }
425 : }
426 : while ((mTotalSize >= mHardLimit) || (mInactiveSize >= mSoftLimit));
427 : }
428 :
429 :
430 : int
431 6832 : nsMemoryCacheDevice::EvictionList(nsCacheEntry * entry, PRInt32 deltaSize)
432 : {
433 : // favor items which never expire by putting them in the lowest-index queue
434 6832 : if (entry->ExpirationTime() == nsICache::NO_EXPIRATION_TIME)
435 448 : return 0;
436 :
437 : // compute which eviction queue this entry should go into,
438 : // based on floor(log2(size/nref))
439 6384 : PRInt32 size = deltaSize + (PRInt32)entry->Size();
440 6384 : PRInt32 fetchCount = NS_MAX(1, entry->FetchCount());
441 :
442 6384 : return NS_MIN(PR_FloorLog2(size / fetchCount), kQueueCount - 1);
443 : }
444 :
445 :
446 : nsresult
447 4 : nsMemoryCacheDevice::Visit(nsICacheVisitor * visitor)
448 : {
449 4 : nsMemoryCacheDeviceInfo * deviceInfo = new nsMemoryCacheDeviceInfo(this);
450 8 : nsCOMPtr<nsICacheDeviceInfo> deviceRef(deviceInfo);
451 4 : if (!deviceInfo) return NS_ERROR_OUT_OF_MEMORY;
452 :
453 : bool keepGoing;
454 4 : nsresult rv = visitor->VisitDevice(gMemoryDeviceID, deviceInfo, &keepGoing);
455 4 : if (NS_FAILED(rv)) return rv;
456 :
457 4 : if (!keepGoing)
458 4 : return NS_OK;
459 :
460 : nsCacheEntry * entry;
461 0 : nsCOMPtr<nsICacheEntryInfo> entryRef;
462 :
463 0 : for (int i = kQueueCount - 1; i >= 0; --i) {
464 0 : entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
465 0 : while (entry != &mEvictionList[i]) {
466 0 : nsCacheEntryInfo * entryInfo = new nsCacheEntryInfo(entry);
467 0 : if (!entryInfo) return NS_ERROR_OUT_OF_MEMORY;
468 0 : entryRef = entryInfo;
469 :
470 0 : rv = visitor->VisitEntry(gMemoryDeviceID, entryInfo, &keepGoing);
471 0 : entryInfo->DetachEntry();
472 0 : if (NS_FAILED(rv)) return rv;
473 0 : if (!keepGoing) break;
474 :
475 0 : entry = (nsCacheEntry *)PR_NEXT_LINK(entry);
476 : }
477 : }
478 0 : return NS_OK;
479 : }
480 :
481 :
482 : nsresult
483 14 : nsMemoryCacheDevice::EvictEntries(const char * clientID)
484 : {
485 : nsCacheEntry * entry;
486 14 : PRUint32 prefixLength = (clientID ? strlen(clientID) : 0);
487 :
488 350 : for (int i = kQueueCount - 1; i >= 0; --i) {
489 336 : PRCList * elem = PR_LIST_HEAD(&mEvictionList[i]);
490 678 : while (elem != &mEvictionList[i]) {
491 6 : entry = (nsCacheEntry *)elem;
492 6 : elem = PR_NEXT_LINK(elem);
493 :
494 6 : const char * key = entry->Key()->get();
495 6 : if (clientID && nsCRT::strncmp(clientID, key, prefixLength) != 0)
496 0 : continue;
497 :
498 6 : if (entry->IsInUse()) {
499 0 : nsresult rv = nsCacheService::DoomEntry(entry);
500 0 : if (NS_FAILED(rv)) {
501 0 : CACHE_LOG_WARNING(("memCache->EvictEntries() aborted: rv =%x", rv));
502 0 : return rv;
503 : }
504 : } else {
505 6 : EvictEntry(entry, DELETE_ENTRY);
506 : }
507 : }
508 : }
509 :
510 14 : return NS_OK;
511 : }
512 :
513 :
514 : // WARNING: SetCapacity can get called before Init()
515 : void
516 91 : nsMemoryCacheDevice::SetCapacity(PRInt32 capacity)
517 : {
518 91 : PRInt32 hardLimit = capacity * 1024; // convert k into bytes
519 91 : PRInt32 softLimit = (hardLimit * 9) / 10;
520 91 : AdjustMemoryLimits(softLimit, hardLimit);
521 91 : }
522 :
523 : void
524 92 : nsMemoryCacheDevice::SetMaxEntrySize(PRInt32 maxSizeInKilobytes)
525 : {
526 : // Internal unit is bytes. Changing this only takes effect *after* the
527 : // change and has no consequences for existing cache-entries
528 92 : if (maxSizeInKilobytes >= 0)
529 91 : mMaxEntrySize = maxSizeInKilobytes * 1024;
530 : else
531 1 : mMaxEntrySize = -1;
532 92 : }
533 :
534 : #ifdef DEBUG
535 : static PLDHashOperator
536 0 : CountEntry(PLDHashTable * table, PLDHashEntryHdr * hdr, PRUint32 number, void * arg)
537 : {
538 0 : PRInt32 *entryCount = (PRInt32 *)arg;
539 0 : ++(*entryCount);
540 0 : return PL_DHASH_NEXT;
541 : }
542 :
543 : void
544 0 : nsMemoryCacheDevice::CheckEntryCount()
545 : {
546 0 : if (!mInitialized) return;
547 :
548 0 : PRInt32 evictionListCount = 0;
549 0 : for (int i=0; i<kQueueCount; ++i) {
550 0 : PRCList * elem = PR_LIST_HEAD(&mEvictionList[i]);
551 0 : while (elem != &mEvictionList[i]) {
552 0 : elem = PR_NEXT_LINK(elem);
553 0 : ++evictionListCount;
554 : }
555 : }
556 0 : NS_ASSERTION(mEntryCount == evictionListCount, "### mem cache badness");
557 :
558 0 : PRInt32 entryCount = 0;
559 0 : mMemCacheEntries.VisitEntries(CountEntry, &entryCount);
560 0 : NS_ASSERTION(mEntryCount == entryCount, "### mem cache badness");
561 : }
562 : #endif
563 :
564 : /******************************************************************************
565 : * nsMemoryCacheDeviceInfo - for implementing about:cache
566 : *****************************************************************************/
567 :
568 :
569 68 : NS_IMPL_ISUPPORTS1(nsMemoryCacheDeviceInfo, nsICacheDeviceInfo)
570 :
571 :
572 : NS_IMETHODIMP
573 0 : nsMemoryCacheDeviceInfo::GetDescription(char ** result)
574 : {
575 0 : NS_ENSURE_ARG_POINTER(result);
576 0 : *result = NS_strdup("Memory cache device");
577 0 : if (!*result) return NS_ERROR_OUT_OF_MEMORY;
578 0 : return NS_OK;
579 : }
580 :
581 :
582 : NS_IMETHODIMP
583 0 : nsMemoryCacheDeviceInfo::GetUsageReport(char ** result)
584 : {
585 0 : NS_ENSURE_ARG_POINTER(result);
586 0 : nsCString buffer;
587 :
588 : buffer.AssignLiteral(" <tr>\n"
589 : " <th>Inactive storage:</th>\n"
590 0 : " <td>");
591 0 : buffer.AppendInt(mDevice->mInactiveSize / 1024);
592 : buffer.AppendLiteral(" KiB</td>\n"
593 0 : " </tr>\n");
594 :
595 0 : *result = ToNewCString(buffer);
596 0 : if (!*result) return NS_ERROR_OUT_OF_MEMORY;
597 0 : return NS_OK;
598 : }
599 :
600 :
601 : NS_IMETHODIMP
602 1 : nsMemoryCacheDeviceInfo::GetEntryCount(PRUint32 * result)
603 : {
604 1 : NS_ENSURE_ARG_POINTER(result);
605 : // XXX compare calculated count vs. mEntryCount
606 1 : *result = (PRUint32)mDevice->mEntryCount;
607 1 : return NS_OK;
608 : }
609 :
610 :
611 : NS_IMETHODIMP
612 0 : nsMemoryCacheDeviceInfo::GetTotalSize(PRUint32 * result)
613 : {
614 0 : NS_ENSURE_ARG_POINTER(result);
615 0 : *result = (PRUint32)mDevice->mTotalSize;
616 0 : return NS_OK;
617 : }
618 :
619 :
620 : NS_IMETHODIMP
621 0 : nsMemoryCacheDeviceInfo::GetMaximumSize(PRUint32 * result)
622 : {
623 0 : NS_ENSURE_ARG_POINTER(result);
624 0 : *result = (PRUint32)mDevice->mHardLimit;
625 0 : return NS_OK;
626 : }
|