/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <systools/win32/uwinapi.h>
 
#include <osl/file.hxx>
#include <rtl/alloc.h>
#include <rtl/byteseq.h>
#include <sal/log.hxx>
#include <o3tl/char16_t2wchar_t.hxx>
 
#include "file_url.hxx"
#include "file_error.hxx"
 
#include <cassert>
#include <algorithm>
#include <limits>
 
#ifdef max /* conflict w/ std::numeric_limits<T>::max() */
#undef max
#endif
#ifdef min
#undef min
#endif
 
/** File handle implementation.
*/
struct FileHandle_Impl
{
    CRITICAL_SECTION m_mutex;
    HANDLE           m_hFile;
 
    /** State
     */
    enum StateBits
    {
        STATE_SEEKABLE  = 1, /*< open() sets, iff regular file */
        STATE_READABLE  = 2, /*< open() sets, read() requires */
        STATE_WRITEABLE = 4, /*< open() sets, write() requires */
        STATE_MODIFIED  = 8  /* write() sets, flush() resets */
    };
    int           m_state;
 
    sal_uInt64    m_size;    /*< file size */
    LONGLONG      m_offset;  /*< physical offset from begin of file */
    LONGLONG      m_filepos; /*< logical offset from begin of file */
 
    LONGLONG      m_bufptr;  /*< buffer offset from begin of file */
    SIZE_T        m_buflen;  /*< buffer filled [0, m_bufsiz - 1] */
 
    SIZE_T        m_bufsiz;
    sal_uInt8 *   m_buffer;
 
    explicit      FileHandle_Impl (HANDLE hFile);
                  ~FileHandle_Impl();
 
    static void*  operator new(size_t n);
    static void   operator delete(void * p, size_t);
    static SIZE_T getpagesize();
 
    sal_uInt64    getPos() const;
    oslFileError  setPos (sal_uInt64 uPos);
 
    sal_uInt64    getSize() const;
    oslFileError  setSize (sal_uInt64 uPos);
 
    oslFileError  readAt(
        LONGLONG     nOffset,
        void *       pBuffer,
        DWORD        nBytesRequested,
        sal_uInt64 * pBytesRead);
 
    oslFileError  writeAt(
        LONGLONG     nOffset,
        void const * pBuffer,
        DWORD        nBytesToWrite,
        sal_uInt64 * pBytesWritten);
 
    oslFileError  readFileAt(
        LONGLONG     nOffset,
        void *       pBuffer,
        sal_uInt64   uBytesRequested,
        sal_uInt64 * pBytesRead);
 
    oslFileError  writeFileAt(
        LONGLONG     nOffset,
        void const * pBuffer,
        sal_uInt64   uBytesToWrite,
        sal_uInt64 * pBytesWritten);
 
    oslFileError  readLineAt(
        LONGLONG        nOffset,
        sal_Sequence ** ppSequence,
        sal_uInt64 *    pBytesRead);
 
    static oslFileError writeSequence_Impl (
        sal_Sequence ** ppSequence,
        SIZE_T *        pnOffset,
        const void *    pBuffer,
        SIZE_T          nBytes);
 
    oslFileError syncFile();
 
    /** Guard.
     */
    class Guard
    {
        LPCRITICAL_SECTION m_mutex;
 
    public:
        explicit Guard(LPCRITICAL_SECTION pMutex);
        ~Guard();
    };
};
 
FileHandle_Impl::Guard::Guard(LPCRITICAL_SECTION pMutex)
    : m_mutex (pMutex)
{
    assert(pMutex);
    ::EnterCriticalSection (m_mutex);
}
 
FileHandle_Impl::Guard::~Guard()
{
    ::LeaveCriticalSection (m_mutex);
}
 
FileHandle_Impl::FileHandle_Impl(HANDLE hFile)
    : m_hFile   (hFile),
      m_state   (STATE_READABLE | STATE_WRITEABLE),
      m_size    (0),
      m_offset  (0),
      m_filepos (0),
      m_bufptr  (-1),
      m_buflen  (0),
      m_bufsiz  (getpagesize()),
      m_buffer  (nullptr)
{
    ::InitializeCriticalSection (&m_mutex);
    m_buffer = static_cast<sal_uInt8 *>(malloc(m_bufsiz));
    if (m_buffer)
        memset (m_buffer, 0, m_bufsiz);
}
 
FileHandle_Impl::~FileHandle_Impl()
{
    free(m_buffer);
    m_buffer = nullptr;
    ::DeleteCriticalSection (&m_mutex);
}
 
