2021SC@SDUSC
上次讲解了SQLITE_OS_WINCE部分。接来下一组例程将实现sqlite3_io_method对象指定的I/O方法。
/*有些微软编译器缺少这个定义*/
#ifndef INVALID_SET_FILE_POINTER
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif
移动作为第一个参数传递的文件句柄的当前位置,以在文件中偏移iOffset。如果成功,则返回0。否则,设置pfile->lastErrno并返回非零。
static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
#if !SQLITE_OS_WINRT
LONG upperBits; /*采用32位偏移量*/
LONG lowerBits; /*采用32位偏移量*/
DWORD dwRet; /*由SetFilePoter()返回值*/
DWORD lastErrno; /*由 GetLastError() 返回值*/
OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset));
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
API奇异点:如果成功,SetFilePoter()将返回一个dword,其中包含新文件偏移量的较低的32位。或者,如果失败,它返回INVALID_SET_FILE_POINTER。但是,根据MSDN,INVALID_SET_FILE_POINTER也可能是一个有效的新偏移量。因此,要确定是否实际发生了错误,还需要调用GetLastError()。
dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
if( (dwRet==INVALID_SET_FILE_POINTER
&& ((lastErrno = osGetLastError())!=NO_ERROR)) ){
pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
"winSeekFile", pFile->zPath);
OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
return 1;
}
OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
return 0;
#else
/* 跟上面一样,但这个实现只适用于WinRT*/
LARGE_INTEGER x; /* 新的偏移量 */
BOOL bRet; /* 由SetFilePointerEx() 返回值*/
x.QuadPart = iOffset;
bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);
if(!bRet){
pFile->lastErrno = osGetLastError();
winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
"winSeekFile", pFile->zPath);
OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
return 1;
}
OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
return 0;
#endif
#if SQLITE_MAX_MMAP_SIZE>0
/*转发用于内存映射文件的VFS助手方法的引用*/
static int winMapfile(winFile*, sqlite3_int64);
static int winUnmapfile(winFile*);
#endif
将数据从文件中读取到缓冲区中。如果所有字节都已成功读取,则返回SQLITE_OK;如果出现任何错误,则返回SQLITE_IOERR。
static int winRead(
sqlite3_file *id, /* 要读取的文件 */
void *pBuf, /* 将内容写入此缓冲区 */
int amt, /* 要读取的字节数*/
sqlite3_int64 offset /*开始阅读此偏移量 */
){
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
OVERLAPPED overlapped; /* ReadFile的偏移量 */
#endif
winFile *pFile = (winFile*)id; /*文件句柄 */
DWORD nRead; /* 实际从文件读取的字节数*/
int nRetry = 0; /* 重试次数 */
assert( id!=0 );
assert( amt>0 );
assert( offset>=0 );
SimulateIOError(return SQLITE_IOERR_READ);
OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, "
"offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
pFile->h, pBuf, amt, offset, pFile->locktype));
#if SQLITE_MAX_MMAP_SIZE>0
/* 通过使用memcpy()从内存映射中传输数据来处理尽可能多的读取请求 */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt);
OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
osGetCurrentProcessId(), pFile, pFile->h));
return SQLITE_OK;
}else{
int nCopy = (int)(pFile->mmapSize - offset);
memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy);
pBuf = &((u8 *)pBuf)[nCopy];
amt -= nCopy;
offset += nCopy;
}
}
#endif
将数据从缓冲区写入文件。在成功时返回SQLITE_OK或在失败时返回其他错误代码。(参照上面将数据从文件读取到缓冲区)
static int winWrite(
sqlite3_file *id, /* 要读取的文件 */
const void *pBuf, /* 要写入的字节 */
int amt, /* 要读取的字节数*/
sqlite3_int64 offset /*开始阅读此偏移量 */
){
int rc = 0; /* 如果发生错误则为True,否则为False*/
winFile *pFile = (winFile*)id; /* 文件句柄 */
int nRetry = 0; /* 重试次数 */
assert( amt>0 );
assert( pFile );
SimulateIOError(return SQLITE_IOERR_WRITE);
SimulateDiskfullError(return SQLITE_FULL);
OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, "
"offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
pFile->h, pBuf, amt, offset, pFile->locktype));
将打开的文件截断到指定的大小。
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
winFile *pFile = (winFile*)id; /*文件句柄对象 */
int rc = SQLITE_OK; /* 此函数的返回代码 */
DWORD lastErrno;
#if SQLITE_MAX_MMAP_SIZE>0
sqlite3_int64 oldMmapSize;
if( pFile->nFetchOut>0 ){
return SQLITE_OK; ①
}
#endif
assert( pFile );
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n",
osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype));
①:如果有未完成的内存映射页,则文件截断是无操作的。这是因为截断文件意味着暂时取消映射文件,这可能会从现有游标下删除内存,可能导致增量真空,而不是截断文件,唯一可行的工作是将截断推迟到所有对内存映射内容的引用都关闭之后。这是可行的,因此,现在决定,只要有挂起的读操作,就把截断文件变成不操作。
/*SetEndOfFile()在成功时返回非零,在失败时返回零。*/
if( winSeekFile(pFile, nByte) ){
rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
"winTruncate1", pFile->zPath);
}else if( 0==osSetEndOfFile(pFile->h) &&
((lastErrno = osGetLastError())!=ERROR_USER_MAPPED_FILE) ){
pFile->lastErrno = lastErrno;
rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
"winTruncate2", pFile->zPath);
}