//
// TransferFile.h
//
// Copyright (c) Shareaza Development Team, 2002-2005.
// This file is part of SHAREAZA (www.shareaza.com)
//
// Shareaza is free software; you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Shareaza is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Shareaza; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
#if !defined(AFX_TRANSFERFILE_H__FF7BC368_5878_4BCF_A2AD_055B0355AC3A__INCLUDED_)
#define AFX_TRANSFERFILE_H__FF7BC368_5878_4BCF_A2AD_055B0355AC3A__INCLUDED_
#pragma once
class CTransferFile;
/*
管理CTranferFiles对象和CTranferFiles对象对应的路径,
为了方便管理,将《路径,对象》保存到map中,然后为所有的
队列缓存写入的CTransferFile对象单独的建立一个链表CPtrList
*/
class CTransferFiles
{
// Construction
public:
CTransferFiles();
virtual ~CTransferFiles();
// Attributes
public:
//确保互斥访问当前的CTransferFiles对象
CCriticalSection m_pSection;
CMapStringToPtr m_pMap;
CPtrList m_pDeferred;
// Operations
public:
CTransferFile* Open(LPCTSTR pszFile,
BOOL bWrite, BOOL bCreate);
//注意关闭的时候仅仅是将m_pDeferred和m_pMap清空,释放
//这两个链表中元素所占用的空间,并没有调用延迟写入!!!!!
void Close();
//将m_pDeferred中所有的延迟写入对象写入文件
void CommitDeferred();
protected:
//将参数指定的CTransferFile对象加入到m_pDeferred中
void QueueDeferred(CTransferFile* pFile);
//从m_pDeferred中删除参数指定的CTransferFile对象
void Remove(CTransferFile* pFile);
friend class CTransferFile;
};
#define DEFERRED_MAX 8
class CTransferFile
{
// Construction
public:
CTransferFile(LPCTSTR pszPath);
virtual ~CTransferFile();
// Deferred Write Structure
protected:
//延迟写入结构,定义了偏移量,写入长度和数据缓冲区指针
class DefWrite
{
public:
QWORD m_nOffset;
DWORD m_nLength;
BYTE* m_pBuffer;
};
// Attributes
protected:
//当前CTransferFile对应的文件
CString m_sPath;
//对应的文件句柄
HANDLE m_hFile;
//当前文件是否可以写入
BOOL m_bWrite;
//当前文件被使用的记数
DWORD m_nReference;
protected:
//管理了一个延迟写入数组
DefWrite m_pDeferred[DEFERRED_MAX];
//当前延迟写入数组中的元素个数
int m_nDeferred;
// Operations
public:
//增加记数
void AddRef();
//延迟写入,减少引用计数
void Release(BOOL bWrite);
//延迟写入后,获得当前的文件句柄
HANDLE GetHandle(BOOL bWrite = FALSE);
//当前文件是否是打开的
BOOL IsOpen();
//先延迟写入,然后读取数据
BOOL Read(QWORD nOffset, LPVOID pBuffer,
QWORD nBuffer, QWORD* pnRead);
//如果写入偏移量大于20M,而且比当前文件长度还要大20M,则延迟写入
//否则立即写入
BOOL Write(QWORD nOffset, LPCVOID pBuffer,
QWORD nBuffer, QWORD* pnWritten);
protected:
//打开已经存在的文件或者创建新文件
BOOL Open(BOOL bWrite, BOOL bCreate);
//确保文件是可以写入的
BOOL EnsureWrite();
//先延迟写入,然后重新打开文件
BOOL CloseWrite();
//将DefWrite数组中的内容全部延迟写入
void DeferredWrite(BOOL bOffline = FALSE);
friend class CTransferFiles;
};
extern CTransferFiles TransferFiles;
#endif // !defined(AFX_TRANSFERFILE_H__FF7BC368_5878_4BCF_A2AD_055B0355AC3A__INCLUDED_)
//
// TransferFile.cpp
//
// Copyright (c) Shareaza Development Team, 2002-2005.
// This file is part of SHAREAZA (www.shareaza.com)
//
// Shareaza is free software; you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Shareaza is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Shareaza; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
#include "StdAfx.h"
#include "Shareaza.h"
#include "TransferFile.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
/*
外部调用的时候,直接调用TransferFiles.open()就可以了
*/
CTransferFiles TransferFiles;
//
// CTransferFiles construction
CTransferFiles::CTransferFiles()
{
}
CTransferFiles::~CTransferFiles()
{
Close();
}
//
// CTransferFiles open a file
//打开一个文件时,创建一个CTransferFile对象,引用计数增加1,
//将之加入到map中,没有加入到m_pDeferred中!!!!
CTransferFile* CTransferFiles::Open(LPCTSTR pszFile,
BOOL bWrite, BOOL bCreate)
{
CSingleLock pLock( &m_pSection, TRUE );
CTransferFile* pFile = NULL;
if ( m_pMap.Lookup( pszFile, (void*&)pFile ) )
{
//在原来的map中有,参数要求写而且当前文件不能写,则返回NULL
if ( bWrite && ! pFile->EnsureWrite() )
{
return NULL;
}
}
else // 原来的map中没有,根据参数路径创建一个新的CTransferFile对象
{
//打开参数路径所指定的文件,将这个pair加入到map中
pFile = new CTransferFile( pszFile );
if ( ! pFile->Open( bWrite, bCreate ) )
{
delete pFile;
return NULL;
}
m_pMap.SetAt( pFile->m_sPath, pFile );
}
//CTransferFile对象的引用记数增加1
pFile->AddRef();
//返回创建的CTransferFile对象
return pFile;
}
//
// CTransferFiles close all files
//删除m_pMap中所有的CTransferFile,删除m_pMap,然后删除所有的m_pDeferred
void CTransferFiles::Close()
{
CSingleLock pLock( &m_pSection, TRUE );
for ( POSITION pos = m_pMap.GetStartPosition() ; pos ; )
{
CTransferFile* pFile;
CString strPath;
m_pMap.GetNextAssoc( pos, strPath, (void*&)pFile );
//删除所有的CTransferFile对象
delete pFile;
}
m_pMap.RemoveAll();
m_pDeferred.RemoveAll();
}
//
// CTransferFiles commit deferred writes
//将m_pDeferred中所有的CTransferFile对象延迟写入,然后清空m_pDeferred链表
void CTransferFiles::CommitDeferred()
{
CSingleLock pLock( &m_pSection, TRUE );
for ( POSITION pos = m_pDeferred.GetHeadPosition() ; pos ; )
{
CTransferFile* pFile = (CTransferFile*)m_pDeferred.GetNext( pos );
pFile->DeferredWrite( TRUE );
}
m_pDeferred.RemoveAll();
}
//
// CTransferFiles queue for deferred write
//如果在m_pDeferred链表中没有找到pFile文件,则将之加入到m_pDeferred的最后
void CTransferFiles::QueueDeferred(CTransferFile* pFile)
{
if ( NULL == m_pDeferred.Find( pFile ) )
{
m_pDeferred.AddTail( pFile );
}
}
//
// CTransferFiles remove a single file
//在m_pMap和m_pDeferred中删除参数指定的CTransferFile对象
void CTransferFiles::Remove(CTransferFile* pFile)
{
m_pMap.RemoveKey( pFile->m_sPath );
if ( POSITION pos = m_pDeferred.Find( pFile ) )
{
m_pDeferred.RemoveAt( pos );
}
}
//
// CTransferFile construction
CTransferFile::CTransferFile(LPCTSTR pszPath)
{
m_sPath = pszPath;
m_hFile = INVALID_HANDLE_VALUE;
m_nReference = 0;
m_bWrite = FALSE;
m_nDeferred = 0;
}
//将当前TransferFile的内容写入文件m_hFile,然后关闭这个文件
CTransferFile::~CTransferFile()
{
if ( m_hFile != INVALID_HANDLE_VALUE )
{
DeferredWrite();
CloseHandle( m_hFile );
}
}
//
// CTransferFile reference counts
//将当前TransferFile的记数增加1
void CTransferFile::AddRef()
{
CSingleLock pLock( &TransferFiles.m_pSection, TRUE );
m_nReference++;
}
//将当前TransferFile的记数减少1
void CTransferFile::Release(BOOL bWrite)
{
CSingleLock pLock( &TransferFiles.m_pSection, TRUE );
if ( ! --m_nReference )
{
TransferFiles.Remove( this );
delete this;
return;
}
if ( m_bWrite && bWrite )
{
//如果可以写而且参数规定了要写,则先延迟写入,然后再打开
CloseWrite();
}
}
//
// CTransferFile handle
//获得文件句柄,这里如果当前文件是不可写的,而参数为要写入,则返回false
//如果文件是可写的,先延迟写入,然后返回文件句柄m_hFile
HANDLE CTransferFile::GetHandle(BOOL bWrite)
{
CSingleLock pLock( &TransferFiles.m_pSection, TRUE );
if ( bWrite && ! m_bWrite )
{
return INVALID_HANDLE_VALUE;
}
if ( m_nDeferred > 0 )
{
DeferredWrite();
}
return m_hFile;
}
//m_hFile句柄有效说明当前的CTransferFile是打开的
BOOL CTransferFile::IsOpen()
{
return m_hFile != INVALID_HANDLE_VALUE;
}
//
// CTransferFile open
//打开m_sPath指定的文件,参数确定了打开的方式
BOOL CTransferFile::Open(BOOL bWrite, BOOL bCreate)
{
if ( m_hFile != INVALID_HANDLE_VALUE )
{
return FALSE;
}
DWORD dwDesiredAccess = GENERIC_READ;
if ( bWrite )
{
dwDesiredAccess |= GENERIC_WRITE;
}
DWORD dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE;
if ( theApp.m_bNT )
{
dwShare |= FILE_SHARE_DELETE;
}
DWORD dwCreation = bCreate ? CREATE_ALWAYS : OPEN_EXISTING;
#if 1
m_hFile = CreateFile( m_sPath, dwDesiredAccess, dwShare,
NULL, dwCreation, FILE_ATTRIBUTE_NORMAL, NULL );
#else
// Testing
m_hFile = CreateFile( _T("C://Junk//Incomplete.bin"), dwDesiredAccess,
dwShare, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
#endif
if ( m_hFile != INVALID_HANDLE_VALUE )
{
m_bWrite = bWrite;
}
return m_hFile != INVALID_HANDLE_VALUE;
}
//
// CTransferFile write access management
//首先关闭m_hFile句柄,然后重新打开,这里是打开已经存在的文件
//所以是open(true, false ),这个方法是确保文件是可以写的
BOOL CTransferFile::EnsureWrite()
{
if ( m_hFile == INVALID_HANDLE_VALUE )
{
return FALSE;
}
if ( m_bWrite )
{
return TRUE;
}
CloseHandle( m_hFile );
m_hFile = INVALID_HANDLE_VALUE;
if ( Open( TRUE, FALSE ) )
{
return TRUE;
}
Open( FALSE, FALSE );
return FALSE;
}
//调用DeferredWrite完成写入,然后关闭文件句柄,再打开这个文件
BOOL CTransferFile::CloseWrite()
{
if ( m_hFile == INVALID_HANDLE_VALUE )
{
return FALSE;
}
if ( ! m_bWrite )
{
return TRUE;
}
DeferredWrite();
CloseHandle( m_hFile );
m_hFile = INVALID_HANDLE_VALUE;
return Open( FALSE, FALSE );
}
//
// CTransferFile read
//如果需要延迟写,则写入,然后读取数据
BOOL CTransferFile::Read(QWORD nOffset, LPVOID pBuffer,
QWORD nBuffer, QWORD* pnRead)
{
CSingleLock pLock( &TransferFiles.m_pSection, TRUE );
*pnRead = 0;
if ( m_hFile == INVALID_HANDLE_VALUE )
{
return FALSE;
}
if ( m_nDeferred > 0 ) //m_nDeferred > 0表示需要延迟写,则写入
{
DeferredWrite();
}
//从m_hFile文件的 nOffset位置读取pnRead个字符到参数pBuffer中
DWORD nOffsetLow = (DWORD)( nOffset & 0x00000000FFFFFFFF );
DWORD nOffsetHigh = (DWORD)( ( nOffset & 0xFFFFFFFF00000000 ) >> 32 );
SetFilePointer( m_hFile, nOffsetLow, (PLONG)&nOffsetHigh, FILE_BEGIN );
return ReadFile( m_hFile, pBuffer, (DWORD)nBuffer, (DWORD*)pnRead, NULL );
}
//
// CTransferFile write (with deferred extension)
/*
在nOffset位置写入数据pBuffer,长度为nBuffer。
如果偏移位置nOffset比当前文件的大小还要大,而且差距超过了
20M,则将这个写的任务使用队列缓存到TransferFiles中,以后延迟写入。
其他情况将文件指针直接定位到nOffset位置,然后写入
pBuffer数据
*/
#define DEFERRED_THRESHOLD (20*1024*1024)
BOOL CTransferFile::Write( QWORD nOffset, LPCVOID pBuffer,
QWORD nBuffer, QWORD* pnWritten )
{
CSingleLock pLock( &TransferFiles.m_pSection, TRUE );
*pnWritten = 0;
if ( m_hFile == INVALID_HANDLE_VALUE )
{
return FALSE;
}
if ( ! m_bWrite )
{
return FALSE;
}
if ( nOffset > DEFERRED_THRESHOLD )
{
DWORD nSizeHigh = 0;
QWORD nSize = (QWORD)GetFileSize( m_hFile, &nSizeHigh );
nSize |= ( (QWORD)nSizeHigh << 32 );
//如果要写入的位置比当前文件的大小还要大,而且间隔超过了20M
//则将之缓存起来,并不写入
if ( nOffset > nSize
&& nOffset - nSize > DEFERRED_THRESHOLD )
{
//首先将当前的TransferFile对象加入到队列中缓存起来先
TransferFiles.QueueDeferred( this );
//如果缓存的数目超过8了,则首先将缓存的全部写入
if ( m_nDeferred >= DEFERRED_MAX )
{
DeferredWrite();
}
//创建一个新的DefWrite对象,加入到m_pDeferred中
DefWrite* pWrite = &m_pDeferred[ m_nDeferred++ ];
pWrite->m_nOffset = nOffset;
pWrite->m_nLength = (DWORD)nBuffer;
pWrite->m_pBuffer = new BYTE[ (DWORD)nBuffer ];
//将参数pBuffer的内容拷贝到新创建的DefWrite对象的buffer中
CopyMemory( pWrite->m_pBuffer, pBuffer, (DWORD)nBuffer );
*pnWritten = nBuffer;
theApp.Message( MSG_TEMP,
_T("Deferred write of %I64i bytes at %I64i"),
nBuffer, nOffset );
return TRUE;
}
}
//如果偏移量小于20M,则直接将指针移动到nOffset位置,然后写入数据
DWORD nOffsetLow = (DWORD)( nOffset & 0x00000000FFFFFFFF );
DWORD nOffsetHigh = (DWORD)( ( nOffset & 0xFFFFFFFF00000000 ) >> 32 );
SetFilePointer( m_hFile, nOffsetLow, (PLONG)&nOffsetHigh, FILE_BEGIN );
return WriteFile( m_hFile, pBuffer, (DWORD)nBuffer, (LPDWORD)pnWritten, NULL );
}
//
// CTransferFile deferred writes
//延迟写
/*
将m_pDeferred链表中的所有的DefWrite指针的buffer的内容写入到文件
m_hFile中,每个DefWrite定义了要写入数据的开始位置,写入长度
和数据缓冲区,m_pDeferred保存了好多个DefWrite对象
*/
void CTransferFile::DeferredWrite(BOOL bOffline)
{
if ( m_nDeferred == 0 )
{
return;
}
if ( m_hFile == INVALID_HANDLE_VALUE )
{
return;
}
if ( ! m_bWrite )
{
return;
}
DefWrite* pWrite = m_pDeferred;
//pWrite->m_nOffset 指明了要写入的起始位置,pWrite->m_nLength指定了写入的长度
for ( int nDeferred = 0 ; nDeferred < m_nDeferred ; nDeferred++, pWrite++ )
{
theApp.Message( MSG_TEMP, _T("Committing deferred write of %lu bytes at %I64i"),
pWrite->m_nLength, pWrite->m_nOffset );
//将指针移动到pWrite->m_nOffset位置,然后写入pWrite->m_nLength个字节的数据
//为什么要拆成两半,是因为SetFilePointer函数需要用到高位字节
DWORD nOffsetLow = (DWORD)( pWrite->m_nOffset & 0x00000000FFFFFFFF );
DWORD nOffsetHigh = (DWORD)( ( pWrite->m_nOffset & 0xFFFFFFFF00000000 ) >> 32 );
SetFilePointer( m_hFile, nOffsetLow, (PLONG)&nOffsetHigh, FILE_BEGIN );
DWORD nWritten = 0;
//将m_pBuffer写入文件
WriteFile( m_hFile, pWrite->m_pBuffer, pWrite->m_nLength, &nWritten, NULL );
delete [] pWrite->m_pBuffer;
}
m_nDeferred = 0;
theApp.Message( MSG_TEMP, _T("Commit finished") );
}