void * FileHandle_Impl::operator new(size_t n)
{
    return malloc(n);
}
 
void FileHandle_Impl::operator delete(void * p, size_t)
{
    free(p);
}
 
SIZE_T FileHandle_Impl::getpagesize()
{
    SYSTEM_INFO info;
    ::GetSystemInfo(&info);
    return sal::static_int_cast< SIZE_T >(info.dwPageSize);
}
 
sal_uInt64 FileHandle_Impl::getPos() const
{
    return sal::static_int_cast< sal_uInt64 >(m_filepos);
}
 
oslFileError FileHandle_Impl::setPos(sal_uInt64 uPos)
{
    m_filepos = sal::static_int_cast< LONGLONG >(uPos);
    return osl_File_E_None;
}
 
sal_uInt64 FileHandle_Impl::getSize() const
{
    LONGLONG bufend = std::max(LONGLONG(0), m_bufptr) + m_buflen;
    return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend));
}
 
oslFileError FileHandle_Impl::setSize(sal_uInt64 uSize)
{
    LARGE_INTEGER nDstPos; nDstPos.QuadPart = sal::static_int_cast< LONGLONG >(uSize);
    if (!::SetFilePointerEx(m_hFile, nDstPos, nullptr, FILE_BEGIN))
        return oslTranslateFileError(GetLastError());
 
    if (!::SetEndOfFile(m_hFile))
        return oslTranslateFileError(GetLastError());
    m_size = uSize;
 
    nDstPos.QuadPart = m_offset;
    if (!::SetFilePointerEx(m_hFile, nDstPos, nullptr, FILE_BEGIN))
        return oslTranslateFileError(GetLastError());
 
    return osl_File_E_None;
}
 
oslFileError FileHandle_Impl::readAt(
    LONGLONG     nOffset,
    void *       pBuffer,
    DWORD        nBytesRequested,
    sal_uInt64 * pBytesRead)
{
    SAL_WARN_IF(!(m_state & STATE_SEEKABLE), "sal.osl", "FileHandle_Impl::readAt(): not seekable");
    if (!(m_state & STATE_SEEKABLE))
        return osl_File_E_SPIPE;
 
    SAL_WARN_IF(!(m_state & STATE_READABLE), "sal.osl", "FileHandle_Impl::readAt(): not readable");
    if (!(m_state & STATE_READABLE))
        return osl_File_E_BADF;
 
    if (nOffset != m_offset)
    {
        LARGE_INTEGER liOffset; liOffset.QuadPart = nOffset;
        if (!::SetFilePointerEx(m_hFile, liOffset, nullptr, FILE_BEGIN))
            return oslTranslateFileError(GetLastError());
        m_offset = nOffset;
    }
 
    DWORD dwDone = 0;
    if (!::ReadFile(m_hFile, pBuffer, nBytesRequested, &dwDone, nullptr))
        return oslTranslateFileError(GetLastError());
    m_offset += dwDone;
 
    *pBytesRead = dwDone;
    return osl_File_E_None;
}
 
oslFileError FileHandle_Impl::writeAt(
    LONGLONG     nOffset,
    void const * pBuffer,
    DWORD        nBytesToWrite,
    sal_uInt64 * pBytesWritten)
{
    SAL_WARN_IF(!(m_state & STATE_SEEKABLE), "sal.osl", "FileHandle_Impl::writeAt(): not seekable");
    if (!(m_state & STATE_SEEKABLE))
        return osl_File_E_SPIPE;
 
    SAL_WARN_IF(!(m_state & STATE_WRITEABLE), "sal.osl", "FileHandle_Impl::writeAt(): not writeable");
    if (!(m_state & STATE_WRITEABLE))
        return osl_File_E_BADF;
 
    if (nOffset != m_offset)
    {
        LARGE_INTEGER liOffset; liOffset.QuadPart = nOffset;
        if (!::SetFilePointerEx (m_hFile, liOffset, nullptr, FILE_BEGIN))
            return oslTranslateFileError(GetLastError());
        m_offset = nOffset;
    }
 
    DWORD dwDone = 0;
    if (!::WriteFile(m_hFile, pBuffer, nBytesToWrite, &dwDone, nullptr))
        return oslTranslateFileError(GetLastError());
    m_offset += dwDone;
 
    m_size = std::max(m_size, sal::static_int_cast< sal_uInt64 >(m_offset));
 
    *pBytesWritten = dwDone;
    return osl_File_E_None;
}
 
