// ========================================================== // Multi-Page functions // // Design and implementation by // - Floris van den Berg (flvdberg@wxs.nl) // - checkered (checkered@users.sourceforge.net) // - Mihail Naydenov (mnaydenov@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 "../stdafx.h" // ---------------------------------------------------------- CacheFile::CacheFile() : m_file(NULL), 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(TRUE) { } CacheFile::~CacheFile() { close(); } BOOL CacheFile::open(const std::string& filename, BOOL keep_in_memory) { assert(!m_file); m_filename = filename; m_keep_in_memory = keep_in_memory; 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); m_file = NULL; // 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 uint8_t[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 uint8_t[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(uint8_t *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(uint8_t *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); }