1
2/*
3 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkBitmapCache.h"
11
12struct SkBitmapCache::Entry {
13    Entry*      fPrev;
14    Entry*      fNext;
15
16    void*       fBuffer;
17    size_t      fSize;
18    SkBitmap    fBitmap;
19
20    Entry(const void* buffer, size_t size, const SkBitmap& bm)
21            : fPrev(NULL),
22              fNext(NULL),
23              fBitmap(bm) {
24        fBuffer = sk_malloc_throw(size);
25        fSize = size;
26        memcpy(fBuffer, buffer, size);
27    }
28
29    ~Entry() { sk_free(fBuffer); }
30
31    bool equals(const void* buffer, size_t size) const {
32        return (fSize == size) && !memcmp(fBuffer, buffer, size);
33    }
34};
35
36SkBitmapCache::SkBitmapCache(int max) : fMaxEntries(max) {
37    fEntryCount = 0;
38    fHead = fTail = NULL;
39
40    this->validate();
41}
42
43SkBitmapCache::~SkBitmapCache() {
44    this->validate();
45
46    Entry* entry = fHead;
47    while (entry) {
48        Entry* next = entry->fNext;
49        delete entry;
50        entry = next;
51    }
52}
53
54SkBitmapCache::Entry* SkBitmapCache::detach(Entry* entry) const {
55    if (entry->fPrev) {
56        SkASSERT(fHead != entry);
57        entry->fPrev->fNext = entry->fNext;
58    } else {
59        SkASSERT(fHead == entry);
60        fHead = entry->fNext;
61    }
62    if (entry->fNext) {
63        SkASSERT(fTail != entry);
64        entry->fNext->fPrev = entry->fPrev;
65    } else {
66        SkASSERT(fTail == entry);
67        fTail = entry->fPrev;
68    }
69    return entry;
70}
71
72void SkBitmapCache::attachToHead(Entry* entry) const {
73    entry->fPrev = NULL;
74    entry->fNext = fHead;
75    if (fHead) {
76        fHead->fPrev = entry;
77    } else {
78        fTail = entry;
79    }
80    fHead = entry;
81}
82
83bool SkBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const {
84    AutoValidate av(this);
85
86    Entry* entry = fHead;
87    while (entry) {
88        if (entry->equals(buffer, size)) {
89            if (bm) {
90                *bm = entry->fBitmap;
91            }
92            // move to the head of our list, so we purge it last
93            this->detach(entry);
94            this->attachToHead(entry);
95            return true;
96        }
97        entry = entry->fNext;
98    }
99    return false;
100}
101
102void SkBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) {
103    AutoValidate av(this);
104
105    if (fEntryCount == fMaxEntries) {
106        SkASSERT(fTail);
107        delete this->detach(fTail);
108        fEntryCount -= 1;
109    }
110
111    Entry* entry = SkNEW_ARGS(Entry, (buffer, len, bm));
112    this->attachToHead(entry);
113    fEntryCount += 1;
114}
115
116///////////////////////////////////////////////////////////////////////////////
117
118#ifdef SK_DEBUG
119
120void SkBitmapCache::validate() const {
121    SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries);
122
123    if (fEntryCount > 0) {
124        SkASSERT(NULL == fHead->fPrev);
125        SkASSERT(NULL == fTail->fNext);
126
127        if (fEntryCount == 1) {
128            SkASSERT(fHead == fTail);
129        } else {
130            SkASSERT(fHead != fTail);
131        }
132
133        Entry* entry = fHead;
134        int count = 0;
135        while (entry) {
136            count += 1;
137            entry = entry->fNext;
138        }
139        SkASSERT(count == fEntryCount);
140
141        entry = fTail;
142        while (entry) {
143            count -= 1;
144            entry = entry->fPrev;
145        }
146        SkASSERT(0 == count);
147    } else {
148        SkASSERT(NULL == fHead);
149        SkASSERT(NULL == fTail);
150    }
151}
152
153#endif
154