oslFileError FileHandle_Impl::readFileAt(
    LONGLONG     nOffset,
    void *       pBuffer,
    sal_uInt64   uBytesRequested,
    sal_uInt64 * pBytesRead)
{
    static sal_uInt64 const g_limit_dword = std::numeric_limits< DWORD >::max();
    if (g_limit_dword < uBytesRequested)
        return osl_File_E_OVERFLOW;
    DWORD nBytesRequested = sal::static_int_cast< DWORD >(uBytesRequested);
 
    if ((m_state & STATE_SEEKABLE) == 0)
    {
        // not seekable (pipe)
        DWORD dwDone = 0;
        if (!::ReadFile(m_hFile, pBuffer, nBytesRequested, &dwDone, nullptr))
            return oslTranslateFileError(GetLastError());
        *pBytesRead = dwDone;
        return osl_File_E_None;
    }
    else if (!m_buffer)
    {
        // not buffered
        return readAt (nOffset, pBuffer, nBytesRequested, pBytesRead);
    }
    else
    {
        sal_uInt8 * buffer = static_cast< sal_uInt8* >(pBuffer);
        for (*pBytesRead = 0; nBytesRequested > 0;)
        {
            LONGLONG const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
            SIZE_T   const bufpos = (nOffset % m_bufsiz);
 
            if (bufptr != m_bufptr)
            {
                // flush current buffer
                oslFileError result = syncFile();
                if (result != osl_File_E_None)
                    return result;
                m_bufptr = -1;
                m_buflen = 0;
 
                if (nBytesRequested >= m_bufsiz)
                {
                    // buffer too small, read through from file
                    sal_uInt64 uDone = 0;
                    result = readAt (nOffset, &(buffer[*pBytesRead]), nBytesRequested, &uDone);
                    if (result != osl_File_E_None)
                        return result;
 
                    nBytesRequested -= sal::static_int_cast< DWORD >(uDone);
                    *pBytesRead += uDone;
                    return osl_File_E_None;
                }
 
                // update buffer (pointer)
                sal_uInt64 uDone = 0;
                result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
                if (result != osl_File_E_None)
                    return result;
                m_bufptr = bufptr;
                m_buflen = sal::static_int_cast< SIZE_T >(uDone);
            }
            if (bufpos >= m_buflen)
            {
                // end of file
                return osl_File_E_None;
            }
 
            SIZE_T const bytes = std::min(m_buflen - bufpos, static_cast<SIZE_T>(nBytesRequested));
            memcpy(&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes);
            nBytesRequested -= bytes;
            *pBytesRead += bytes;
            nOffset += bytes;
        }
        return osl_File_E_None;
    }
}
 
oslFileError FileHandle_Impl::writeFileAt(
    LONGLONG     nOffset,
    void const * pBuffer,
    sal_uInt64   uBytesToWrite,
    sal_uInt64 * pBytesWritten)
{
    static sal_uInt64 const g_limit_dword = std::numeric_limits< DWORD >::max();
    if (g_limit_dword < uBytesToWrite)
        return osl_File_E_OVERFLOW;
    DWORD nBytesToWrite = sal::static_int_cast< DWORD >(uBytesToWrite);
 
    if ((m_state & STATE_SEEKABLE) == 0)
    {
        // not seekable (pipe)
        DWORD dwDone = 0;
        if (!::WriteFile(m_hFile, pBuffer, nBytesToWrite, &dwDone, nullptr))
            return oslTranslateFileError(GetLastError());
        *pBytesWritten = dwDone;
        return osl_File_E_None;
    }
    else if (!m_buffer)
    {
        // not buffered
        return writeAt(nOffset, pBuffer, nBytesToWrite, pBytesWritten);
    }
    else
    {
        sal_uInt8 const * buffer = static_cast< sal_uInt8 const* >(pBuffer);
        for (*pBytesWritten = 0; nBytesToWrite > 0;)
        {
            LONGLONG const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
            SIZE_T   const bufpos = (nOffset % m_bufsiz);
            if (bufptr != m_bufptr)
            {
                // flush current buffer
                oslFileError result = syncFile();
                if (result != osl_File_E_None)
                    return result;
                m_bufptr = -1;
                m_buflen = 0;
 
                if (nBytesToWrite >= m_bufsiz)
                {
                    // buffer too small, write through to file
                    sal_uInt64 uDone = 0;
                    result = writeAt(nOffset, &(buffer[*pBytesWritten]), nBytesToWrite, &uDone);
                    if (result != osl_File_E_None)
                        return result;
                    if (uDone != nBytesToWrite)
                        return osl_File_E_IO;
 
                    nBytesToWrite -= sal::static_int_cast< DWORD >(uDone);
                    *pBytesWritten += uDone;
                    return osl_File_E_None;
                }
 
                // update buffer (pointer)
                sal_uInt64 uDone = 0;
                result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
                if (result != osl_File_E_None)
                    return result;
                m_bufptr = bufptr;
                m_buflen = sal::static_int_cast< SIZE_T >(uDone);
            }
 
            SIZE_T const bytes = std::min(m_bufsiz - bufpos, static_cast<SIZE_T>(nBytesToWrite));
            memcpy(&(m_buffer[bufpos]), &(buffer[*pBytesWritten]), bytes);
            nBytesToWrite -= bytes;
            *pBytesWritten += bytes;
            nOffset += bytes;
 
            m_buflen = std::max(m_buflen, bufpos + bytes);
            m_state |= STATE_MODIFIED;
        }
        return osl_File_E_None;
    }
}
 
