Coverage Report

Created: 2018-09-25 14:53

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