Source code of Windows XP (NT5)
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.

1228 lines
29 KiB

  1. /****************************************************************************
  2. *
  3. * DIRECTIO.CPP
  4. *
  5. * routines for reading Standard AVI files
  6. *
  7. * Copyright (c) 1992 - 1995 Microsoft Corporation. All Rights Reserved.
  8. *
  9. *
  10. * implementation of a disk i/o class designed to optimise
  11. * sequential reading and writing to disk by using overlapped i/o (for read
  12. * ahead and write behind) and using large buffers written with no buffering.
  13. *
  14. ***************************************************************************/
  15. #include <windows.h>
  16. #include <win32.h>
  17. #include "debug.h"
  18. #include "directio.h"
  19. #ifdef USE_DIRECTIO
  20. //
  21. // implementation of a disk i/o class designed to optimise
  22. // sequential reading and writing to disk by using overlapped i/o (for read
  23. // ahead and write behind) and using large buffers written with no buffering.
  24. // -- CFileStream class methods ---------------------------------------
  25. // initialise to known (invalid) state
  26. CFileStream::CFileStream()
  27. {
  28. m_State = Invalid;
  29. m_Position = 0;
  30. m_hFile = INVALID_HANDLE_VALUE;
  31. #ifdef CHICAGO
  32. ZeroMemory(&m_qio, sizeof(m_qio));
  33. #endif
  34. }
  35. BOOL
  36. CFileStream::Open(LPTSTR file, BOOL bWrite, BOOL bTruncate)
  37. {
  38. if (m_State != Invalid) {
  39. return FALSE;
  40. }
  41. // remember this for default streaming mode
  42. m_bWrite = bWrite;
  43. DWORD dwAccess = GENERIC_READ;
  44. if (bWrite) {
  45. dwAccess |= GENERIC_WRITE;
  46. }
  47. // open the file. Always get read access. exclusive open if we
  48. // are writing the file, otherwise deny other write opens.
  49. // never truncate the file, since the file may be de-fragmented.
  50. #ifdef CHICAGO
  51. DWORD dwFlags = FILE_FLAG_NO_BUFFERING;
  52. #else
  53. DWORD dwFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING;
  54. #endif
  55. m_hFile = CreateFile(file,
  56. dwAccess,
  57. (bWrite ? 0 : FILE_SHARE_READ),
  58. NULL,
  59. OPEN_ALWAYS,
  60. dwFlags,
  61. 0);
  62. if (m_hFile == INVALID_HANDLE_VALUE) {
  63. return FALSE;
  64. }
  65. #ifdef CHICAGO
  66. if ( ! QioInitialize(&m_qio, m_hFile, THREAD_PRIORITY_HIGHEST)) {
  67. CloseHandle (m_hFile);
  68. return FALSE;
  69. }
  70. #endif
  71. // find the bytes per sector that we have to round to for this file
  72. // -requires finding the 'root path' for this file.
  73. TCHAR ch[MAX_PATH];
  74. LPTSTR ptmp; //required arg
  75. GetFullPathName(file, sizeof(ch)/sizeof(ch[0]), ch, &ptmp);
  76. // truncate this to the name of the root directory
  77. if ((ch[0] == TEXT('\\')) && (ch[1] == TEXT('\\'))) {
  78. // path begins with \\server\share\path so skip the first
  79. // three backslashes
  80. ptmp = &ch[2];
  81. while (*ptmp && (*ptmp != TEXT('\\'))) {
  82. ptmp++;
  83. }
  84. if (*ptmp) {
  85. // advance past the third backslash
  86. ptmp++;
  87. }
  88. } else {
  89. // path must be drv:\path
  90. ptmp = ch;
  91. }
  92. // find next backslash and put a null after it
  93. while (*ptmp && (*ptmp != TEXT('\\'))) {
  94. ptmp++;
  95. }
  96. // found a backslash ?
  97. if (*ptmp) {
  98. // skip it and insert null
  99. ptmp++;
  100. *ptmp = TEXT('\0');
  101. }
  102. DWORD dwtmp1, dwtmp2, dwtmp3;
  103. if (!GetDiskFreeSpace(ch,
  104. &dwtmp1,
  105. &m_SectorSize,
  106. &dwtmp2,
  107. &dwtmp3))
  108. m_SectorSize = 2048;
  109. // sigh. now init the first buffer
  110. // sets the right buffer count and size for current mode
  111. m_State = Stopped;
  112. if (!EnsureBuffersValid()) {
  113. return FALSE;
  114. }
  115. m_Current = 0;
  116. m_Position = 0;
  117. // if asked to truncate the file, we will not actually do so, since this
  118. // could throw away a de-fragged file. We will however, note that the file
  119. // size is 0 and use this to affect reading and writing past 'eof' - eg
  120. // if you write 8 bytes to the beginning of a truncated file, we do not
  121. // need to read in the first sector beforehand.
  122. if (bTruncate) {
  123. m_Size = 0;
  124. } else {
  125. // get the current file size
  126. m_Size = GetFileSize(m_hFile, NULL);
  127. }
  128. // all set
  129. return TRUE;
  130. }
  131. BOOL
  132. CFileStream::Seek(DWORD pos)
  133. {
  134. // we just record this and go away
  135. //if (pos < m_Position) {
  136. // DPF("seek back by 0x%x to 0x%x\n", m_Position - pos, pos);
  137. //}
  138. m_Position = pos;
  139. return TRUE;
  140. }
  141. DWORD
  142. CFileStream::GetCurrentPosition()
  143. {
  144. return m_Position;
  145. }
  146. BOOL
  147. CFileStream::Write(LPBYTE pData, DWORD count, DWORD * pbyteswritten)
  148. {
  149. *pbyteswritten = 0;
  150. // error if file not opened
  151. if (m_State == Invalid) {
  152. return FALSE;
  153. }
  154. DWORD nBytes;
  155. while (count > 0) {
  156. // is our current buffer ready to write this data ?
  157. // (we need to tell it eof pos as well since if eof is
  158. // in middle of buffer but beyond valid data, ok to write.)
  159. if ((m_Current < 0) ||
  160. (!m_Buffers[m_Current].QueryPosition(m_Position, m_Size))) {
  161. // commit this buffer if we have changed position beyond it
  162. if (m_Current >= 0) {
  163. if (!m_Buffers[m_Current].Commit()) {
  164. // file error - abort
  165. return FALSE;
  166. }
  167. // if we are streaming, then advance to next buffer while
  168. // current one is writing.
  169. if (m_State != Stopped) {
  170. m_Current = NextBuffer(m_Current);
  171. }
  172. } else {
  173. m_Current = 0;
  174. }
  175. // make sure that previous operations on this buffer have completed
  176. if (!m_Buffers[m_Current].WaitComplete()) {
  177. // i/o error
  178. return FALSE;
  179. }
  180. }
  181. // we either have a buffer that has already pre-read the sector
  182. // we start writing to, or we have an idle buffer that
  183. // will do the pre-read for us
  184. if (!m_Buffers[m_Current].Write(m_Position, pData, count, m_Size, &nBytes)) {
  185. return FALSE;
  186. }
  187. count -= nBytes;
  188. pData += nBytes;
  189. m_Position += nBytes;
  190. *pbyteswritten += nBytes;
  191. }
  192. if (m_Position > m_Size) {
  193. m_Size = m_Position;
  194. }
  195. return TRUE;
  196. }
  197. BOOL
  198. CFileStream::Read(LPBYTE pData, DWORD count, DWORD * pbytesread)
  199. {
  200. *pbytesread = 0;
  201. // error if file not opened
  202. if (m_State == Invalid) {
  203. return FALSE;
  204. }
  205. // force the read to be within the file size limits
  206. if (m_Position >= m_Size) {
  207. // all done - nothing read
  208. return TRUE;
  209. } else {
  210. count = min(count, (m_Size - m_Position));
  211. }
  212. BOOL bDoReadAhead = FALSE;
  213. DWORD nBytes;
  214. while (count > 0) {
  215. // is data within current buffer
  216. if ((m_Current < 0) ||
  217. (!m_Buffers[m_Current].QueryPosition(m_Position, m_Size))) {
  218. if (m_Current >= 0) {
  219. // commit this buffer if we have changed position beyond it
  220. if (!m_Buffers[m_Current].Commit()) {
  221. // file error - abort
  222. return FALSE;
  223. }
  224. // advance to next buffer (if streaming)
  225. if (m_State == Writing) {
  226. m_Current = NextBuffer(m_Current);
  227. } else if (m_State == Reading) {
  228. // smart read-ahead strategy: try to find in existing
  229. // buffers, and only issue a read-ahead if we take the
  230. // highest buffer
  231. int n = NextBuffer(m_Current);
  232. m_Current = -1;
  233. for (int i = 0; i < m_NrValid; i++) {
  234. if (m_Buffers[n].QueryPosition(m_Position, m_Size)) {
  235. m_Current = n;
  236. break;
  237. }
  238. n = NextBuffer(n);
  239. }
  240. if (m_Current < 0) {
  241. // read-ahead is messed up because we have made too big
  242. // a seek for the current buffer size
  243. // Best thing is to use the lowest buffer (should be the
  244. // one after the highest, and to restart readaheads with
  245. // this position).
  246. m_Current = NextBuffer(m_HighestBuffer);
  247. m_HighestBuffer = m_Current;
  248. DPF("using idle %d\n", m_Current);
  249. }
  250. if (m_Current == m_HighestBuffer) {
  251. bDoReadAhead = TRUE;
  252. }
  253. }
  254. } else {
  255. m_Current = 0;
  256. if (m_Current == m_HighestBuffer) {
  257. bDoReadAhead = TRUE;
  258. }
  259. }
  260. // make sure that previous operations on this buffer have completed
  261. if (!m_Buffers[m_Current].WaitComplete()) {
  262. // i/o error
  263. return FALSE;
  264. }
  265. }
  266. // now we have a buffer that either contains the data we want, or
  267. // is idle and ready to fetch it.
  268. if (!m_Buffers[m_Current].Read(m_Position, pData, count, m_Size, &nBytes)) {
  269. return FALSE;
  270. }
  271. count -= nBytes;
  272. pData += nBytes;
  273. m_Position += nBytes;
  274. *pbytesread += nBytes;
  275. // do read ahead now if necessary (the Read() call may have required
  276. // a seek and read if the data was not in the buffer, so delay the
  277. // read-ahead until after it has completed).
  278. if (bDoReadAhead) {
  279. // remember that this new buffer contains the highest position
  280. // -- we should issue another readahead when we start using this
  281. // buffer.
  282. m_HighestBuffer = NextBuffer(m_Current);
  283. DWORD p = m_Buffers[m_Current].GetNextPosition();
  284. m_Buffers[m_HighestBuffer].ReadAhead(p, m_Size);
  285. bDoReadAhead = FALSE;
  286. }
  287. }
  288. return TRUE;
  289. }
  290. // set the right buffer size and count for current mode
  291. BOOL
  292. CFileStream::EnsureBuffersValid()
  293. {
  294. if (m_State == Invalid) {
  295. // file not opened
  296. return FALSE;
  297. }
  298. #ifdef CHICAGO
  299. if (m_State == Writing) {
  300. m_NrValid = 4; // total 256k
  301. } else if (m_State == Reading) {
  302. m_NrValid = 4; // total 256k
  303. } else {
  304. m_NrValid = 1; // total 64k
  305. }
  306. int size = (64 * 1024);
  307. #else
  308. if (m_State == Writing) {
  309. m_NrValid = 2; // total 512k
  310. } else if (m_State == Reading) {
  311. m_NrValid = 4; // total 256k
  312. } else {
  313. m_NrValid = 1; // total 64k
  314. }
  315. int size = (64 * 1024);
  316. if (m_State == Writing)
  317. size = (256 * 1024);
  318. #endif
  319. int i =0;
  320. Assert(m_NrValid <= NR_OF_BUFFERS);
  321. // init valid buffers
  322. for (; i < m_NrValid; i++) {
  323. #ifdef CHICAGO
  324. if (!m_Buffers[i].Init(m_SectorSize, size, &m_qio)) {
  325. #else
  326. if (!m_Buffers[i].Init(m_SectorSize, size, m_hFile)) {
  327. #endif
  328. return FALSE;
  329. }
  330. }
  331. // discard others
  332. for (; i < NR_OF_BUFFERS; i++) {
  333. m_Buffers[i].FreeMemory();
  334. }
  335. return TRUE;
  336. }
  337. BOOL
  338. CFileStream::StartStreaming()
  339. {
  340. if (m_bWrite) {
  341. return StartWriteStreaming();
  342. } else {
  343. return StartReadStreaming();
  344. }
  345. }
  346. BOOL
  347. CFileStream::StartWriteStreaming()
  348. {
  349. m_State = Writing;
  350. if (!EnsureBuffersValid()) {
  351. return FALSE;
  352. }
  353. return TRUE;
  354. }
  355. BOOL
  356. CFileStream::StartReadStreaming()
  357. {
  358. // commit the current buffer
  359. if (!m_Buffers[m_Current].Commit()) {
  360. return FALSE;
  361. }
  362. m_State = Reading;
  363. if (!EnsureBuffersValid()) {
  364. return FALSE;
  365. }
  366. // start read-ahead on buffer 0 - read from current position
  367. // (tell buffer the eof point so it won't bother reading beyond it)
  368. // remember that this is the highest current buffer - when we start using
  369. // this buffer it is time to issue the next readahead (this allows for
  370. // seeks backwards and forwards within the valid buffers without upsetting
  371. // the read-aheads).
  372. m_HighestBuffer = 0;
  373. m_Buffers[0].ReadAhead(m_Position, m_Size);
  374. // set m_Current invalid: this ensures that we will wait for read-ahead
  375. // to complete before getting data, and that when we start using it, we
  376. // will issue the next read-ahead.
  377. m_Current = -1;
  378. return TRUE;
  379. }
  380. BOOL
  381. CFileStream::StopStreaming()
  382. {
  383. // complete all i/o
  384. if (!CommitAndWait()) {
  385. return FALSE;
  386. }
  387. m_Current = 0;
  388. m_State = Stopped;
  389. // recalc buffer size/count for new mode
  390. if (!EnsureBuffersValid()) {
  391. return FALSE;
  392. }
  393. return TRUE;
  394. }
  395. // wait for all transfers to complete.
  396. BOOL CFileStream::CommitAndWait()
  397. {
  398. // write current buffer
  399. //
  400. if (!m_Buffers[m_Current].Commit())
  401. return FALSE;
  402. #ifdef CHICAGO
  403. // flush all buffers that have been queued
  404. //
  405. //QioCommit (&m_qio);
  406. #endif
  407. // wait for all buffers to complete
  408. for (int i = 0; i < m_NrValid; i++) {
  409. if (!m_Buffers[i].WaitComplete()) {
  410. return FALSE;
  411. }
  412. }
  413. // no need to reset m_Current
  414. return TRUE;
  415. }
  416. // destructor will call Commit()
  417. CFileStream::~CFileStream()
  418. {
  419. if (m_hFile != INVALID_HANDLE_VALUE) {
  420. CommitAndWait();
  421. #ifdef CHICAGO
  422. QioShutdown (&m_qio);
  423. #endif
  424. CloseHandle(m_hFile);
  425. }
  426. }
  427. // --- CFileBuffer methods -----------------------------------------
  428. // initiate to an invalid (no buffer ready) state
  429. CFileBuffer::CFileBuffer()
  430. {
  431. m_pBuffer = NULL;
  432. m_pAllocedMem = NULL;
  433. m_State = Invalid;
  434. #ifdef CHICAGO
  435. m_pqio = NULL;
  436. #endif
  437. }
  438. // allocate memory and become idle.
  439. BOOL
  440. #ifdef CHICAGO
  441. CFileBuffer::Init(DWORD nBytesPerSector, DWORD buffersize, LPQIO pqio)
  442. #else
  443. CFileBuffer::Init(DWORD nBytesPerSector, DWORD buffersize, HANDLE hfile)
  444. #endif
  445. {
  446. if (m_State != Invalid) {
  447. if ((nBytesPerSector == m_BytesPerSector) &&
  448. (buffersize == RoundSizeToSector(m_TotalSize))) {
  449. // we're there already
  450. return TRUE;
  451. }
  452. // discard what we have
  453. FreeMemory();
  454. }
  455. Assert(m_State == Invalid);
  456. // round up RAWIO_SIZE to a multiple of sector size
  457. m_BytesPerSector = nBytesPerSector;
  458. m_TotalSize = (DWORD) RoundSizeToSector(buffersize);
  459. m_DataLength = 0;
  460. m_State = Idle;
  461. m_bDirty = FALSE;
  462. #ifdef CHICAGO
  463. m_pqio = pqio;
  464. m_pAllocedMem = (unsigned char *)VirtualAlloc (NULL, m_TotalSize,
  465. MEM_RESERVE | MEM_COMMIT,
  466. PAGE_READWRITE);
  467. if (m_pAllocedMem == NULL)
  468. return FALSE;
  469. #else
  470. m_hFile = hfile;
  471. m_pAllocedMem = new BYTE[m_TotalSize];
  472. if (m_pAllocedMem == NULL)
  473. return FALSE;
  474. m_Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  475. if (!m_Overlapped.hEvent) {
  476. delete[] m_pAllocedMem;
  477. return FALSE;
  478. }
  479. #endif
  480. // this is where my naming scheme falls down. RoundPos rounds down, and
  481. // RoundSize rounds up. to correctly align the buffer and stay within it,
  482. // we need to round the start address up and the size down.
  483. // round start address up to sector size
  484. m_pBuffer = (LPBYTE) RoundSizeToSector((LONG_PTR) m_pAllocedMem);
  485. // remove rounding from size - and round it again!
  486. m_TotalSize = (DWORD) RoundPosToSector(m_TotalSize - (m_pBuffer - m_pAllocedMem));
  487. return TRUE;
  488. }
  489. // revert to invalid state (eg when streaming stops)
  490. void
  491. CFileBuffer::FreeMemory()
  492. {
  493. if (m_State == Idle) {
  494. Commit();
  495. }
  496. if (m_State == Busy) {
  497. WaitComplete();
  498. }
  499. if (m_State != Invalid) {
  500. #ifdef CHICAGO
  501. VirtualFree (m_pAllocedMem, 0, MEM_RELEASE);
  502. m_pBuffer = NULL;
  503. m_pAllocedMem = NULL;
  504. #else
  505. CloseHandle(m_Overlapped.hEvent);
  506. delete[] m_pAllocedMem;
  507. #endif
  508. m_State = Invalid;
  509. }
  510. }
  511. // calls commit if dirty before freeing everything.
  512. CFileBuffer::~CFileBuffer()
  513. {
  514. FreeMemory();
  515. }
  516. // does this position occur anywhere within the current buffer ?
  517. // needs to know current eof for some cases (writing beyond eof
  518. // if eof is within this buffer is ok to this buffer).
  519. //
  520. // we can use this buffer if:
  521. // 1. if the buffer is empty and the write is past eof (where eof is rounded
  522. // to a sector boundary).
  523. //
  524. // 2. if the start position is within the current m_DataLength
  525. //
  526. // 3. if eof is within the buffer and the write is past eof
  527. //
  528. // all reads are limited by the caller to be within the file limits, so
  529. // the reading case is covered by 2 above (1 and 3 will not occur).
  530. //
  531. // all other cases will require the read of data that is not in the buffer.
  532. // or the (early) committing of data in the buffer
  533. //
  534. BOOL
  535. CFileBuffer::QueryPosition(DWORD pos, DWORD filesize)
  536. {
  537. if (m_State == Invalid) {
  538. return FALSE;
  539. }
  540. // round filesize to sector boundary
  541. filesize = (DWORD) RoundSizeToSector(filesize);
  542. if (pos >= filesize) {
  543. // write is past eof. ok if buffer empty or if buffer contains
  544. // eof (and has space in it)
  545. if ((m_DataLength == 0) ||
  546. ((m_Position + m_DataLength == filesize) &&
  547. (m_DataLength < m_TotalSize))) {
  548. return TRUE;
  549. }
  550. // we have data that needs to be flushed before we can do this
  551. return FALSE;
  552. } else {
  553. if ((pos >= m_Position) &&
  554. (pos < m_Position + m_DataLength)) {
  555. // we have this byte
  556. return TRUE;
  557. }
  558. // we don't have this byte of valid data. we have some other.
  559. //
  560. // you might think that if the write begins on a sector boundary, and
  561. // this buffer's data is not dirty you could permit this without a
  562. // pre-read - but we don't know yet where the write will end, and if
  563. // it ends mid-sector and not past current eof, we will need to
  564. // read that sector in.
  565. return FALSE;
  566. }
  567. }
  568. // write some data to buffer (must be committed separately)
  569. // filesize parameter is the file size before this write, and is used to
  570. // control what we do with the partial sector at beginning and end
  571. // -if not past current eof, we need to read the current sector before
  572. // writing to it.
  573. BOOL
  574. CFileBuffer::Write(
  575. DWORD pos,
  576. LPBYTE pData,
  577. DWORD count,
  578. DWORD filesize,
  579. DWORD * pbytesWritten)
  580. {
  581. // remember for later (during commit)
  582. m_FileLength = filesize;
  583. *pbytesWritten = 0;
  584. if (m_State != Idle) {
  585. if (!WaitComplete()) {
  586. return FALSE;
  587. }
  588. }
  589. if (m_State == Invalid) {
  590. // naughty boy!
  591. return FALSE;
  592. }
  593. // do we need to commit the current contents or read anything ?
  594. // if there is data, and the start position is not within the valid data
  595. // range, then flush this lot. note that we count the region from
  596. // end of valid data to end of actual buffer as valid data if the eof
  597. // is within this buffer.
  598. if ((m_DataLength > 0) &&
  599. ((pos < m_Position) ||
  600. (pos >= m_Position + m_TotalSize) ||
  601. ((pos >= m_Position + m_DataLength) &&
  602. ((m_Position + m_DataLength) < filesize)))) {
  603. // we're not ok - need to flush current contents
  604. if (!Commit() || !WaitComplete()) {
  605. return FALSE;
  606. }
  607. m_DataLength = 0;
  608. }
  609. // if empty (or we just flushed it), we can start at the beginning
  610. if (m_DataLength == 0) {
  611. m_Position = (DWORD) RoundPosToSector(pos);
  612. // do we need to read the partial sector?
  613. if ((pos < RoundSizeToSector(filesize)) &&
  614. (pos % m_BytesPerSector != 0)) {
  615. // yes - write starts partway through a valid sector
  616. m_DataLength = m_BytesPerSector;
  617. if (!ReadIntoBuffer(0, m_Position, m_BytesPerSector) ||
  618. !WaitComplete()) {
  619. return FALSE;
  620. }
  621. }
  622. }
  623. // we can start the data. now what about the end?
  624. // if it all fits within the buffer, and it ends mid-sector and the
  625. // final sector is within the file length but not currently in the
  626. // buffer, we will need to pre-read the final buffer
  627. if ((pos + count) < (m_Position + m_TotalSize)) {
  628. if ((pos + count) % m_BytesPerSector) {
  629. // we have to write a partial sector - is it past eof or within
  630. // valid region ?
  631. if ((pos+count > m_Position + m_DataLength) &&
  632. (pos+count < filesize)) {
  633. // yes need to read partial sector
  634. DWORD sec = (DWORD) RoundPosToSector(pos+count);
  635. // need to temporarily set m_DataLength
  636. // to the amount read so that WaitComplete can check
  637. // its ok
  638. m_DataLength = m_BytesPerSector;
  639. if (!ReadIntoBuffer(
  640. sec - m_Position, // index in buffer
  641. sec, // position in file
  642. m_BytesPerSector) ||
  643. !WaitComplete()) {
  644. return FALSE;
  645. }
  646. // set size correctly again
  647. m_DataLength = (sec - m_Position) + m_BytesPerSector;
  648. }
  649. }
  650. }
  651. // now we can stuff the data in
  652. int index = pos - m_Position;
  653. *pbytesWritten = min(count, m_TotalSize - index);
  654. CopyMemory(
  655. &m_pBuffer[index],
  656. pData,
  657. *pbytesWritten);
  658. // adjust data length
  659. if ((index + *pbytesWritten) > m_DataLength) {
  660. m_DataLength = (DWORD) RoundSizeToSector(index + *pbytesWritten);
  661. }
  662. m_bDirty = TRUE;
  663. return TRUE;
  664. }
  665. // read data from buffer (will seek and read if necessary first)
  666. BOOL
  667. CFileBuffer::Read(
  668. DWORD pos,
  669. LPBYTE pData,
  670. DWORD count,
  671. DWORD filelength,
  672. DWORD * pBytesRead)
  673. {
  674. Assert(m_State == Idle);
  675. // remember this for read completion checking
  676. m_FileLength = filelength;
  677. *pBytesRead = 0;
  678. if ((pos < m_Position) ||
  679. (pos >= m_Position + m_DataLength)) {
  680. // not in current buffer - flush current contents if dirty
  681. if (!Commit() || !WaitComplete()) {
  682. return FALSE;
  683. }
  684. m_Position = (DWORD) RoundPosToSector(pos);
  685. // remember if we round the start down, we also need to increase
  686. // the length (as well as rounding it up at the other end)
  687. // force a minimum read size to avoid lots of single sectors
  688. m_DataLength = count + (pos - m_Position);
  689. m_DataLength = max(MIN_READ_SIZE, m_DataLength);
  690. m_DataLength = (DWORD) RoundSizeToSector(m_DataLength);
  691. m_DataLength = min(m_DataLength, m_TotalSize);
  692. if (!ReadIntoBuffer(0, m_Position, m_DataLength) ||
  693. !WaitComplete()) {
  694. return FALSE;
  695. }
  696. }
  697. // we have (at least the start part of) the data in the buffer
  698. int offset = pos - m_Position;
  699. count = min(count, m_DataLength - offset);
  700. CopyMemory(pData, &m_pBuffer[offset], count);
  701. *pBytesRead = count;
  702. return TRUE;
  703. }
  704. // what is the first file position after this buffer's valid data
  705. // ---return this even if still busy reading it
  706. DWORD
  707. CFileBuffer::GetNextPosition()
  708. {
  709. if ((m_State == Invalid) || (m_DataLength == 0)) {
  710. return 0;
  711. } else {
  712. return m_Position + m_DataLength;
  713. }
  714. }
  715. // initiate a read-ahead
  716. void
  717. CFileBuffer::ReadAhead(DWORD start, DWORD filelength)
  718. {
  719. if (m_State != Idle) {
  720. if (!CheckComplete()) {
  721. return;
  722. }
  723. }
  724. // we may already hold this position
  725. if (QueryPosition(start, filelength)) {
  726. return;
  727. }
  728. m_FileLength = filelength;
  729. if (m_bDirty) {
  730. // current data needs to be flushed to disk.
  731. // we should initiate this, but we can't wait for
  732. // it to complete, so we won't do the read-ahead
  733. Commit();
  734. return;
  735. }
  736. m_Position = (DWORD) RoundPosToSector(start);
  737. m_DataLength = min((DWORD) RoundSizeToSector(filelength - m_Position),
  738. m_TotalSize);
  739. ReadIntoBuffer(0, m_Position, m_DataLength);
  740. // no wait - this is an async readahead.
  741. }
  742. // initiate the i/o from the buffer
  743. BOOL
  744. CFileBuffer::Commit()
  745. {
  746. if ((m_State != Idle) || (!m_bDirty)) {
  747. return TRUE;
  748. }
  749. #ifndef CHICAGO
  750. DWORD nrWritten;
  751. #endif
  752. #ifdef CHICAGO
  753. m_State = Busy;
  754. m_qiobuf.dwOffset = m_Position;
  755. m_qiobuf.lpv = m_pBuffer;
  756. m_qiobuf.cb = m_DataLength;
  757. m_qiobuf.cbDone = 0;
  758. m_qiobuf.bWrite = TRUE;
  759. m_qiobuf.dwError = ERROR_IO_PENDING;
  760. QioAdd (m_pqio, &m_qiobuf);
  761. #else
  762. ResetEvent(m_Overlapped.hEvent);
  763. m_State = Busy;
  764. //start from m_Position
  765. m_Overlapped.Offset = m_Position;
  766. m_Overlapped.OffsetHigh = 0;
  767. if (WriteFile(m_hFile, m_pBuffer, m_DataLength,
  768. &nrWritten, &m_Overlapped)) {
  769. DPF(("instant completion"));
  770. // if it completed already, then sort out the new position
  771. if (nrWritten != m_DataLength) {
  772. DPF("commit- bad length %d not %d", nrWritten, m_DataLength);
  773. return FALSE;
  774. }
  775. m_bDirty = FALSE;
  776. m_State = Idle;
  777. } else {
  778. // should be pending
  779. if (GetLastError() != ERROR_IO_PENDING) {
  780. // no longer busy
  781. m_State = Idle;
  782. DPF("commit error %d", GetLastError());
  783. return FALSE;
  784. }
  785. }
  786. #endif
  787. // we must do this here, since WaitComplete could complete a
  788. // partial read that would leave the buffer dirty.
  789. // we are safe since the buffer will remain Busy until this is
  790. // actually TRUE. (if we fail to write to the disk then the
  791. // file state is guaranteed messed up).
  792. m_bDirty = FALSE;
  793. return TRUE;
  794. }
  795. // wait for any pending commit or read to complete and check for errors.
  796. BOOL
  797. CFileBuffer::WaitComplete()
  798. {
  799. if (m_State == ErrorOccurred) {
  800. // the i/o has completed in error but we haven't been able to
  801. // report the fact yet
  802. m_State = Idle;
  803. return FALSE;
  804. }
  805. if (m_State == Busy) {
  806. DWORD actual;
  807. // no longer busy
  808. m_State = Idle;
  809. #ifdef CHICAGO
  810. if ( ! QioWait (m_pqio, &m_qiobuf, TRUE))
  811. return FALSE;
  812. actual = m_qiobuf.cbDone;
  813. #else
  814. if (!GetOverlappedResult(m_hFile, &m_Overlapped, &actual, TRUE)) {
  815. DPF("WC: GetOverlapped failed %d", GetLastError());
  816. return FALSE;
  817. }
  818. #endif
  819. if (actual != m_DataLength) {
  820. // rounding to sector size may have taken us past eof
  821. if (m_Position + actual != m_FileLength) {
  822. DPF("WC: actual wrong (%d not %d)", actual, m_DataLength);
  823. return FALSE;
  824. }
  825. }
  826. }
  827. return TRUE;
  828. }
  829. // non-blocking check to see if async io is complete
  830. BOOL
  831. CFileBuffer::CheckComplete()
  832. {
  833. if (m_State == Idle) {
  834. return TRUE;
  835. }
  836. if (m_State != Busy) {
  837. return FALSE; // invalid or error
  838. }
  839. #ifdef CHICAGO
  840. if (QioWait(m_pqio, &m_qiobuf, FALSE))
  841. return FALSE;
  842. else if (m_qiobuf.dwError == 0) {
  843. m_State = Idle;
  844. return TRUE;
  845. }
  846. m_State = ErrorOccurred;
  847. return FALSE;
  848. #else
  849. DWORD actual;
  850. if (GetOverlappedResult(m_hFile, &m_Overlapped, &actual, FALSE)) {
  851. if ((actual == m_DataLength) ||
  852. (actual + m_Position == m_FileLength)) {
  853. m_State = Idle;
  854. return TRUE;
  855. }
  856. } else if (GetLastError() == ERROR_IO_INCOMPLETE) {
  857. // still busy
  858. return FALSE;
  859. }
  860. // some error state occurred - this must be reported by WaitComplete()
  861. m_State = ErrorOccurred;
  862. DPF("CheckComplete error %d", GetLastError());
  863. return FALSE;
  864. #endif
  865. }
  866. // initiates an async read request into the buffer (can be an insertion into
  867. // middle of buffer rather than a complete buffer fill - and so will not
  868. // adjust m_Position or m_DataLength). reads count bytes
  869. // offset bytes from the start of the buffer, pos bytes from the start of the
  870. // file. Assumes necessary rounding of length and position has already happened.
  871. BOOL
  872. CFileBuffer::ReadIntoBuffer(int offset, DWORD pos, DWORD count)
  873. {
  874. Assert(m_State == Idle);
  875. #ifndef CHICAGO
  876. DWORD nrRead;
  877. #endif
  878. #ifdef CHICAGO
  879. m_State = Busy;
  880. m_qiobuf.dwOffset = pos;
  881. m_qiobuf.lpv = (LPVOID)(m_pBuffer + offset);
  882. m_qiobuf.cb = count;
  883. m_qiobuf.cbDone = 0;
  884. m_qiobuf.bWrite = FALSE;
  885. m_qiobuf.dwError = ERROR_IO_PENDING;
  886. // if this read is not sector aligned, we cannot do it
  887. // in async in chicago, so do it right now!
  888. //
  889. if ((count & 511) || (pos & 511) || (offset & 511))
  890. {
  891. DWORD dwOff;
  892. m_qiobuf.bPending = FALSE;
  893. DPF("%s %X bytes (non-aligned) at %08X into %08X\r\n", m_qiobuf.bWrite ? "Writing" : "Reading", m_qiobuf.cb, m_qiobuf.dwOffset, m_qiobuf.lpv);
  894. dwOff = SetFilePointer (m_pqio->hFile, m_qiobuf.dwOffset, NULL, FILE_BEGIN);
  895. if (dwOff != m_qiobuf.dwOffset)
  896. {
  897. m_qiobuf.dwError = GetLastError();
  898. DPF("avifile32 non-aligned seek error %d", m_qiobuf.dwError);
  899. return FALSE;
  900. }
  901. else if ( ! ReadFile (m_pqio->hFile, m_qiobuf.lpv, m_qiobuf.cb,
  902. &m_qiobuf.cbDone, NULL) ||
  903. (m_qiobuf.cbDone != m_qiobuf.cb))
  904. {
  905. m_qiobuf.dwError = GetLastError ();
  906. DPF("avifile32 non-aligned read error %d", m_qiobuf.dwError);
  907. return FALSE;
  908. }
  909. m_State = Idle;
  910. }
  911. else
  912. return QioAdd (m_pqio, &m_qiobuf);
  913. #else
  914. ResetEvent(m_Overlapped.hEvent);
  915. m_State = Busy;
  916. //start from pos
  917. m_Overlapped.Offset = pos;
  918. m_Overlapped.OffsetHigh = 0;
  919. if (ReadFile(m_hFile, &m_pBuffer[offset], count,
  920. &nrRead, &m_Overlapped)) {
  921. m_State = Idle;
  922. DPF(("instant completion"));
  923. // if it completed already, then sort out the new position
  924. if (nrRead != count) {
  925. // rounding to sector size may have taken us past eof -
  926. // in this case we must still ask for the full sector, but
  927. // we will be told about the actual size
  928. if (m_Position + nrRead != m_FileLength) {
  929. DPF("ReadInto: actual wrong");
  930. return FALSE;
  931. }
  932. }
  933. } else {
  934. // should be pending
  935. if (GetLastError() != ERROR_IO_PENDING) {
  936. DPF("read failed %d\n", GetLastError());
  937. // no longer busy
  938. m_State = Idle;
  939. DPF("ReadInto failed %d", GetLastError());
  940. return FALSE;
  941. }
  942. }
  943. #endif
  944. return TRUE;
  945. }
  946. #endif // USE_DIRECTIO