oslFileError FileHandle_Impl::readLineAt(
    LONGLONG        nOffset,
    sal_Sequence ** ppSequence,
    sal_uInt64 *    pBytesRead)
{
    oslFileError result = osl_File_E_None;
 
    LONGLONG bufptr = (nOffset / m_bufsiz) * m_bufsiz;
    if (bufptr != m_bufptr)
    {
        /* flush current buffer */
        result = syncFile();
        if (result != osl_File_E_None)
            return result;
 
        /* update buffer (pointer) */
        sal_uInt64 uDone = 0;
        result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
        if (result != osl_File_E_None)
            return result;
 
        m_bufptr = bufptr;
        m_buflen = sal::static_int_cast< SIZE_T >(uDone);
    }
 
    static int const LINE_STATE_BEGIN = 0;
    static int const LINE_STATE_CR    = 1;
    static int const LINE_STATE_LF    = 2;
 
    SIZE_T bufpos = sal::static_int_cast< SIZE_T >(nOffset - m_bufptr), curpos = bufpos, dstpos = 0;
    int    state  = (bufpos >= m_buflen) ? LINE_STATE_LF : LINE_STATE_BEGIN;
 
    for (; state != LINE_STATE_LF;)
    {
        if (curpos >= m_buflen)
        {
            /* buffer examined */
            if ((curpos - bufpos) > 0)
            {
                /* flush buffer to sequence */
                result = writeSequence_Impl(
                    ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos);
                if (result != osl_File_E_None)
                    return result;
                *pBytesRead += curpos - bufpos;
                nOffset += curpos - bufpos;
            }
 
            bufptr = nOffset / m_bufsiz * m_bufsiz;
            if (bufptr != m_bufptr)
            {
                /* update buffer (pointer) */
                sal_uInt64 uDone = 0;
                result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
                if (result != osl_File_E_None)
                    return result;
                m_bufptr = bufptr;
                m_buflen = sal::static_int_cast< SIZE_T >(uDone);
            }
 
            bufpos = sal::static_int_cast< SIZE_T >(nOffset - m_bufptr);
            curpos = bufpos;
            if (bufpos >= m_buflen)
                break;
        }
        switch (state)
        {
        case LINE_STATE_CR:
            state = LINE_STATE_LF;
            switch (m_buffer[curpos])
            {
            case 0x0A: /* CRLF */
                /* eat current char */
                curpos++;
                break;
            default: /* single CR */
                /* keep current char */
                break;
            }
            break;
        default:
            /* determine next state */
            switch (m_buffer[curpos])
            {
            case 0x0A: /* single LF */
                state = LINE_STATE_LF;
                break;
            case 0x0D: /* CR */
                state = LINE_STATE_CR;
                break;
            default: /* advance to next char */
                curpos++;
                break;
            }
            if (state != LINE_STATE_BEGIN)
            {
                /* store (and eat) the newline char */
                m_buffer[curpos] = 0x0A;
                curpos++;
 
                /* flush buffer to sequence */
                result = writeSequence_Impl(
                    ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos - 1);
                if (result != osl_File_E_None)
                    return result;
                *pBytesRead += curpos - bufpos;
                nOffset += curpos - bufpos;
            }
            break;
        }
    }
 
    result = writeSequence_Impl(ppSequence, &dstpos, nullptr, 0);
    if (result != osl_File_E_None)
        return result;
    if (dstpos > 0)
        return osl_File_E_None;
    if (bufpos >= m_buflen)
        return osl_File_E_AGAIN;
    return osl_File_E_None;
}
 
