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