/src/FreeImage/Source/FreeImage/CacheFile.cpp

https://bitbucket.org/cabalistic/ogredeps/ · 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. #ifdef _MSC_VER
  23. #pragma warning (disable : 4786) // identifier was truncated to 'number' characters
  24. #endif
  25. #include "CacheFile.h"
  26. // ----------------------------------------------------------
  27. CacheFile::CacheFile(const std::string filename, BOOL keep_in_memory) :
  28. m_file(NULL),
  29. m_filename(filename),
  30. m_free_pages(),
  31. m_page_cache_mem(),
  32. m_page_cache_disk(),
  33. m_page_map(),
  34. m_page_count(0),
  35. m_current_block(NULL),
  36. m_keep_in_memory(keep_in_memory) {
  37. }
  38. CacheFile::~CacheFile() {
  39. }
  40. BOOL
  41. CacheFile::open() {
  42. if ((!m_filename.empty()) && (!m_keep_in_memory)) {
  43. m_file = fopen(m_filename.c_str(), "w+b");
  44. return (m_file != NULL);
  45. }
  46. return (m_keep_in_memory == TRUE);
  47. }
  48. void
  49. CacheFile::close() {
  50. // dispose the cache entries
  51. while (!m_page_cache_disk.empty()) {
  52. Block *block = *m_page_cache_disk.begin();
  53. m_page_cache_disk.pop_front();
  54. delete [] block->data;
  55. delete block;
  56. }
  57. while (!m_page_cache_mem.empty()) {
  58. Block *block = *m_page_cache_mem.begin();
  59. m_page_cache_mem.pop_front();
  60. delete [] block->data;
  61. delete block;
  62. }
  63. if (m_file) {
  64. // close the file
  65. fclose(m_file);
  66. // delete the file
  67. remove(m_filename.c_str());
  68. }
  69. }
  70. void
  71. CacheFile::cleanupMemCache() {
  72. if (!m_keep_in_memory) {
  73. if (m_page_cache_mem.size() > CACHE_SIZE) {
  74. // flush the least used block to file
  75. Block *old_block = m_page_cache_mem.back();
  76. fseek(m_file, old_block->nr * BLOCK_SIZE, SEEK_SET);
  77. fwrite(old_block->data, BLOCK_SIZE, 1, m_file);
  78. // remove the data
  79. delete [] old_block->data;
  80. old_block->data = NULL;
  81. // move the block to another list
  82. m_page_cache_disk.splice(m_page_cache_disk.begin(), m_page_cache_mem, --m_page_cache_mem.end());
  83. m_page_map[old_block->nr] = m_page_cache_disk.begin();
  84. }
  85. }
  86. }
  87. int
  88. CacheFile::allocateBlock() {
  89. Block *block = new Block;
  90. block->data = new BYTE[BLOCK_SIZE];
  91. block->next = 0;
  92. if (!m_free_pages.empty()) {
  93. block->nr = *m_free_pages.begin();
  94. m_free_pages.pop_front();
  95. } else {
  96. block->nr = m_page_count++;
  97. }
  98. m_page_cache_mem.push_front(block);
  99. m_page_map[block->nr] = m_page_cache_mem.begin();
  100. cleanupMemCache();
  101. return block->nr;
  102. }
  103. Block *
  104. CacheFile::lockBlock(int nr) {
  105. if (m_current_block == NULL) {
  106. PageMapIt it = m_page_map.find(nr);
  107. if (it != m_page_map.end()) {
  108. m_current_block = *(it->second);
  109. // the block is swapped out to disc. load it back
  110. // and remove the block from the cache. it might get cached
  111. // again as soon as the memory buffer fills up
  112. if (m_current_block->data == NULL) {
  113. m_current_block->data = new BYTE[BLOCK_SIZE];
  114. fseek(m_file, m_current_block->nr * BLOCK_SIZE, SEEK_SET);
  115. fread(m_current_block->data, BLOCK_SIZE, 1, m_file);
  116. m_page_cache_mem.splice(m_page_cache_mem.begin(), m_page_cache_disk, it->second);
  117. m_page_map[nr] = m_page_cache_mem.begin();
  118. }
  119. // if the memory cache size is too large, swap an item to disc
  120. cleanupMemCache();
  121. // return the current block
  122. return m_current_block;
  123. }
  124. }
  125. return NULL;
  126. }
  127. BOOL
  128. CacheFile::unlockBlock(int nr) {
  129. if (m_current_block) {
  130. m_current_block = NULL;
  131. return TRUE;
  132. }
  133. return FALSE;
  134. }
  135. BOOL
  136. CacheFile::deleteBlock(int nr) {
  137. if (!m_current_block) {
  138. PageMapIt it = m_page_map.find(nr);
  139. // remove block from cache
  140. if (it != m_page_map.end())
  141. m_page_map.erase(nr);
  142. // add block to free page list
  143. m_free_pages.push_back(nr);
  144. return TRUE;
  145. }
  146. return FALSE;
  147. }
  148. BOOL
  149. CacheFile::readFile(BYTE *data, int nr, int size) {
  150. if ((data) && (size > 0)) {
  151. int s = 0;
  152. int block_nr = nr;
  153. do {
  154. int copy_nr = block_nr;
  155. Block *block = lockBlock(copy_nr);
  156. block_nr = block->next;
  157. memcpy(data + s, block->data, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE);
  158. unlockBlock(copy_nr);
  159. s += BLOCK_SIZE;
  160. } while (block_nr != 0);
  161. return TRUE;
  162. }
  163. return FALSE;
  164. }
  165. int
  166. CacheFile::writeFile(BYTE *data, int size) {
  167. if ((data) && (size > 0)) {
  168. int nr_blocks_required = 1 + (size / BLOCK_SIZE);
  169. int count = 0;
  170. int s = 0;
  171. int stored_alloc;
  172. int alloc;
  173. stored_alloc = alloc = allocateBlock();
  174. do {
  175. int copy_alloc = alloc;
  176. Block *block = lockBlock(copy_alloc);
  177. block->next = 0;
  178. memcpy(block->data, data + s, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE);
  179. if (count + 1 < nr_blocks_required)
  180. alloc = block->next = allocateBlock();
  181. unlockBlock(copy_alloc);
  182. s += BLOCK_SIZE;
  183. } while (++count < nr_blocks_required);
  184. return stored_alloc;
  185. }
  186. return 0;
  187. }
  188. void
  189. CacheFile::deleteFile(int nr) {
  190. do {
  191. Block *block = lockBlock(nr);
  192. if (block == NULL)
  193. break;
  194. int next = block->next;
  195. unlockBlock(nr);
  196. deleteBlock(nr);
  197. nr = next;
  198. } while (nr != 0);
  199. }