oslFileError FileHandle_Impl::writeSequence_Impl(
    sal_Sequence ** ppSequence,
    SIZE_T *        pnOffset,
    const void *    pBuffer,
    SIZE_T          nBytes)
{
    sal_Int32 nElements = *pnOffset + nBytes;
    if (!*ppSequence)
    {
        /* construct sequence */
        rtl_byte_sequence_constructNoDefault(ppSequence, nElements);
    }
    else if (nElements != (*ppSequence)->nElements)
    {
        /* resize sequence */
        rtl_byte_sequence_realloc(ppSequence, nElements);
    }
    if (*ppSequence)
    {
        /* fill sequence */
        memcpy(&((*ppSequence)->elements[*pnOffset]), pBuffer, nBytes);
        *pnOffset += nBytes;
    }
    return (*ppSequence) ? osl_File_E_None : osl_File_E_NOMEM;
}
 
oslFileError FileHandle_Impl::syncFile()
{
    oslFileError result = osl_File_E_None;
    if (m_state & STATE_MODIFIED)
    {
        sal_uInt64 uDone = 0;
        result = writeAt(m_bufptr, m_buffer, m_buflen, &uDone);
        if (result != osl_File_E_None)
            return result;
        if (uDone != m_buflen)
            return osl_File_E_IO;
        m_state &= ~STATE_MODIFIED;
    }
    return result;
}
 
extern "C" oslFileHandle osl_createFileHandleFromOSHandle(
    HANDLE     hFile,
    sal_uInt32 uFlags)
{
    if (!IsValidHandle(hFile))
        return nullptr; // EINVAL
 
    FileHandle_Impl * pImpl = new FileHandle_Impl(hFile);
 
    /* check for regular file */
    if (GetFileType(hFile) == FILE_TYPE_DISK)
    {
        /* mark seekable */
        pImpl->m_state |= FileHandle_Impl::STATE_SEEKABLE;
 
        /* init current size */
        LARGE_INTEGER uSize = { { 0, 0 } };
        (void) ::GetFileSizeEx(hFile, &uSize);
        pImpl->m_size = (sal::static_int_cast<sal_uInt64>(uSize.HighPart) << 32) + uSize.LowPart;
    }
 
    if (!(uFlags & osl_File_OpenFlag_Read))
        pImpl->m_state &= ~FileHandle_Impl::STATE_READABLE;
    if (!(uFlags & osl_File_OpenFlag_Write))
        pImpl->m_state &= ~FileHandle_Impl::STATE_WRITEABLE;
 
    SAL_WARN_IF(
        !((uFlags & osl_File_OpenFlag_Read) || (uFlags & osl_File_OpenFlag_Write)),
        "sal.osl",
        "osl_createFileHandleFromOSHandle(): missing read/write access flags");
    return static_cast<oslFileHandle>(pImpl);
}
 
oslFileError SAL_CALL osl_openFile(
    rtl_uString *   strPath,
    oslFileHandle * pHandle,
    sal_uInt32      uFlags)
{
    rtl_uString * strSysPath = nullptr;
    oslFileError result = osl_getSystemPathFromFileURL_(strPath, &strSysPath, false);
    if (result != osl_File_E_None)
        return result;
 
    DWORD dwAccess = GENERIC_READ, dwShare = FILE_SHARE_READ, dwCreation = 0;
 
    if (uFlags & osl_File_OpenFlag_Write)
        dwAccess |= GENERIC_WRITE;
    else
        dwShare  |= FILE_SHARE_WRITE;
 
    if (uFlags & osl_File_OpenFlag_NoLock)
        dwShare  |= FILE_SHARE_WRITE;
 
    if (uFlags & osl_File_OpenFlag_Create)
        dwCreation |= CREATE_NEW;
    else
        dwCreation |= OPEN_EXISTING;
 
    HANDLE hFile = CreateFileW(
        o3tl::toW(rtl_uString_getStr(strSysPath)),
        dwAccess, dwShare, nullptr, dwCreation, 0, nullptr);
 
    // @@@ ERROR HANDLING @@@
    if (!IsValidHandle(hFile))
        result = oslTranslateFileError(GetLastError());
 
    *pHandle = osl_createFileHandleFromOSHandle(hFile, uFlags | osl_File_OpenFlag_Read);
 
    rtl_uString_release(strSysPath);
    return result;
}
 
