PageRenderTime 32ms CodeModel.GetById 47ms RepoModel.GetById 1ms app.codeStats 0ms

/src/framework/io/File.cpp

https://github.com/jiawen/temporal-lightfield-reconstruction
C++ | 593 lines | 394 code | 123 blank | 76 comment | 96 complexity | d5f9fba5dd4395e2df83207659773050 MD5 | raw file
  1. /*
  2. * Copyright (c) 2009-2011, NVIDIA Corporation
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of NVIDIA Corporation nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include "io/File.hpp"
  28. using namespace FW;
  29. //------------------------------------------------------------------------
  30. File::AsyncOp::~AsyncOp(void)
  31. {
  32. wait();
  33. CloseHandle(m_overlapped.hEvent);
  34. }
  35. //------------------------------------------------------------------------
  36. bool File::AsyncOp::isDone(void)
  37. {
  38. if (m_done)
  39. return true;
  40. if (!HasOverlappedIoCompleted(&m_overlapped))
  41. return false;
  42. wait();
  43. return true;
  44. }
  45. //------------------------------------------------------------------------
  46. void File::AsyncOp::wait(void)
  47. {
  48. if (m_done)
  49. return;
  50. DWORD numBytes = 0;
  51. if (!GetOverlappedResult(m_fileHandle, &m_overlapped, &numBytes, TRUE))
  52. {
  53. setError("GetOverlappedResult() failed!");
  54. failed();
  55. }
  56. else if ((int)numBytes != m_expectedBytes)
  57. {
  58. setError("GetOverlappedResult() returned %d bytes, expected %d!", numBytes, m_expectedBytes);
  59. failed();
  60. }
  61. else
  62. {
  63. done();
  64. }
  65. }
  66. //------------------------------------------------------------------------
  67. File::AsyncOp::AsyncOp(HANDLE fileHandle)
  68. : m_offset (0),
  69. m_numBytes (0),
  70. m_expectedBytes (0),
  71. m_userBytes (0),
  72. m_readPtr (NULL),
  73. m_writePtr (NULL),
  74. m_copyBytes (0),
  75. m_copySrc (NULL),
  76. m_copyDst (NULL),
  77. m_freePtr (NULL),
  78. m_fileHandle (fileHandle),
  79. m_done (false),
  80. m_failed (false)
  81. {
  82. memset(&m_overlapped, 0, sizeof(m_overlapped));
  83. // Create event object. Without one, GetOverlappedResult()
  84. // occassionally fails to wait long enough on WinXP.
  85. m_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  86. if (!m_overlapped.hEvent)
  87. failWin32Error("CreateEvent");
  88. }
  89. //------------------------------------------------------------------------
  90. void File::AsyncOp::done(void)
  91. {
  92. if (m_done)
  93. return;
  94. m_done = true;
  95. if (m_copyBytes && !m_failed)
  96. memcpy(m_copyDst, m_copySrc, m_copyBytes);
  97. if (m_freePtr)
  98. free(m_freePtr);
  99. }
  100. //------------------------------------------------------------------------
  101. File::File(const String& name, Mode mode, bool disableCache)
  102. : m_name (name),
  103. m_mode (mode),
  104. m_disableCache (disableCache),
  105. m_handle (NULL),
  106. m_align (1),
  107. m_size (0),
  108. m_offset (0)
  109. {
  110. static bool privilegeSet = false;
  111. if (!privilegeSet)
  112. {
  113. privilegeSet = true;
  114. HANDLE token;
  115. if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
  116. {
  117. TOKEN_PRIVILEGES tp;
  118. tp.PrivilegeCount = 1;
  119. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  120. if (LookupPrivilegeValue(NULL, SE_MANAGE_VOLUME_NAME, &tp.Privileges[0].Luid))
  121. AdjustTokenPrivileges(token, FALSE, &tp, 0, NULL, NULL);
  122. CloseHandle(token);
  123. }
  124. }
  125. // Select mode.
  126. const char* modeName;
  127. DWORD access;
  128. DWORD creation;
  129. switch (mode)
  130. {
  131. case Read: modeName = "read"; access = GENERIC_READ; creation = OPEN_EXISTING; break;
  132. case Create: modeName = "create"; access = GENERIC_READ | GENERIC_WRITE; creation = CREATE_ALWAYS; break;
  133. case Modify: modeName = "modify"; access = GENERIC_READ | GENERIC_WRITE; creation = OPEN_ALWAYS; break;
  134. default: FW_ASSERT(false); return;
  135. }
  136. // Open.
  137. DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED;
  138. if (disableCache)
  139. flags |= FILE_FLAG_NO_BUFFERING;
  140. m_handle = CreateFile(
  141. name.getPtr(),
  142. access,
  143. FILE_SHARE_READ,
  144. NULL,
  145. creation,
  146. flags,
  147. NULL);
  148. if (m_handle == INVALID_HANDLE_VALUE)
  149. setError("Cannot open file '%s' for %s!", m_name.getPtr(), modeName);
  150. // Get size.
  151. LARGE_INTEGER size;
  152. size.QuadPart = 0;
  153. if (m_handle && !GetFileSizeEx(m_handle, &size))
  154. setError("GetFileSizeEx() failed on '%s'!", m_name.getPtr());
  155. m_size = size.QuadPart;
  156. m_actualSize = size.QuadPart;
  157. // Get alignment.
  158. if (disableCache)
  159. {
  160. DWORD bytesPerSector = 1;
  161. if (!GetDiskFreeSpace(NULL, NULL, &bytesPerSector, NULL, NULL))
  162. failWin32Error("GetDiskFreeSpace");
  163. m_align = bytesPerSector;
  164. }
  165. FW_ASSERT((m_align & (m_align - 1)) == 0);
  166. }
  167. //------------------------------------------------------------------------
  168. File::~File(void)
  169. {
  170. if (!m_handle)
  171. return;
  172. fixSize();
  173. CancelIo(m_handle);
  174. CloseHandle(m_handle);
  175. }
  176. //------------------------------------------------------------------------
  177. bool File::checkWritable(void) const
  178. {
  179. if (m_mode != File::Read)
  180. return true;
  181. setError("File '%s' was opened as read-only!", m_name.getPtr());
  182. return false;
  183. }
  184. //------------------------------------------------------------------------
  185. void File::seek(S64 ofs)
  186. {
  187. if (ofs >= 0 && ofs <= m_size)
  188. m_offset = ofs;
  189. else
  190. setError("Tried to seek outside '%s'!", m_name.getPtr());
  191. }
  192. //------------------------------------------------------------------------
  193. void File::setSize(S64 size)
  194. {
  195. if (!checkWritable() || !m_handle)
  196. return;
  197. if (size >= 0)
  198. m_size = size;
  199. else
  200. setError("Tried to set negative size for '%s'!", m_name.getPtr());
  201. }
  202. //------------------------------------------------------------------------
  203. void File::allocateSpace(S64 size)
  204. {
  205. if (m_mode == Read || !m_handle || m_actualSize >= size)
  206. return;
  207. LARGE_INTEGER ofs;
  208. ofs.QuadPart = (size + m_align - 1) & -m_align;
  209. if (SetFilePointerEx(m_handle, ofs, NULL, FILE_BEGIN) && SetEndOfFile(m_handle))
  210. {
  211. SetFileValidData(m_handle, ofs.QuadPart);
  212. m_actualSize = ofs.QuadPart;
  213. }
  214. }
  215. //------------------------------------------------------------------------
  216. int File::read(void* ptr, int size)
  217. {
  218. AsyncOp* op = readAsync(ptr, size);
  219. op->wait();
  220. int numBytes = op->getNumBytes();
  221. delete op;
  222. return numBytes;
  223. }
  224. //------------------------------------------------------------------------
  225. void File::write(const void* ptr, int size)
  226. {
  227. delete writeAsync(ptr, size);
  228. }
  229. //------------------------------------------------------------------------
  230. void File::flush(void)
  231. {
  232. if (m_mode == Read || !m_handle)
  233. return;
  234. profilePush("Flush file");
  235. if (!FlushFileBuffers(m_handle))
  236. setError("FlushFileBuffers() failed on '%s'!", m_name.getPtr());
  237. profilePop();
  238. fixSize();
  239. }
  240. //------------------------------------------------------------------------
  241. File::AsyncOp* File::readAsync(void* ptr, int size)
  242. {
  243. FW_ASSERT(ptr || !size);
  244. profilePush("Read file");
  245. // Create AsyncOp.
  246. AsyncOp* op = new AsyncOp(m_handle);
  247. op->m_userBytes = max((int)min((S64)size, m_size - m_offset), 0);
  248. if (!m_handle)
  249. op->m_userBytes = 0;
  250. int mask = m_align - 1;
  251. op->m_numBytes = min((op->m_userBytes + mask) & ~mask, max(size, 0));
  252. // Aligned => read directly.
  253. if (!op->m_numBytes || (((UPTR)ptr & (UPTR)mask) == 0 && (op->m_numBytes & mask) == 0))
  254. {
  255. op->m_offset = m_offset;
  256. op->m_readPtr = ptr;
  257. }
  258. // Unaligned => read through temporary buffer.
  259. else
  260. {
  261. op->m_offset = m_offset & ~mask;
  262. op->m_numBytes = (((S32)m_offset & mask) + op->m_userBytes + mask) & ~mask;
  263. op->m_readPtr = allocAligned(op->m_freePtr, op->m_numBytes);
  264. op->m_copyBytes = op->m_userBytes;
  265. op->m_copySrc = (U8*)op->m_readPtr + ((S32)m_offset & mask);
  266. op->m_copyDst = ptr;
  267. }
  268. // Execute the op.
  269. op->m_expectedBytes = (S32)min((S64)op->m_numBytes, m_actualSize - op->m_offset);
  270. startOp(op);
  271. if (!op->hasFailed())
  272. m_offset += op->m_userBytes;
  273. profilePop();
  274. return op;
  275. }
  276. //------------------------------------------------------------------------
  277. File::AsyncOp* File::writeAsync(const void* ptr, int size)
  278. {
  279. FW_ASSERT(ptr || !size);
  280. profilePush("Write file");
  281. // Create AsyncOp.
  282. AsyncOp* op = new AsyncOp(m_handle);
  283. op->m_userBytes = size;
  284. if (op->m_userBytes < 0 || !checkWritable() || !m_handle)
  285. op->m_userBytes = 0;
  286. // Aligned => write directly.
  287. int mask = m_align - 1;
  288. if (!op->m_userBytes || (((UPTR)ptr & (UPTR)mask) == 0 && (op->m_userBytes & mask) == 0))
  289. {
  290. op->m_offset = m_offset;
  291. op->m_numBytes = op->m_userBytes;
  292. op->m_writePtr = ptr;
  293. }
  294. // Unaligned => write through temporary buffer.
  295. else
  296. {
  297. S64 start = m_offset & ~mask;
  298. S64 end = (m_offset + op->m_userBytes + mask) & ~mask;
  299. op->m_offset = start;
  300. op->m_numBytes = (S32)(end - start);
  301. U8* buffer = (U8*)allocAligned(op->m_freePtr, op->m_numBytes);
  302. op->m_writePtr = buffer;
  303. // Read head.
  304. if (start != m_offset && start < m_size)
  305. if (!readAligned(start, buffer, m_align))
  306. op->failed();
  307. // Read tail.
  308. if (end != m_offset + op->m_userBytes && end > start + m_align && end - m_align < m_size)
  309. if (!readAligned(end - m_align, buffer + end - m_align - start, m_align))
  310. op->failed();
  311. // Copy body.
  312. memcpy(buffer + m_offset - start, ptr, op->m_userBytes);
  313. }
  314. op->m_expectedBytes = op->m_numBytes;
  315. startOp(op);
  316. if (!op->hasFailed())
  317. {
  318. m_offset += op->m_userBytes;
  319. m_size = max(m_size, m_offset);
  320. }
  321. profilePop();
  322. return op;
  323. }
  324. //------------------------------------------------------------------------
  325. void File::fixSize(void)
  326. {
  327. if (m_mode == Read || !m_handle || m_actualSize == m_size)
  328. return;
  329. // Size is not aligned properly => reopen with buffering.
  330. profilePush("Resize file");
  331. bool reopen = ((m_size & (m_align - 1)) != 0);
  332. if (reopen)
  333. {
  334. CloseHandle(m_handle);
  335. m_handle = CreateFile(
  336. m_name.getPtr(),
  337. GENERIC_WRITE,
  338. FILE_SHARE_READ,
  339. NULL,
  340. OPEN_EXISTING,
  341. FILE_ATTRIBUTE_NORMAL,
  342. NULL);
  343. if (!m_handle)
  344. setError("CreateFile() failed on '%s'!", m_name.getPtr());
  345. }
  346. // Set size.
  347. LARGE_INTEGER ofs;
  348. ofs.QuadPart = m_size;
  349. if (!SetFilePointerEx(m_handle, ofs, NULL, FILE_BEGIN))
  350. setError("SetFilePointerEx() failed on '%s'!", m_name.getPtr());
  351. else if (!SetEndOfFile(m_handle))
  352. setError("SetEndOfFile() failed on '%s'!", m_name.getPtr());
  353. else
  354. m_actualSize = m_size;
  355. // File was reopened => reopen without buffering.
  356. if (reopen)
  357. {
  358. CloseHandle(m_handle);
  359. m_handle = CreateFile(
  360. m_name.getPtr(),
  361. GENERIC_READ | GENERIC_WRITE,
  362. FILE_SHARE_READ,
  363. NULL,
  364. OPEN_EXISTING,
  365. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
  366. NULL);
  367. if (!m_handle)
  368. setError("CreateFile() failed on '%s'!", m_name.getPtr());
  369. }
  370. profilePop();
  371. }
  372. //------------------------------------------------------------------------
  373. void File::startOp(AsyncOp* op)
  374. {
  375. // Backup parameters.
  376. FW_ASSERT(op);
  377. S64 offset = op->m_offset;
  378. S32 numBytes = op->m_numBytes;
  379. S32 expectedBytes = op->m_expectedBytes;
  380. U8* readPtr = (U8*)op->m_readPtr;
  381. U8* writePtr = (U8*)op->m_writePtr;
  382. // Nothing to do => skip.
  383. if (!numBytes)
  384. {
  385. op->done();
  386. return;
  387. }
  388. // Write past the end of file => expand.
  389. S64 sizeNeeded = offset + numBytes;
  390. if (op->m_writePtr && m_actualSize < sizeNeeded)
  391. {
  392. if (m_disableCache)
  393. allocateSpace(max(sizeNeeded, m_actualSize * MinimumExpandNum / MinimumExpandDenom));
  394. m_actualSize = max(m_actualSize, sizeNeeded);
  395. }
  396. // Loop over blocks corresponding to MaxBytesPerSysCall.
  397. // Only the last one is executed asynchronously.
  398. S32 pos = 0;
  399. for (;;)
  400. {
  401. // Setup AsyncOp.
  402. AsyncOp* blockOp = op;
  403. if (pos + MaxBytesPerSysCall < numBytes)
  404. blockOp = new AsyncOp(m_handle);
  405. blockOp->m_offset = offset + pos;
  406. blockOp->m_numBytes = min(numBytes - pos, (S32)MaxBytesPerSysCall);
  407. blockOp->m_expectedBytes = min(expectedBytes - pos, blockOp->m_numBytes);
  408. blockOp->m_readPtr = (readPtr) ? readPtr + pos : NULL;
  409. blockOp->m_writePtr = (writePtr) ? writePtr + pos : NULL;
  410. // Queue the op.
  411. BOOL ok;
  412. const char* funcName;
  413. blockOp->m_overlapped.Offset = (DWORD)blockOp->m_offset;
  414. blockOp->m_overlapped.OffsetHigh = (DWORD)(blockOp->m_offset >> 32);
  415. if (op->m_readPtr)
  416. {
  417. ok = ReadFile(m_handle, blockOp->m_readPtr, blockOp->m_numBytes, NULL, &blockOp->m_overlapped);
  418. funcName = "ReadFile";
  419. }
  420. else
  421. {
  422. ok = WriteFile(m_handle, blockOp->m_writePtr, blockOp->m_numBytes, NULL, &blockOp->m_overlapped);
  423. funcName = "WriteFile";
  424. }
  425. // Check result.
  426. if (ok)
  427. blockOp->done();
  428. else if (GetLastError() != ERROR_IO_PENDING)
  429. {
  430. setError("%s() failed on '%s'!", funcName, m_name.getPtr());
  431. blockOp->failed();
  432. }
  433. // Last op => done.
  434. if (blockOp == op)
  435. break;
  436. // Wait for the op to finish.
  437. pos += blockOp->m_numBytes;
  438. blockOp->wait();
  439. bool failed = blockOp->hasFailed();
  440. delete blockOp;
  441. if (failed)
  442. {
  443. op->failed();
  444. break;
  445. }
  446. }
  447. }
  448. //------------------------------------------------------------------------
  449. void* File::allocAligned(void*& base, int size)
  450. {
  451. base = (U8*)malloc(size + m_align - 1);
  452. U8* ptr = (U8*)base + m_align - 1;
  453. ptr -= (UPTR)ptr & (UPTR)(m_align - 1);
  454. return ptr;
  455. }
  456. //------------------------------------------------------------------------
  457. bool File::readAligned(S64 ofs, void* ptr, int size)
  458. {
  459. AsyncOp* op = new AsyncOp(m_handle);
  460. op->m_offset = ofs;
  461. op->m_numBytes = size;
  462. op->m_readPtr = ptr;
  463. op->m_expectedBytes = (S32)min((S64)size, m_actualSize - ofs);
  464. startOp(op);
  465. op->wait();
  466. bool ok = (!op->hasFailed());
  467. delete op;
  468. return ok;
  469. }
  470. //------------------------------------------------------------------------