1 // ==========================================================
2 // Multi-Page functions
3 //
4 // Design and implementation by
5 // - Floris van den Berg (flvdberg@wxs.nl)
6 // - checkered (checkered@users.sourceforge.net)
7 // - Mihail Naydenov (mnaydenov@users.sourceforge.net)
8 //
9 // This file is part of FreeImage 3
10 //
11 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
12 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
13 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
14 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
15 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
16 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
17 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
18 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
19 // THIS DISCLAIMER.
20 //
21 // Use at your own risk!
22 // ==========================================================
23
24 #ifdef _MSC_VER
25 #pragma warning (disable : 4786) // identifier was truncated to 'number' characters
26 #endif
27
28 #include "CacheFile.h"
29
30 // ----------------------------------------------------------
31
CacheFile()32 CacheFile::CacheFile() :
33 m_file(NULL),
34 m_free_pages(),
35 m_page_cache_mem(),
36 m_page_cache_disk(),
37 m_page_map(),
38 m_page_count(0),
39 m_current_block(NULL),
40 m_keep_in_memory(TRUE) {
41 }
42
~CacheFile()43 CacheFile::~CacheFile() {
44 close();
45 }
46
47 BOOL
open(const std::string & filename,BOOL keep_in_memory)48 CacheFile::open(const std::string& filename, BOOL keep_in_memory) {
49
50 assert(!m_file);
51
52 m_filename = filename;
53 m_keep_in_memory = keep_in_memory;
54
55 if ((!m_filename.empty()) && (!m_keep_in_memory)) {
56 m_file = fopen(m_filename.c_str(), "w+b");
57 return (m_file != NULL);
58 }
59
60 return (m_keep_in_memory == TRUE);
61 }
62
63 void
close()64 CacheFile::close() {
65 // dispose the cache entries
66
67 while (!m_page_cache_disk.empty()) {
68 Block *block = *m_page_cache_disk.begin();
69 m_page_cache_disk.pop_front();
70 delete [] block->data;
71 delete block;
72 }
73 while (!m_page_cache_mem.empty()) {
74 Block *block = *m_page_cache_mem.begin();
75 m_page_cache_mem.pop_front();
76 delete [] block->data;
77 delete block;
78 }
79
80 if (m_file) {
81 // close the file
82 fclose(m_file);
83 m_file = NULL;
84
85 // delete the file
86 remove(m_filename.c_str());
87 }
88 }
89
90 void
cleanupMemCache()91 CacheFile::cleanupMemCache() {
92 if (!m_keep_in_memory) {
93 if (m_page_cache_mem.size() > CACHE_SIZE) {
94 // flush the least used block to file
95
96 Block *old_block = m_page_cache_mem.back();
97 fseek(m_file, old_block->nr * BLOCK_SIZE, SEEK_SET);
98 fwrite(old_block->data, BLOCK_SIZE, 1, m_file);
99
100 // remove the data
101
102 delete [] old_block->data;
103 old_block->data = NULL;
104
105 // move the block to another list
106
107 m_page_cache_disk.splice(m_page_cache_disk.begin(), m_page_cache_mem, --m_page_cache_mem.end());
108 m_page_map[old_block->nr] = m_page_cache_disk.begin();
109 }
110 }
111 }
112
113 int
allocateBlock()114 CacheFile::allocateBlock() {
115 Block *block = new Block;
116 block->data = new BYTE[BLOCK_SIZE];
117 block->next = 0;
118
119 if (!m_free_pages.empty()) {
120 block->nr = *m_free_pages.begin();
121 m_free_pages.pop_front();
122 } else {
123 block->nr = m_page_count++;
124 }
125
126 m_page_cache_mem.push_front(block);
127 m_page_map[block->nr] = m_page_cache_mem.begin();
128
129 cleanupMemCache();
130
131 return block->nr;
132 }
133
134 Block *
lockBlock(int nr)135 CacheFile::lockBlock(int nr) {
136 if (m_current_block == NULL) {
137 PageMapIt it = m_page_map.find(nr);
138
139 if (it != m_page_map.end()) {
140 m_current_block = *(it->second);
141
142 // the block is swapped out to disc. load it back
143 // and remove the block from the cache. it might get cached
144 // again as soon as the memory buffer fills up
145
146 if (m_current_block->data == NULL) {
147 m_current_block->data = new BYTE[BLOCK_SIZE];
148
149 fseek(m_file, m_current_block->nr * BLOCK_SIZE, SEEK_SET);
150 fread(m_current_block->data, BLOCK_SIZE, 1, m_file);
151
152 m_page_cache_mem.splice(m_page_cache_mem.begin(), m_page_cache_disk, it->second);
153 m_page_map[nr] = m_page_cache_mem.begin();
154 }
155
156 // if the memory cache size is too large, swap an item to disc
157
158 cleanupMemCache();
159
160 // return the current block
161
162 return m_current_block;
163 }
164 }
165
166 return NULL;
167 }
168
169 BOOL
unlockBlock(int nr)170 CacheFile::unlockBlock(int nr) {
171 if (m_current_block) {
172 m_current_block = NULL;
173 return TRUE;
174 }
175 return FALSE;
176 }
177
178 BOOL
deleteBlock(int nr)179 CacheFile::deleteBlock(int nr) {
180 if (!m_current_block) {
181 PageMapIt it = m_page_map.find(nr);
182
183 // remove block from cache
184
185 if (it != m_page_map.end()) {
186 m_page_map.erase(nr);
187 }
188
189 // add block to free page list
190
191 m_free_pages.push_back(nr);
192
193 return TRUE;
194 }
195
196 return FALSE;
197 }
198
199 BOOL
readFile(BYTE * data,int nr,int size)200 CacheFile::readFile(BYTE *data, int nr, int size) {
201 if ((data) && (size > 0)) {
202 int s = 0;
203 int block_nr = nr;
204
205 do {
206 int copy_nr = block_nr;
207
208 Block *block = lockBlock(copy_nr);
209
210 block_nr = block->next;
211
212 memcpy(data + s, block->data, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE);
213
214 unlockBlock(copy_nr);
215
216 s += BLOCK_SIZE;
217 } while (block_nr != 0);
218
219 return TRUE;
220 }
221
222 return FALSE;
223 }
224
225 int
writeFile(BYTE * data,int size)226 CacheFile::writeFile(BYTE *data, int size) {
227 if ((data) && (size > 0)) {
228 int nr_blocks_required = 1 + (size / BLOCK_SIZE);
229 int count = 0;
230 int s = 0;
231 int stored_alloc;
232 int alloc;
233
234 stored_alloc = alloc = allocateBlock();
235
236 do {
237 int copy_alloc = alloc;
238
239 Block *block = lockBlock(copy_alloc);
240
241 block->next = 0;
242
243 memcpy(block->data, data + s, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE);
244
245 if (count + 1 < nr_blocks_required)
246 alloc = block->next = allocateBlock();
247
248 unlockBlock(copy_alloc);
249
250 s += BLOCK_SIZE;
251 } while (++count < nr_blocks_required);
252
253 return stored_alloc;
254 }
255
256 return 0;
257 }
258
259 void
deleteFile(int nr)260 CacheFile::deleteFile(int nr) {
261 do {
262 Block *block = lockBlock(nr);
263
264 if (block == NULL)
265 break;
266
267 int next = block->next;
268
269 unlockBlock(nr);
270
271 deleteBlock(nr);
272
273 nr = next;
274 } while (nr != 0);
275 }
276
277