oslFileError SAL_CALL osl_syncFile(oslFileHandle Handle)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
        return osl_File_E_INVAL;
 
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
 
    oslFileError result = pImpl->syncFile();
    if (result != osl_File_E_None)
        return result;
 
    if (!FlushFileBuffers(pImpl->m_hFile))
        return oslTranslateFileError(GetLastError());
 
    return osl_File_E_None;
}
 
oslFileError SAL_CALL osl_closeFile(oslFileHandle Handle)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
        return osl_File_E_INVAL;
 
    ::EnterCriticalSection(&(pImpl->m_mutex));
 
    oslFileError result = pImpl->syncFile();
    if (result != osl_File_E_None)
    {
        /* ignore double failure */
        (void)::CloseHandle(pImpl->m_hFile);
    }
    else if (!::CloseHandle(pImpl->m_hFile))
    {
        /* translate error code */
        result = oslTranslateFileError(GetLastError());
    }
 
    ::LeaveCriticalSection(&(pImpl->m_mutex));
    delete pImpl;
    return result;
}
 
namespace {
 
// coverity[result_independent_of_operands] - crossplatform requirement
template<typename T> bool exceedsMaxSIZE_T(T n)
{ return n > std::numeric_limits< SIZE_T >::max(); }
 
}
 
oslFileError SAL_CALL osl_mapFile(
    oslFileHandle Handle,
    void**        ppAddr,
    sal_uInt64    uLength,
    sal_uInt64    uOffset,
    sal_uInt32    uFlags)
{
    struct FileMapping
    {
        HANDLE m_handle;
 
        explicit FileMapping(HANDLE hMap)
            : m_handle(hMap)
        {}
 
        ~FileMapping()
        {
            (void)::CloseHandle(m_handle);
        }
    };
 
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!ppAddr))
        return osl_File_E_INVAL;
    *ppAddr = nullptr;
 
    if (exceedsMaxSIZE_T(uLength))
        return osl_File_E_OVERFLOW;
    SIZE_T const nLength = sal::static_int_cast< SIZE_T >(uLength);
 
    FileMapping aMap(::CreateFileMappingW(pImpl->m_hFile, nullptr, SEC_COMMIT | PAGE_READONLY, 0, 0, nullptr));
    if (!IsValidHandle(aMap.m_handle))
        return oslTranslateFileError(GetLastError());
 
    DWORD const dwOffsetHi = sal::static_int_cast<DWORD>(uOffset >> 32);
    DWORD const dwOffsetLo = sal::static_int_cast<DWORD>(uOffset & 0xFFFFFFFF);
 
    *ppAddr = ::MapViewOfFile(aMap.m_handle, FILE_MAP_READ, dwOffsetHi, dwOffsetLo, nLength);
    if (!*ppAddr)
        return oslTranslateFileError(GetLastError());
 
    if (uFlags & osl_File_MapFlag_RandomAccess)
    {
        // Determine memory pagesize.
        SYSTEM_INFO info;
        ::GetSystemInfo(&info);
        DWORD const dwPageSize = info.dwPageSize;
 
        /*
         * Pagein, touching first byte of each memory page.
         * Note: volatile disables optimizing the loop away.
         */
        BYTE * pData(static_cast<BYTE*>(*ppAddr));
        SIZE_T nSize(nLength);
 
        volatile BYTE c = 0;
        while (nSize > dwPageSize)
        {
            c ^= pData[0];
            pData += dwPageSize;
            nSize -= dwPageSize;
        }
        if (nSize > 0)
        {
            c ^= pData[0];
        }
    }
    return osl_File_E_None;
}
 
oslFileError SAL_CALL osl_unmapFile(void* pAddr, sal_uInt64 /* uLength */)
{
    if (!pAddr)
        return osl_File_E_INVAL;
 
    if (!::UnmapViewOfFile(pAddr))
        return oslTranslateFileError(GetLastError());
 
    return osl_File_E_None;
}
 
oslFileError SAL_CALL osl_unmapMappedFile(oslFileHandle /* Handle */, void* pAddr, sal_uInt64 uLength)
{
    return osl_unmapFile(pAddr, uLength);
}
 
oslFileError
SAL_CALL osl_readLine(
    oslFileHandle   Handle,
    sal_Sequence ** ppSequence)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!ppSequence))
        return osl_File_E_INVAL;
    sal_uInt64 uBytesRead = 0;
 
    // read at current filepos; filepos += uBytesRead;
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    oslFileError result = pImpl->readLineAt(
        pImpl->m_filepos, ppSequence, &uBytesRead);
    if (result == osl_File_E_None)
        pImpl->m_filepos += uBytesRead;
    return result;
}
 
