Opera 12.15 Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ExponentialGrowthFile.cpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
  2. **
  3. ** Copyright (C) 2011 Opera Software ASA. All rights reserved.
  4. **
  5. ** This file is part of the Opera web browser.
  6. ** It may not be distributed under any circumstances.
  7. */
  8. #include "core/pch.h"
  9. #ifdef SEARCH_ENGINE_EXPONENTIAL_GROWTH_FILE
  10. #include "modules/search_engine/ExponentialGrowthFile.h"
  11. // ExponentialGrowthFile layout before conversion:
  12. //
  13. // m_virtual_file_length
  14. // |
  15. // V
  16. // +--------/ /----------+
  17. // |ddddddddd...ddddddddddd|
  18. // +--------/ /----------+
  19. // 0 ^
  20. // |
  21. // physical file length
  22. //
  23. //
  24. // Normal ExponentialGrowthFile layout:
  25. //
  26. // m_virtual_file_length
  27. // |
  28. // V
  29. // +--------/ /----------+--------/ /---+---+
  30. // |ddddddddd...ddddddddddd| ... |mmm|
  31. // +--------/ /----------+--------/ /---+---+
  32. // 0 ^
  33. // |
  34. // physical file length
  35. //
  36. // ddd = normal file data
  37. // mmm = metadata:
  38. // - 8 bytes METADATA_MAGIC_COOKIE
  39. // - 8 bytes m_virtual_file_length
  40. #define BAD_FILE_POS FILE_LENGTH_NONE
  41. #define OPF_IS_NEGATIVE(a) ((((a>>31)>>31)>>2) != 0)
  42. #define METADATA_SIZE 16
  43. #define METADATA_MAGIC_COOKIE "w,Lp3YpB"
  44. #define ASSERT_AND_RETURN_IF_ERROR(expr) \
  45. do { \
  46. OP_STATUS ASSERT_RETURN_IF_ERROR_TMP = expr; \
  47. OP_ASSERT(OpStatus::IsSuccess(ASSERT_RETURN_IF_ERROR_TMP));\
  48. if (OpStatus::IsError(ASSERT_RETURN_IF_ERROR_TMP)) \
  49. return ASSERT_RETURN_IF_ERROR_TMP; \
  50. } while (0)
  51. ExponentialGrowthFile*
  52. ExponentialGrowthFile::Create(
  53. OpLowLevelFile* wrapped_file,
  54. OP_STATUS& status,
  55. OpFileLength smallest_size,
  56. BOOL transfer_ownership)
  57. {
  58. if (!wrapped_file || OPF_IS_NEGATIVE(smallest_size))
  59. {
  60. status = OpStatus::ERR;
  61. return NULL;
  62. }
  63. ExponentialGrowthFile* wrapper = OP_NEW(ExponentialGrowthFile, (wrapped_file, nextExpSize(smallest_size), transfer_ownership));
  64. status = wrapper ? OpStatus::OK : OpStatus::ERR_NO_MEMORY;
  65. return wrapper;
  66. }
  67. OpLowLevelFile* ExponentialGrowthFile::MakeNormalFile(ExponentialGrowthFile*& file)
  68. {
  69. OpFileLength physical_file_length;
  70. OpLowLevelFile* op_file = file->m_f;
  71. if (OpStatus::IsSuccess(op_file->GetFileLength(&physical_file_length) &&
  72. OpStatus::IsSuccess(file->EnsureValidVirtualFileLength())))
  73. {
  74. OP_ASSERT(physical_file_length == file->m_virtual_file_length);
  75. if (physical_file_length != file->m_virtual_file_length)
  76. OpStatus::Ignore(file->ConvertBackToNormalFile());
  77. }
  78. file->m_f = NULL;
  79. OP_DELETE(file);
  80. file = NULL;
  81. return op_file;
  82. }
  83. ExponentialGrowthFile::ExponentialGrowthFile(OpLowLevelFile* wrapped_file, OpFileLength smallest_size, BOOL transfer_ownership)
  84. : FileWrapper(wrapped_file, transfer_ownership),
  85. m_smallest_size(smallest_size),
  86. m_virtual_file_length(BAD_FILE_POS),
  87. m_metadata_needs_write(FALSE)
  88. {
  89. }
  90. ExponentialGrowthFile::~ExponentialGrowthFile()
  91. {
  92. if (m_f)
  93. OpStatus::Ignore(WriteMetadata());
  94. }
  95. OP_STATUS ExponentialGrowthFile::SafeClose()
  96. {
  97. RETURN_IF_ERROR(WriteMetadata());
  98. return m_f->SafeClose();
  99. }
  100. OP_STATUS ExponentialGrowthFile::Close()
  101. {
  102. RETURN_IF_ERROR(WriteMetadata());
  103. return m_f->Close();
  104. }
  105. OP_STATUS ExponentialGrowthFile::Flush()
  106. {
  107. if ((m_mode & OPFILE_COMMIT) != 0)
  108. RETURN_IF_ERROR(WriteMetadata());
  109. return m_f->Flush();
  110. }
  111. BOOL ExponentialGrowthFile::Eof() const
  112. {
  113. OpFileLength file_pos;
  114. RETURN_VALUE_IF_ERROR(EnsureValidVirtualFileLength(), TRUE);
  115. RETURN_VALUE_IF_ERROR(m_f->GetFilePos(&file_pos), TRUE);
  116. OP_ASSERT(file_pos <= m_virtual_file_length);
  117. return file_pos >= m_virtual_file_length;
  118. }
  119. OP_STATUS ExponentialGrowthFile::SetFilePos(OpFileLength pos, OpSeekMode mode /* = SEEK_FROM_START */)
  120. {
  121. OP_ASSERT(pos != BAD_FILE_POS || mode == SEEK_FROM_CURRENT); // Seek-from-current with offset -1 is allowed
  122. RETURN_IF_ERROR(EnsureValidVirtualFileLength());
  123. switch (mode)
  124. {
  125. case SEEK_FROM_START:
  126. break;
  127. case SEEK_FROM_END:
  128. if (pos <= m_virtual_file_length)
  129. pos = m_virtual_file_length - pos;
  130. else
  131. pos = BAD_FILE_POS;
  132. break;
  133. case SEEK_FROM_CURRENT:
  134. OpFileLength file_pos;
  135. ASSERT_AND_RETURN_IF_ERROR(m_f->GetFilePos(&file_pos));
  136. pos += file_pos;
  137. break;
  138. }
  139. OP_ASSERT(pos <= m_virtual_file_length && !OPF_IS_NEGATIVE(pos));
  140. if (pos == BAD_FILE_POS || pos > m_virtual_file_length || OPF_IS_NEGATIVE(pos))
  141. return OpStatus::ERR;
  142. return m_f->SetFilePos(pos);
  143. }
  144. OP_STATUS ExponentialGrowthFile::GetFileLength(OpFileLength* len) const
  145. {
  146. if (!len)
  147. return OpStatus::ERR;
  148. RETURN_IF_ERROR(EnsureValidVirtualFileLength());
  149. *len = m_virtual_file_length;
  150. return OpStatus::OK;
  151. }
  152. OP_STATUS ExponentialGrowthFile::Write(const void* data, OpFileLength len)
  153. {
  154. RETURN_IF_ERROR(EnsureValidVirtualFileLength());
  155. OP_STATUS status = m_f->Write(data, len);
  156. OpFileLength file_pos;
  157. ASSERT_AND_RETURN_IF_ERROR(m_f->GetFilePos(&file_pos));
  158. if (file_pos > m_virtual_file_length)
  159. RETURN_IF_ERROR(SetFileLength(file_pos, TRUE));
  160. return status;
  161. }
  162. OP_STATUS ExponentialGrowthFile::Read(void* data, OpFileLength len, OpFileLength* bytes_read)
  163. {
  164. OpFileLength file_pos;
  165. RETURN_IF_ERROR(EnsureValidVirtualFileLength());
  166. ASSERT_AND_RETURN_IF_ERROR(m_f->GetFilePos(&file_pos));
  167. OP_ASSERT(file_pos <= m_virtual_file_length);
  168. if (file_pos > m_virtual_file_length)
  169. return OpStatus::ERR;
  170. if (file_pos+len > m_virtual_file_length)
  171. len = m_virtual_file_length - file_pos;
  172. return m_f->Read(data, len, bytes_read);
  173. }
  174. /**
  175. * Round up to the nearest half-step of 2^n
  176. * The sequence will be 1,2,3,4,6,8,12,16,24,32,48,64,96,128...
  177. * @param size Current size
  178. * @return new size
  179. */
  180. OpFileLength ExponentialGrowthFile::nextExpSize(OpFileLength size)
  181. {
  182. if (size <= 4)
  183. return size;
  184. size --;
  185. int shift;
  186. for (shift = 0; size>>shift != 0; shift++)
  187. ;
  188. if (((size >> (shift-2)) & 1) == 0)
  189. size = (OpFileLength)3 << (shift-2);
  190. else
  191. size = (OpFileLength)1 << shift;
  192. return size;
  193. }
  194. OpFileLength ExponentialGrowthFile::GetNewPhysicalFileLength(OpFileLength new_file_length) const
  195. {
  196. OpFileLength required_length = new_file_length + METADATA_SIZE;
  197. if (required_length <= m_smallest_size)
  198. return m_smallest_size;
  199. OpFileLength biggest_increment = EXPONENTIAL_GROWTH_FILE_BIGGEST_INCREMENT;
  200. if (required_length >= 2*biggest_increment)
  201. return ((required_length + biggest_increment-1) / biggest_increment) * biggest_increment;
  202. return nextExpSize(required_length);
  203. }
  204. OP_STATUS ExponentialGrowthFile::SetFileLength(OpFileLength new_file_length, BOOL return_to_current_file_pos)
  205. {
  206. OpFileLength old_physical_file_length, new_physical_file_length, current_file_pos = 0;
  207. OP_ASSERT(!OPF_IS_NEGATIVE(new_file_length) || new_file_length == BAD_FILE_POS);
  208. if (OPF_IS_NEGATIVE(new_file_length) || new_file_length == BAD_FILE_POS)
  209. return OpStatus::ERR;
  210. if (new_file_length == m_virtual_file_length)
  211. return OpStatus::OK;
  212. m_virtual_file_length = new_file_length;
  213. m_metadata_needs_write = TRUE;
  214. ASSERT_AND_RETURN_IF_ERROR(m_f->GetFileLength(&old_physical_file_length));
  215. new_physical_file_length = GetNewPhysicalFileLength(new_file_length);
  216. if (new_physical_file_length == old_physical_file_length)
  217. return OpStatus::OK;
  218. if (return_to_current_file_pos)
  219. {
  220. ASSERT_AND_RETURN_IF_ERROR(m_f->GetFilePos(&current_file_pos));
  221. OP_ASSERT(current_file_pos <= new_file_length); // return_to_current_file_pos shouldn't be true if we are shortening the file
  222. if (current_file_pos > new_file_length)
  223. current_file_pos = new_file_length;
  224. }
  225. if (OpStatus::IsError(m_f->SetFileLength(new_physical_file_length)))
  226. {
  227. // Unable to expand file (probably disk full). Attempt to set "unconverted" length
  228. OP_STATUS status = ConvertBackToNormalFile();
  229. OP_STATUS status2 = OpStatus::OK;
  230. if (return_to_current_file_pos)
  231. status2 = m_f->SetFilePos(current_file_pos);
  232. ASSERT_AND_RETURN_IF_ERROR(status);
  233. ASSERT_AND_RETURN_IF_ERROR(status2);
  234. return OpStatus::OK;
  235. }
  236. if (new_physical_file_length > old_physical_file_length)
  237. RETURN_IF_ERROR(WriteMetadata()); // Write something to ensure that the file is physically resized
  238. if (return_to_current_file_pos)
  239. ASSERT_AND_RETURN_IF_ERROR(m_f->SetFilePos(current_file_pos));
  240. return OpStatus::OK;
  241. }
  242. OP_STATUS ExponentialGrowthFile::EnsureValidVirtualFileLength() const
  243. {
  244. unsigned char metadata[METADATA_SIZE]; /* ARRAY OK 2010-11-24 roarl */
  245. OpFileLength physical_file_length, current_file_pos, metadata_pos, bytes_read;
  246. if (m_virtual_file_length != BAD_FILE_POS)
  247. return OpStatus::OK;
  248. ASSERT_AND_RETURN_IF_ERROR(m_f->GetFilePos(&current_file_pos));
  249. ASSERT_AND_RETURN_IF_ERROR(m_f->GetFileLength(&physical_file_length));
  250. if (physical_file_length < METADATA_SIZE)
  251. goto fallback;
  252. metadata_pos = physical_file_length - METADATA_SIZE;
  253. ASSERT_AND_RETURN_IF_ERROR(m_f->SetFilePos(metadata_pos));
  254. ASSERT_AND_RETURN_IF_ERROR(m_f->Read(metadata, METADATA_SIZE, &bytes_read));
  255. OP_ASSERT(bytes_read == METADATA_SIZE);
  256. if (bytes_read != METADATA_SIZE)
  257. return OpStatus::ERR_OUT_OF_RANGE; // File too short
  258. m_virtual_file_length = 0;
  259. for (int i=8; i<16; i++)
  260. m_virtual_file_length = (m_virtual_file_length<<8) + metadata[i];
  261. // If the metadata is not okay, the file is not yet converted
  262. if (op_strncmp((char*)metadata, METADATA_MAGIC_COOKIE, 8) != 0)
  263. goto fallback;
  264. OP_ASSERT(m_virtual_file_length <= metadata_pos && !OPF_IS_NEGATIVE(m_virtual_file_length));
  265. if (m_virtual_file_length > metadata_pos || OPF_IS_NEGATIVE(m_virtual_file_length))
  266. goto fallback;
  267. ASSERT_AND_RETURN_IF_ERROR(m_f->SetFilePos(current_file_pos));
  268. return OpStatus::OK;
  269. fallback:
  270. // Assume the file is not yet converted
  271. m_virtual_file_length = physical_file_length;
  272. ASSERT_AND_RETURN_IF_ERROR(m_f->SetFilePos(current_file_pos));
  273. return OpStatus::OK;
  274. }
  275. OP_STATUS ExponentialGrowthFile::WriteMetadata()
  276. {
  277. unsigned char metadata[METADATA_SIZE]; /* ARRAY OK 2010-11-24 roarl */
  278. OpFileLength physical_file_length, metadata_pos, tmp;
  279. if (!m_metadata_needs_write)
  280. return OpStatus::OK;
  281. ASSERT_AND_RETURN_IF_ERROR(m_f->GetFileLength(&physical_file_length));
  282. OP_ASSERT(m_virtual_file_length != BAD_FILE_POS);
  283. OP_ASSERT(physical_file_length >= m_virtual_file_length);
  284. m_metadata_needs_write = FALSE; // From this point, it will be written or doesn't need to be written
  285. if (physical_file_length < m_virtual_file_length + METADATA_SIZE)
  286. {
  287. // No room left after m_virtual_file_length to write the metadata. Attempt to set "unconverted" length
  288. ASSERT_AND_RETURN_IF_ERROR(ConvertBackToNormalFile());
  289. return OpStatus::OK;
  290. }
  291. metadata_pos = physical_file_length - METADATA_SIZE;
  292. op_memcpy(metadata, METADATA_MAGIC_COOKIE, 8);
  293. tmp = m_virtual_file_length;
  294. for (int i=0; i<8; i++) {
  295. metadata[15-i] = (unsigned char)(tmp & 0xff);
  296. tmp >>= 8;
  297. }
  298. if (OpStatus::IsError(m_f->SetFilePos(metadata_pos)) ||
  299. OpStatus::IsError(m_f->Write(metadata, METADATA_SIZE)))
  300. {
  301. // Unable to expand file (probably disk full). Attempt to set "unconverted" length
  302. ASSERT_AND_RETURN_IF_ERROR(ConvertBackToNormalFile());
  303. return OpStatus::OK;
  304. }
  305. return OpStatus::OK;
  306. }
  307. OP_STATUS ExponentialGrowthFile::ConvertBackToNormalFile()
  308. {
  309. OP_STATUS status = m_f->SetFileLength(m_virtual_file_length);
  310. if (OpStatus::IsError(status))
  311. {
  312. // Now we're screwed, there is no way out. Let's hope the error status
  313. // is propagated and appropriate action is taken.
  314. m_virtual_file_length = BAD_FILE_POS;
  315. }
  316. m_metadata_needs_write = FALSE;
  317. return status;
  318. }
  319. #undef BAD_FILE_POS
  320. #undef OPF_IS_NEGATIVE
  321. #undef META_DATA_SIZE
  322. #undef METADATA_MAGIC_COOKIE
  323. #undef ASSERT_AND_RETURN_IF_ERROR
  324. #endif // SEARCH_ENGINE_EXPONENTIAL_GROWTH_FILE