| // ========================================================== |
| // Multi-Page functions |
| // |
| // Design and implementation by |
| // - Floris van den Berg (flvdberg@wxs.nl) |
| // - checkered (checkered@users.sourceforge.net) |
| // |
| // This file is part of FreeImage 3 |
| // |
| // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY |
| // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES |
| // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE |
| // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED |
| // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT |
| // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY |
| // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL |
| // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER |
| // THIS DISCLAIMER. |
| // |
| // Use at your own risk! |
| // ========================================================== |
| |
| #ifdef _MSC_VER |
| #pragma warning (disable : 4786) // identifier was truncated to 'number' characters |
| #endif |
| |
| #include "CacheFile.h" |
| |
| // ---------------------------------------------------------- |
| |
| CacheFile::CacheFile(const std::string filename, BOOL keep_in_memory) : |
| m_file(NULL), |
| m_filename(filename), |
| m_free_pages(), |
| m_page_cache_mem(), |
| m_page_cache_disk(), |
| m_page_map(), |
| m_page_count(0), |
| m_current_block(NULL), |
| m_keep_in_memory(keep_in_memory) { |
| } |
| |
| CacheFile::~CacheFile() { |
| } |
| |
| BOOL |
| CacheFile::open() { |
| if ((!m_filename.empty()) && (!m_keep_in_memory)) { |
| m_file = fopen(m_filename.c_str(), "w+b"); |
| return (m_file != NULL); |
| } |
| |
| return (m_keep_in_memory == TRUE); |
| } |
| |
| void |
| CacheFile::close() { |
| // dispose the cache entries |
| |
| while (!m_page_cache_disk.empty()) { |
| Block *block = *m_page_cache_disk.begin(); |
| m_page_cache_disk.pop_front(); |
| delete [] block->data; |
| delete block; |
| } |
| while (!m_page_cache_mem.empty()) { |
| Block *block = *m_page_cache_mem.begin(); |
| m_page_cache_mem.pop_front(); |
| delete [] block->data; |
| delete block; |
| } |
| |
| if (m_file) { |
| // close the file |
| |
| fclose(m_file); |
| |
| // delete the file |
| |
| remove(m_filename.c_str()); |
| } |
| } |
| |
| void |
| CacheFile::cleanupMemCache() { |
| if (!m_keep_in_memory) { |
| if (m_page_cache_mem.size() > CACHE_SIZE) { |
| // flush the least used block to file |
| |
| Block *old_block = m_page_cache_mem.back(); |
| fseek(m_file, old_block->nr * BLOCK_SIZE, SEEK_SET); |
| fwrite(old_block->data, BLOCK_SIZE, 1, m_file); |
| |
| // remove the data |
| |
| delete [] old_block->data; |
| old_block->data = NULL; |
| |
| // move the block to another list |
| |
| m_page_cache_disk.splice(m_page_cache_disk.begin(), m_page_cache_mem, --m_page_cache_mem.end()); |
| m_page_map[old_block->nr] = m_page_cache_disk.begin(); |
| } |
| } |
| } |
| |
| int |
| CacheFile::allocateBlock() { |
| Block *block = new Block; |
| block->data = new BYTE[BLOCK_SIZE]; |
| block->next = 0; |
| |
| if (!m_free_pages.empty()) { |
| block->nr = *m_free_pages.begin(); |
| m_free_pages.pop_front(); |
| } else { |
| block->nr = m_page_count++; |
| } |
| |
| m_page_cache_mem.push_front(block); |
| m_page_map[block->nr] = m_page_cache_mem.begin(); |
| |
| cleanupMemCache(); |
| |
| return block->nr; |
| } |
| |
| Block * |
| CacheFile::lockBlock(int nr) { |
| if (m_current_block == NULL) { |
| PageMapIt it = m_page_map.find(nr); |
| |
| if (it != m_page_map.end()) { |
| m_current_block = *(it->second); |
| |
| // the block is swapped out to disc. load it back |
| // and remove the block from the cache. it might get cached |
| // again as soon as the memory buffer fills up |
| |
| if (m_current_block->data == NULL) { |
| m_current_block->data = new BYTE[BLOCK_SIZE]; |
| |
| fseek(m_file, m_current_block->nr * BLOCK_SIZE, SEEK_SET); |
| fread(m_current_block->data, BLOCK_SIZE, 1, m_file); |
| |
| m_page_cache_mem.splice(m_page_cache_mem.begin(), m_page_cache_disk, it->second); |
| m_page_map[nr] = m_page_cache_mem.begin(); |
| } |
| |
| // if the memory cache size is too large, swap an item to disc |
| |
| cleanupMemCache(); |
| |
| // return the current block |
| |
| return m_current_block; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| BOOL |
| CacheFile::unlockBlock(int nr) { |
| if (m_current_block) { |
| m_current_block = NULL; |
| |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| BOOL |
| CacheFile::deleteBlock(int nr) { |
| if (!m_current_block) { |
| PageMapIt it = m_page_map.find(nr); |
| |
| // remove block from cache |
| |
| if (it != m_page_map.end()) |
| m_page_map.erase(nr); |
| |
| // add block to free page list |
| |
| m_free_pages.push_back(nr); |
| |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| BOOL |
| CacheFile::readFile(BYTE *data, int nr, int size) { |
| if ((data) && (size > 0)) { |
| int s = 0; |
| int block_nr = nr; |
| |
| do { |
| int copy_nr = block_nr; |
| |
| Block *block = lockBlock(copy_nr); |
| |
| block_nr = block->next; |
| |
| memcpy(data + s, block->data, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE); |
| |
| unlockBlock(copy_nr); |
| |
| s += BLOCK_SIZE; |
| } while (block_nr != 0); |
| |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| int |
| CacheFile::writeFile(BYTE *data, int size) { |
| if ((data) && (size > 0)) { |
| int nr_blocks_required = 1 + (size / BLOCK_SIZE); |
| int count = 0; |
| int s = 0; |
| int stored_alloc; |
| int alloc; |
| |
| stored_alloc = alloc = allocateBlock(); |
| |
| do { |
| int copy_alloc = alloc; |
| |
| Block *block = lockBlock(copy_alloc); |
| |
| block->next = 0; |
| |
| memcpy(block->data, data + s, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE); |
| |
| if (count + 1 < nr_blocks_required) |
| alloc = block->next = allocateBlock(); |
| |
| unlockBlock(copy_alloc); |
| |
| s += BLOCK_SIZE; |
| } while (++count < nr_blocks_required); |
| |
| return stored_alloc; |
| } |
| |
| return 0; |
| } |
| |
| void |
| CacheFile::deleteFile(int nr) { |
| do { |
| Block *block = lockBlock(nr); |
| |
| if (block == NULL) |
| break; |
| |
| int next = block->next; |
| |
| unlockBlock(nr); |
| |
| deleteBlock(nr); |
| |
| nr = next; |
| } while (nr != 0); |
| } |
| |