Example #1
0
BOOL My_ReadFileScatter()
{
    HANDLE hFile=NULL;
    FILE_SEGMENT_ELEMENT aSegmentArray[50];
    DWORD nNumberOfBytesToRead=NULL;
    LPDWORD lpReserved=NULL;
    LPOVERLAPPED lpOverlapped=NULL;
    BOOL returnVal_Real = NULL;
    BOOL returnVal_Intercepted = NULL;

    DWORD error_Real = 0;
    DWORD error_Intercepted = 0;
    disableInterception();
    returnVal_Real = ReadFileScatter (hFile,aSegmentArray,nNumberOfBytesToRead,lpReserved,lpOverlapped);
    error_Real = GetLastError();
    enableInterception();
    returnVal_Intercepted = ReadFileScatter (hFile,aSegmentArray,nNumberOfBytesToRead,lpReserved,lpOverlapped);
    error_Intercepted = GetLastError();
    return ((returnVal_Real == returnVal_Intercepted) && (error_Real == error_Intercepted));
}
size_type file::readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec)
{
    TORRENT_ASSERT((m_open_mode & rw_mask) == read_only || (m_open_mode & rw_mask) == read_write);
    TORRENT_ASSERT(bufs);
    TORRENT_ASSERT(num_bufs > 0);
    TORRENT_ASSERT(is_open());

#if defined TORRENT_WINDOWS || defined TORRENT_LINUX || defined TORRENT_DEBUG
    // make sure m_page_size is initialized
    init_file();
#endif

#ifdef TORRENT_DEBUG
    if (m_open_mode & no_buffer)
    {
        bool eof = false;
        int size = 0;
        // when opened in no_buffer mode, the file_offset must
        // be aligned to pos_alignment()
        TORRENT_ASSERT((file_offset & (pos_alignment()-1)) == 0);
        for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
        {
            TORRENT_ASSERT((uintptr_t(i->iov_base) & (buf_alignment()-1)) == 0);
            // every buffer must be a multiple of the page size
            // except for the last one
            TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1);
            if ((i->iov_len & (size_alignment()-1)) != 0) eof = true;
            size += i->iov_len;
        }
        error_code code;
        if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code));
    }
#endif

#ifdef TORRENT_WINDOWS

    DWORD ret = 0;

    // since the ReadFileScatter requires the file to be opened
    // with no buffering, and no buffering requires page aligned
    // buffers, open the file in non-buffered mode in case the
    // buffer is not aligned. Most of the times the buffer should
    // be aligned though

    if ((m_open_mode & no_buffer) == 0)
    {
        // this means the buffer base or the buffer size is not aligned
        // to the page size. Use a regular file for this operation.

        LARGE_INTEGER offs;
        offs.QuadPart = file_offset;
        if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE)
        {
            ec = error_code(GetLastError(), get_system_category());
            return -1;
        }

        for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
        {
            DWORD intermediate = 0;
            if (ReadFile(m_file_handle, (char*)i->iov_base
                         , (DWORD)i->iov_len, &intermediate, 0) == FALSE)
            {
                ec = error_code(GetLastError(), get_system_category());
                return -1;
            }
            ret += intermediate;
        }
        return ret;
    }

    int size = bufs_size(bufs, num_bufs);
    // number of pages for the read. round up
    int num_pages = (size + m_page_size - 1) / m_page_size;
    // allocate array of FILE_SEGMENT_ELEMENT for ReadFileScatter
    FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1);
    FILE_SEGMENT_ELEMENT* cur_seg = segment_array;

    for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
    {
        for (int k = 0; k < i->iov_len; k += m_page_size)
        {
            cur_seg->Buffer = PtrToPtr64((((char*)i->iov_base) + k));
            ++cur_seg;
        }
    }
    // terminate the array
    cur_seg->Buffer = 0;

    OVERLAPPED ol;
    ol.Internal = 0;
    ol.InternalHigh = 0;
    ol.OffsetHigh = file_offset >> 32;
    ol.Offset = file_offset & 0xffffffff;
    ol.hEvent = CreateEvent(0, true, false, 0);

    ret += size;
    size = num_pages * m_page_size;
    if (ReadFileScatter(m_file_handle, segment_array, size, 0, &ol) == 0)
    {
        DWORD last_error = GetLastError();
        if (last_error != ERROR_IO_PENDING)
        {
            TORRENT_ASSERT(GetLastError() != ERROR_BAD_ARGUMENTS);
            ec = error_code(GetLastError(), get_system_category());
            CloseHandle(ol.hEvent);
            return -1;
        }
        if (GetOverlappedResult(m_file_handle, &ol, &ret, true) == 0)
        {
            ec = error_code(GetLastError(), get_system_category());
            CloseHandle(ol.hEvent);
            return -1;
        }
    }
    CloseHandle(ol.hEvent);
    return ret;

#else // TORRENT_WINDOWS

    size_type ret = lseek(m_fd, file_offset, SEEK_SET);
    if (ret < 0)
    {
        ec = error_code(errno, get_posix_category());
        return -1;
    }
#if TORRENT_USE_READV

#ifdef TORRENT_LINUX
    bool aligned = false;
    int size = 0;
    // if we're not opened in no-buffer mode, we don't need alignment
    if ((m_open_mode & no_buffer) == 0) aligned = true;
    if (!aligned)
    {
        size = bufs_size(bufs, num_bufs);
        if ((size & (size_alignment()-1)) == 0) aligned = true;
    }
    if (aligned)
#endif // TORRENT_LINUX
    {
        ret = ::readv(m_fd, bufs, num_bufs);
        if (ret < 0)
        {
            ec = error_code(errno, get_posix_category());
            return -1;
        }
        return ret;
    }
#ifdef TORRENT_LINUX
    file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
    memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * num_bufs);
    iovec_t& last = temp_bufs[num_bufs-1];
    last.iov_len = (last.iov_len & ~(size_alignment()-1)) + m_page_size;
    ret = ::readv(m_fd, temp_bufs, num_bufs);
    if (ret < 0)
    {
        ec = error_code(errno, get_posix_category());
        return -1;
    }
    return (std::min)(ret, size_type(size));
#endif // TORRENT_LINUX

#else // TORRENT_USE_READV

    ret = 0;
    for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
    {
        int tmp = read(m_fd, i->iov_base, i->iov_len);
        if (tmp < 0)
        {
            ec = error_code(errno, get_posix_category());
            return -1;
        }
        ret += tmp;
        if (tmp < i->iov_len) break;
    }
    return ret;

#endif // TORRENT_USE_READV

#endif // TORRENT_WINDOWS
}