oslFileError SAL_CALL osl_readFile(
    oslFileHandle Handle,
    void *        pBuffer,
    sal_uInt64    uBytesRequested,
    sal_uInt64 *  pBytesRead)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesRead))
        return osl_File_E_INVAL;
 
    // read at current filepos; filepos += *pBytesRead;
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    oslFileError result = pImpl->readFileAt(
        pImpl->m_filepos, pBuffer, uBytesRequested, pBytesRead);
    if (result == osl_File_E_None)
        pImpl->m_filepos += *pBytesRead;
    return result;
}
 
oslFileError SAL_CALL osl_writeFile(
    oslFileHandle Handle,
    const void *  pBuffer,
    sal_uInt64    uBytesToWrite,
    sal_uInt64 *  pBytesWritten)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
 
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesWritten))
        return osl_File_E_INVAL;
 
    // write at current filepos; filepos += *pBytesWritten;
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    oslFileError result = pImpl->writeFileAt(
        pImpl->m_filepos, pBuffer, uBytesToWrite, pBytesWritten);
    if (result == osl_File_E_None)
        pImpl->m_filepos += *pBytesWritten;
    return result;
}
 
LONGLONG const g_limit_longlong = std::numeric_limits< LONGLONG >::max();
 
namespace {
 
// coverity[result_independent_of_operands] - crossplatform requirement
template<typename T> bool exceedsMaxLONGLONG(T n)
{ return n > g_limit_longlong; }
 
template<typename T> bool exceedsMinLONGLONG(T n)
{ return n < std::numeric_limits<LONGLONG>::min(); }
 
}
 
oslFileError SAL_CALL osl_readFileAt(
    oslFileHandle Handle,
    sal_uInt64    uOffset,
    void*         pBuffer,
    sal_uInt64    uBytesRequested,
    sal_uInt64*   pBytesRead)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
 
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesRead))
        return osl_File_E_INVAL;
    if ((pImpl->m_state & FileHandle_Impl::STATE_SEEKABLE) == 0)
        return osl_File_E_SPIPE;
 
    if (exceedsMaxLONGLONG(uOffset))
        return osl_File_E_OVERFLOW;
    LONGLONG const nOffset = sal::static_int_cast< LONGLONG >(uOffset);
 
    // read at specified fileptr
    FileHandle_Impl::Guard lock (&(pImpl->m_mutex));
    return pImpl->readFileAt(nOffset, pBuffer, uBytesRequested, pBytesRead);
}
 
oslFileError SAL_CALL osl_writeFileAt(
    oslFileHandle Handle,
    sal_uInt64    uOffset,
    const void*   pBuffer,
    sal_uInt64    uBytesToWrite,
    sal_uInt64*   pBytesWritten)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
 
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesWritten))
        return osl_File_E_INVAL;
    if ((pImpl->m_state & FileHandle_Impl::STATE_SEEKABLE) == 0)
        return osl_File_E_SPIPE;
 
    if (exceedsMaxLONGLONG(uOffset))
        return osl_File_E_OVERFLOW;
    LONGLONG const nOffset = sal::static_int_cast< LONGLONG >(uOffset);
 
    // write at specified fileptr
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    return pImpl->writeFileAt(nOffset, pBuffer, uBytesToWrite, pBytesWritten);
}
 
oslFileError SAL_CALL osl_isEndOfFile(oslFileHandle Handle, sal_Bool *pIsEOF)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
 
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pIsEOF))
        return osl_File_E_INVAL;
 
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    *pIsEOF = (pImpl->getPos() == pImpl->getSize());
    return osl_File_E_None;
}
 
oslFileError SAL_CALL osl_getFilePos(oslFileHandle Handle, sal_uInt64 *pPos)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pPos))
        return osl_File_E_INVAL;
 
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    *pPos = pImpl->getPos();
    return osl_File_E_None;
}
 
oslFileError SAL_CALL osl_setFilePos(oslFileHandle Handle, sal_uInt32 uHow, sal_Int64 uOffset)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
        return osl_File_E_INVAL;
 
    if (exceedsMaxLONGLONG(uOffset) || exceedsMinLONGLONG(uOffset))
        return osl_File_E_OVERFLOW;
    LONGLONG nPos = 0, nOffset = sal::static_int_cast< LONGLONG >(uOffset);
 
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    switch (uHow)
    {
        case osl_Pos_Absolut:
            if (nOffset < 0)
                return osl_File_E_INVAL;
            break;
 
        case osl_Pos_Current:
            nPos = sal::static_int_cast< LONGLONG >(pImpl->getPos());
            if ((nOffset < 0) && (nPos < -1*nOffset))
                return osl_File_E_INVAL;
            assert(nPos >= 0);
            if (nOffset > g_limit_longlong - nPos)
                return osl_File_E_OVERFLOW;
            break;
 
        case osl_Pos_End:
            nPos = sal::static_int_cast< LONGLONG >(pImpl->getSize());
            if ((nOffset < 0) && (nPos < -1*nOffset))
                return osl_File_E_INVAL;
            assert(nPos >= 0);
            if (nOffset > g_limit_longlong - nPos)
                return osl_File_E_OVERFLOW;
            break;
 
        default:
            return osl_File_E_INVAL;
    }
 
    return pImpl->setPos(nPos + nOffset);
}
 
oslFileError SAL_CALL osl_getFileSize(oslFileHandle Handle, sal_uInt64 *pSize)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
 
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pSize))
        return osl_File_E_INVAL;
 
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    *pSize = pImpl->getSize();
    return osl_File_E_None;
}
 
oslFileError SAL_CALL osl_setFileSize(oslFileHandle Handle, sal_uInt64 uSize)
{
    FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
 
    if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
        return osl_File_E_INVAL;
    if ((pImpl->m_state & FileHandle_Impl::STATE_WRITEABLE) == 0)
        return osl_File_E_BADF;
 
    if (exceedsMaxLONGLONG(uSize))
        return osl_File_E_OVERFLOW;
 
    FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
    oslFileError result = pImpl->syncFile();
    if (result != osl_File_E_None)
        return result;
    pImpl->m_bufptr = -1;
    pImpl->m_buflen = 0;
 
    return pImpl->setSize(uSize);
}
 
oslFileError SAL_CALL osl_removeFile(rtl_uString* strPath)
{
    rtl_uString *strSysPath = nullptr;
    oslFileError    error = osl_getSystemPathFromFileURL_(strPath, &strSysPath, false);
 
    if (error == osl_File_E_None)
    {
        if (DeleteFileW(o3tl::toW(rtl_uString_getStr(strSysPath))))
            error = osl_File_E_None;
        else
            error = oslTranslateFileError(GetLastError());
 
        rtl_uString_release(strSysPath);
    }
    return error;
}
 
oslFileError SAL_CALL osl_copyFile(rtl_uString* strPath, rtl_uString *strDestPath)
{
    rtl_uString *strSysPath = nullptr, *strSysDestPath = nullptr;
    oslFileError    error = osl_getSystemPathFromFileURL_(strPath, &strSysPath, false);
 
    if (error == osl_File_E_None)
        error = osl_getSystemPathFromFileURL_(strDestPath, &strSysDestPath, false);
 
    if (error == osl_File_E_None)
    {
        LPCWSTR src = o3tl::toW(rtl_uString_getStr(strSysPath));
        LPCWSTR dst = o3tl::toW(rtl_uString_getStr(strSysDestPath));
 
        if (CopyFileW(src, dst, FALSE))
            error = osl_File_E_None;
        else
            error = oslTranslateFileError(GetLastError());
    }
 
    if (strSysPath)
        rtl_uString_release(strSysPath);
    if (strSysDestPath)
        rtl_uString_release(strSysDestPath);
 
    return error;
}
 
oslFileError SAL_CALL osl_moveFile(rtl_uString* strPath, rtl_uString *strDestPath)
{
    rtl_uString *strSysPath = nullptr, *strSysDestPath = nullptr;
    oslFileError    error = osl_getSystemPathFromFileURL_(strPath, &strSysPath, false);
 
    if (error == osl_File_E_None)
        error = osl_getSystemPathFromFileURL_(strDestPath, &strSysDestPath, false);
 
    if (error == osl_File_E_None)
    {
        LPCWSTR src = o3tl::toW(rtl_uString_getStr(strSysPath));
        LPCWSTR dst = o3tl::toW(rtl_uString_getStr(strSysDestPath));
 
        if (MoveFileExW(src, dst, MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING))
            error = osl_File_E_None;
        else
            error = oslTranslateFileError(GetLastError());
    }
 
    if (strSysPath)
        rtl_uString_release(strSysPath);
    if (strSysDestPath)
        rtl_uString_release(strSysDestPath);
 
    return error;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V555 The expression '(curpos - bufpos) > 0' will work as 'curpos != bufpos'.