<commit_before>/* -*- 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 "osl/file.hxx"
#include "osl/detail/file.h"

#include "osl/diagnose.h"
#include "rtl/alloc.h"

#include "system.h"
#include "createfilehandlefromfd.hxx"
#include "file_error_transl.h"
#include "file_url.h"

#include <algorithm>
#include <limits>

#include <string.h>
#include <pthread.h>
#include <sys/mman.h>

#if defined(MACOSX)

#include <sys/param.h>
#include <sys/mount.h>
#define HAVE_O_EXLOCK

// add MACOSX Time Value
#include <CoreFoundation/CoreFoundation.h>

#endif /* MACOSX */

#ifdef ANDROID
#include <osl/detail/android-bootstrap.h>
#endif

#ifdef DEBUG_OSL_FILE
#   define OSL_FILE_TRACE osl_trace
#else
#   define OSL_FILE_TRACE(fmt, ...)
#endif



/*******************************************************************
 *
 * FileHandle_Impl interface
 *
 ******************************************************************/
struct FileHandle_Impl
{
    pthread_mutex_t m_mutex;
    rtl_String *    m_strFilePath; /* holds native file path */
    int             m_fd;

    enum Kind
    {
        KIND_FD = 1,
        KIND_MEM = 2
    };
    int          m_kind;
    /** State
     */
    enum StateBits
    {
        STATE_SEEKABLE  = 1, /* default */
        STATE_READABLE  = 2, /* default */
        STATE_WRITEABLE = 4, /* open() sets, write() requires, else osl_File_E_BADF */
        STATE_MODIFIED  = 8  /* write() sets, flush() resets  */
    };
    int          m_state;

    sal_uInt64   m_size;    /* file size */
    off_t        m_offset;  /* physical offset from begin of file */
    off_t        m_fileptr; /* logical offset from begin of file */

    off_t        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 (int fd, Kind kind = KIND_FD, char const * path = "<anon>");
    ~FileHandle_Impl();

    static void* operator new (size_t n);
    static void  operator delete (void * p);

    static size_t getpagesize();

    sal_uInt64   getPos() const;
    oslFileError setPos (sal_uInt64 uPos);

    sal_uInt64   getSize() const;
    oslFileError setSize (sal_uInt64 uSize);

    oslFileError readAt (
        off_t        nOffset,
        void *       pBuffer,
        size_t       nBytesRequested,
        sal_uInt64 * pBytesRead);

    oslFileError writeAt (
        off_t        nOffset,
        void const * pBuffer,
        size_t       nBytesToWrite,
        sal_uInt64 * pBytesWritten);

    oslFileError readFileAt (
        off_t        nOffset,
        void *       pBuffer,
        size_t       nBytesRequested,
        sal_uInt64 * pBytesRead);

    oslFileError writeFileAt (
        off_t        nOffset,
        void const * pBuffer,
        size_t       nBytesToWrite,
        sal_uInt64 * pBytesWritten);

    oslFileError readLineAt (
        off_t           nOffset,
        sal_Sequence ** ppSequence,
        sal_uInt64 *    pBytesRead);

    oslFileError writeSequence_Impl (
        sal_Sequence ** ppSequence,
        size_t *        pnOffset,
        const void *    pBuffer,
        size_t          nBytes);

    oslFileError syncFile();

    /** Buffer cache / allocator.
     */
    class Allocator
    {
        rtl_cache_type * m_cache;
        size_t           m_bufsiz;

        Allocator (Allocator const &);
        Allocator & operator= (Allocator const &);

    public:
        static Allocator & get();

        void allocate (sal_uInt8 ** ppBuffer, size_t * pnSize);
        void deallocate (sal_uInt8 * pBuffer);

    protected:
        Allocator();
        ~Allocator();
    };

    /** Guard.
     */
    class Guard
    {
        pthread_mutex_t * m_mutex;

    public:
        explicit Guard(pthread_mutex_t * pMutex);
        ~Guard();
    };
};

/*******************************************************************
 *
 * FileHandle_Impl implementation
 *
 ******************************************************************/

FileHandle_Impl::Allocator &
FileHandle_Impl::Allocator::get()
{
    static Allocator g_aBufferAllocator;
    return g_aBufferAllocator;
}

FileHandle_Impl::Allocator::Allocator()
    : m_cache  (0),
      m_bufsiz (0)
{
    size_t const pagesize = FileHandle_Impl::getpagesize();
    if (size_t(-1) != pagesize)
    {
        m_cache  = rtl_cache_create (
            "osl_file_buffer_cache", pagesize, 0, 0, 0, 0, 0, 0, 0);
        if (0 != m_cache)
            m_bufsiz = pagesize;
    }
}
FileHandle_Impl::Allocator::~Allocator()
{
    rtl_cache_destroy (m_cache), m_cache = 0;
}

void FileHandle_Impl::Allocator::allocate (sal_uInt8 ** ppBuffer, size_t * pnSize)
{
    OSL_PRECOND((0 != ppBuffer) && (0 != pnSize), "FileHandle_Impl::Allocator::allocate(): contract violation");
    if ((0 != ppBuffer) && (0 != pnSize))
        *ppBuffer = static_cast< sal_uInt8* >(rtl_cache_alloc(m_cache)), *pnSize = m_bufsiz;
}
void FileHandle_Impl::Allocator::deallocate (sal_uInt8 * pBuffer)
{
    if (0 != pBuffer)
        rtl_cache_free (m_cache, pBuffer);
}

FileHandle_Impl::Guard::Guard(pthread_mutex_t * pMutex)
    : m_mutex (pMutex)
{
    OSL_PRECOND (m_mutex != 0, "FileHandle_Impl::Guard::Guard(): null pointer.");
    (void) pthread_mutex_lock (m_mutex); // ignoring EINVAL ...
}
FileHandle_Impl::Guard::~Guard()
{
    OSL_PRECOND (m_mutex != 0, "FileHandle_Impl::Guard::~Guard(): null pointer.");
    (void) pthread_mutex_unlock (m_mutex);
}

FileHandle_Impl::FileHandle_Impl (int fd, enum Kind kind, char const * path)
    : m_strFilePath (0),
      m_fd      (fd),
      m_kind    (kind),
      m_state   (STATE_SEEKABLE | STATE_READABLE),
      m_size    (0),
      m_offset  (0),
      m_fileptr (0),
      m_bufptr  (-1),
      m_buflen  (0),
      m_bufsiz  (0),
      m_buffer  (0)
{
    (void) pthread_mutex_init(&m_mutex, 0);
    rtl_string_newFromStr (&m_strFilePath, path);
    if (m_kind == KIND_FD) {
        Allocator::get().allocate (&m_buffer, &m_bufsiz);
        if (0 != m_buffer)
            memset (m_buffer, 0, m_bufsiz);
    }
}
FileHandle_Impl::~FileHandle_Impl()
{
    if (m_kind == KIND_FD)
        Allocator::get().deallocate (m_buffer), m_buffer = 0;
    rtl_string_release (m_strFilePath), m_strFilePath = 0;
    (void) pthread_mutex_destroy(&m_mutex); // ignoring EBUSY ...
}

void* FileHandle_Impl::operator new (size_t n)
{
    return rtl_allocateMemory(n);
}
void FileHandle_Impl::operator delete (void * p)
{
    rtl_freeMemory(p);
}

size_t FileHandle_Impl::getpagesize()
{
#if defined(FREEBSD) || defined(NETBSD) || defined(MACOSX) || \
    defined(OPENBSD) || defined(DRAGONFLY)
    return sal::static_int_cast< size_t >(::getpagesize());
#else /* POSIX */
    return sal::static_int_cast< size_t >(::sysconf(_SC_PAGESIZE));
#endif /* xBSD || POSIX */
}

sal_uInt64 FileHandle_Impl::getPos() const
{
    return sal::static_int_cast< sal_uInt64 >(m_fileptr);
}

oslFileError FileHandle_Impl::setPos (sal_uInt64 uPos)
{
    OSL_FILE_TRACE("FileHandle_Impl::setPos(%d, %lld) => %lld", m_fd, getPos(), uPos);
    m_fileptr = sal::static_int_cast< off_t >(uPos);
    return osl_File_E_None;
}

sal_uInt64 FileHandle_Impl::getSize() const
{
    off_t const bufend = std::max((off_t)(0), m_bufptr) + m_buflen;
    return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend));
}

oslFileError FileHandle_Impl::setSize (sal_uInt64 uSize)
{
    off_t const nSize = sal::static_int_cast< off_t >(uSize);
    if (-1 == ftruncate (m_fd, nSize))
    {
        /* Failure. Save original result. Try fallback algorithm */
        oslFileError result = oslTranslateFileError (OSL_FET_ERROR, errno);

        /* Check against current size. Fail upon 'shrink' */
        if (uSize <= getSize())
        {
            /* Failure upon 'shrink'. Return original result */
            return (result);
        }

        /* Save current position */
        off_t const nCurPos = (off_t)lseek (m_fd, (off_t)0, SEEK_CUR);
        if (nCurPos == (off_t)(-1))
            return (result);

        /* Try 'expand' via 'lseek()' and 'write()' */
        if (-1 == lseek (m_fd, (off_t)(nSize - 1), SEEK_SET))
            return (result);

        if (-1 == write (m_fd, (char*)"", (size_t)1))
        {
            /* Failure. Restore saved position */
            (void) lseek (m_fd, (off_t)(nCurPos), SEEK_SET);
            return (result);
        }

        /* Success. Restore saved position */
        if (-1 == lseek (m_fd, (off_t)nCurPos, SEEK_SET))
            return (result);
    }

    OSL_FILE_TRACE("osl_setFileSize(%d, %lld) => %ld", m_fd, getSize(), nSize);
    m_size = sal::static_int_cast< sal_uInt64 >(nSize);
    return osl_File_E_None;
}

oslFileError FileHandle_Impl::readAt (
    off_t        nOffset,
    void *       pBuffer,
    size_t       nBytesRequested,
    sal_uInt64 * pBytesRead)
{
    OSL_PRECOND((m_state & STATE_SEEKABLE), "FileHandle_Impl::readAt(): not seekable");
    if (!(m_state & STATE_SEEKABLE))
        return osl_File_E_SPIPE;

    OSL_PRECOND((m_state & STATE_READABLE), "FileHandle_Impl::readAt(): not readable");
    if (!(m_state & STATE_READABLE))
        return osl_File_E_BADF;

    if (m_kind == KIND_MEM)
    {
        ssize_t nBytes;

        m_offset = nOffset;

        if ((sal_uInt64) m_offset >= m_size)
            nBytes = 0;
        else
        {
            nBytes = std::min(nBytesRequested, (size_t) (m_size - m_offset));
            memmove(pBuffer, m_buffer + m_offset, nBytes);
            m_offset += nBytes;
        }
        *pBytesRead = nBytes;
        return osl_File_E_None;
    }

    ssize_t nBytes = ::pread (m_fd, pBuffer, nBytesRequested, nOffset);
    if ((-1 == nBytes) && (EOVERFLOW == errno))
    {
        /* Some 'pread()'s fail with EOVERFLOW when reading at (or past)
         * end-of-file, different from 'lseek() + read()' behaviour.
         * Returning '0 bytes read' and 'osl_File_E_None' instead.
         */
        nBytes = 0;
    }
    if (-1 == nBytes)
        return oslTranslateFileError (OSL_FET_ERROR, errno);

    OSL_FILE_TRACE("FileHandle_Impl::readAt(%d, %lld, %ld)", m_fd, nOffset, nBytes);
    *pBytesRead = nBytes;
    return osl_File_E_None;
}

oslFileError FileHandle_Impl::writeAt (
    off_t        nOffset,
    void const * pBuffer,
    size_t       nBytesToWrite,
    sal_uInt64 * pBytesWritten)
{
    OSL_PRECOND((m_state & STATE_SEEKABLE), "FileHandle_Impl::writeAt(): not seekable");
    if (!(m_state & STATE_SEEKABLE))
        return osl_File_E_SPIPE;

    OSL_PRECOND((m_state & STATE_WRITEABLE), "FileHandle_Impl::writeAt(): not writeable");
    if (!(m_state & STATE_WRITEABLE))
        return osl_File_E_BADF;

    ssize_t nBytes = ::pwrite (m_fd, pBuffer, nBytesToWrite, nOffset);
    if (-1 == nBytes)
        return oslTranslateFileError (OSL_FET_ERROR, errno);

    OSL_FILE_TRACE("FileHandle_Impl::writeAt(%d, %lld, %ld)", m_fd, nOffset, nBytes);
    m_size = std::max (m_size, sal::static_int_cast< sal_uInt64 >(nOffset + nBytes));

    *pBytesWritten = nBytes;
    return osl_File_E_None;
}

oslFileError FileHandle_Impl::readFileAt (
    off_t        nOffset,
    void *       pBuffer,
    size_t       nBytesRequested,
    sal_uInt64 * pBytesRead)
{
    if (0 == (m_state & STATE_SEEKABLE))
    {
        // not seekable (pipe)
        ssize_t nBytes = ::read (m_fd, pBuffer, nBytesRequested);
        if (-1 == nBytes)
            return oslTranslateFileError (OSL_FET_ERROR, errno);
        *pBytesRead = nBytes;
        return osl_File_E_None;
    }
    else if (m_kind == KIND_MEM || 0 == m_buffer)
    {
        // not buffered
        return readAt (nOffset, pBuffer, nBytesRequested, pBytesRead);
    }
    else
    {
        sal_uInt8 * buffer = static_cast<sal_uInt8*>(pBuffer);
        for (*pBytesRead = 0; nBytesRequested > 0; )
        {
            off_t  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 -= 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 = uDone;
            }
            if (bufpos >= m_buflen)
            {
                // end of file
                return osl_File_E_None;
            }

            size_t const bytes = std::min (m_buflen - bufpos, nBytesRequested);
            OSL_FILE_TRACE("FileHandle_Impl::readFileAt(%d, %lld, %ld)", m_fd, nOffset, bytes);

            memcpy (&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes);
            nBytesRequested -= bytes, *pBytesRead += bytes, nOffset += bytes;
        }
        return osl_File_E_None;
    }
}

oslFileError FileHandle_Impl::writeFileAt (
    off_t        nOffset,
    void const * pBuffer,
    size_t       nBytesToWrite,
    sal_uInt64 * pBytesWritten)
{
    if (0 == (m_state & STATE_SEEKABLE))
    {
        // not seekable (pipe)
        ssize_t nBytes = ::write (m_fd, pBuffer, nBytesToWrite);
        if (-1 == nBytes)
            return oslTranslateFileError (OSL_FET_ERROR, errno);
        *pBytesWritten = nBytes;
        return osl_File_E_None;
    }
    else if (0 == 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; )
        {
            off_t  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 to 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 -= 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 = uDone;<commit_msg>Fix warning: Value stored is never read<commit_after>/* -*- 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 "osl/file.hxx"
#include "osl/detail/file.h"

#include "osl/diagnose.h"
#include "rtl/alloc.h"

#include "system.h"
#include "createfilehandlefromfd.hxx"
#include "file_error_transl.h"
#include "file_url.h"

#include <algorithm>
#include <limits>

#include <string.h>
#include <pthread.h>
#include <sys/mman.h>

#if defined(MACOSX)

#include <sys/param.h>
#include <sys/mount.h>
#define HAVE_O_EXLOCK

// add MACOSX Time Value
#include <CoreFoundation/CoreFoundation.h>

#endif /* MACOSX */

#ifdef ANDROID
#include <osl/detail/android-bootstrap.h>
#endif

#ifdef DEBUG_OSL_FILE
#   define OSL_FILE_TRACE osl_trace
#else
#   define OSL_FILE_TRACE(fmt, ...)
#endif



/*******************************************************************
 *
 * FileHandle_Impl interface
 *
 ******************************************************************/
struct FileHandle_Impl
{
    pthread_mutex_t m_mutex;
    rtl_String *    m_strFilePath; /* holds native file path */
    int             m_fd;

    enum Kind
    {
        KIND_FD = 1,
        KIND_MEM = 2
    };
    int          m_kind;
    /** State
     */
    enum StateBits
    {
        STATE_SEEKABLE  = 1, /* default */
        STATE_READABLE  = 2, /* default */
        STATE_WRITEABLE = 4, /* open() sets, write() requires, else osl_File_E_BADF */
        STATE_MODIFIED  = 8  /* write() sets, flush() resets  */
    };
    int          m_state;

    sal_uInt64   m_size;    /* file size */
    off_t        m_offset;  /* physical offset from begin of file */
    off_t        m_fileptr; /* logical offset from begin of file */

    off_t        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 (int fd, Kind kind = KIND_FD, char const * path = "<anon>");
    ~FileHandle_Impl();

    static void* operator new (size_t n);
    static void  operator delete (void * p);

    static size_t getpagesize();

    sal_uInt64   getPos() const;
    oslFileError setPos (sal_uInt64 uPos);

    sal_uInt64   getSize() const;
    oslFileError setSize (sal_uInt64 uSize);

    oslFileError readAt (
        off_t        nOffset,
        void *       pBuffer,
        size_t       nBytesRequested,
        sal_uInt64 * pBytesRead);

    oslFileError writeAt (
        off_t        nOffset,
        void const * pBuffer,
        size_t       nBytesToWrite,
        sal_uInt64 * pBytesWritten);

    oslFileError readFileAt (
        off_t        nOffset,
        void *       pBuffer,
        size_t       nBytesRequested,
        sal_uInt64 * pBytesRead);

    oslFileError writeFileAt (
        off_t        nOffset,
        void const * pBuffer,
        size_t       nBytesToWrite,
        sal_uInt64 * pBytesWritten);

    oslFileError readLineAt (
        off_t           nOffset,
        sal_Sequence ** ppSequence,
        sal_uInt64 *    pBytesRead);

    oslFileError writeSequence_Impl (
        sal_Sequence ** ppSequence,
        size_t *        pnOffset,
        const void *    pBuffer,
        size_t          nBytes);

    oslFileError syncFile();

    /** Buffer cache / allocator.
     */
    class Allocator
    {
        rtl_cache_type * m_cache;
        size_t           m_bufsiz;

        Allocator (Allocator const &);
        Allocator & operator= (Allocator const &);

    public:
        static Allocator & get();

        void allocate (sal_uInt8 ** ppBuffer, size_t * pnSize);
        void deallocate (sal_uInt8 * pBuffer);

    protected:
        Allocator();
        ~Allocator();
    };

    /** Guard.
     */
    class Guard
    {
        pthread_mutex_t * m_mutex;

    public:
        explicit Guard(pthread_mutex_t * pMutex);
        ~Guard();
    };
};

/*******************************************************************
 *
 * FileHandle_Impl implementation
 *
 ******************************************************************/

FileHandle_Impl::Allocator &
FileHandle_Impl::Allocator::get()
{
    static Allocator g_aBufferAllocator;
    return g_aBufferAllocator;
}

FileHandle_Impl::Allocator::Allocator()
    : m_cache  (0),
      m_bufsiz (0)
{
    size_t const pagesize = FileHandle_Impl::getpagesize();
    if (size_t(-1) != pagesize)
    {
        m_cache  = rtl_cache_create (
            "osl_file_buffer_cache", pagesize, 0, 0, 0, 0, 0, 0, 0);
        if (0 != m_cache)
            m_bufsiz = pagesize;
    }
}
FileHandle_Impl::Allocator::~Allocator()
{
    rtl_cache_destroy (m_cache), m_cache = 0;
}

void FileHandle_Impl::Allocator::allocate (sal_uInt8 ** ppBuffer, size_t * pnSize)
{
    OSL_PRECOND((0 != ppBuffer) && (0 != pnSize), "FileHandle_Impl::Allocator::allocate(): contract violation");
    if ((0 != ppBuffer) && (0 != pnSize))
        *ppBuffer = static_cast< sal_uInt8* >(rtl_cache_alloc(m_cache)), *pnSize = m_bufsiz;
}
void FileHandle_Impl::Allocator::deallocate (sal_uInt8 * pBuffer)
{
    if (0 != pBuffer)
        rtl_cache_free (m_cache, pBuffer);
}

FileHandle_Impl::Guard::Guard(pthread_mutex_t * pMutex)
    : m_mutex (pMutex)
{
    OSL_PRECOND (m_mutex != 0, "FileHandle_Impl::Guard::Guard(): null pointer.");
    (void) pthread_mutex_lock (m_mutex); // ignoring EINVAL ...
}
FileHandle_Impl::Guard::~Guard()
{
    OSL_PRECOND (m_mutex != 0, "FileHandle_Impl::Guard::~Guard(): null pointer.");
    (void) pthread_mutex_unlock (m_mutex);
}

FileHandle_Impl::FileHandle_Impl (int fd, enum Kind kind, char const * path)
    : m_strFilePath (0),
      m_fd      (fd),
      m_kind    (kind),
      m_state   (STATE_SEEKABLE | STATE_READABLE),
      m_size    (0),
      m_offset  (0),
      m_fileptr (0),
      m_bufptr  (-1),
      m_buflen  (0),
      m_bufsiz  (0),
      m_buffer  (0)
{
    (void) pthread_mutex_init(&m_mutex, 0);
    rtl_string_newFromStr (&m_strFilePath, path);
    if (m_kind == KIND_FD) {
        Allocator::get().allocate (&m_buffer, &m_bufsiz);
        if (0 != m_buffer)
            memset (m_buffer, 0, m_bufsiz);
    }
}
FileHandle_Impl::~FileHandle_Impl()
{
    if (m_kind == KIND_FD)
        Allocator::get().deallocate (m_buffer), m_buffer = 0;
    rtl_string_release (m_strFilePath), m_strFilePath = 0;
    (void) pthread_mutex_destroy(&m_mutex); // ignoring EBUSY ...
}

void* FileHandle_Impl::operator new (size_t n)
{
    return rtl_allocateMemory(n);
}
void FileHandle_Impl::operator delete (void * p)
{
    rtl_freeMemory(p);
}

size_t FileHandle_Impl::getpagesize()
{
#if defined(FREEBSD) || defined(NETBSD) || defined(MACOSX) || \
    defined(OPENBSD) || defined(DRAGONFLY)
    return sal::static_int_cast< size_t >(::getpagesize());
#else /* POSIX */
    return sal::static_int_cast< size_t >(::sysconf(_SC_PAGESIZE));
#endif /* xBSD || POSIX */
}

sal_uInt64 FileHandle_Impl::getPos() const
{
    return sal::static_int_cast< sal_uInt64 >(m_fileptr);
}

oslFileError FileHandle_Impl::setPos (sal_uInt64 uPos)
{
    OSL_FILE_TRACE("FileHandle_Impl::setPos(%d, %lld) => %lld", m_fd, getPos(), uPos);
    m_fileptr = sal::static_int_cast< off_t >(uPos);
    return osl_File_E_None;
}

sal_uInt64 FileHandle_Impl::getSize() const
{
    off_t const bufend = std::max((off_t)(0), m_bufptr) + m_buflen;
    return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend));
}

oslFileError FileHandle_Impl::setSize (sal_uInt64 uSize)
{
    off_t const nSize = sal::static_int_cast< off_t >(uSize);
    if (-1 == ftruncate (m_fd, nSize))
    {
        /* Failure. Save original result. Try fallback algorithm */
        oslFileError result = oslTranslateFileError (OSL_FET_ERROR, errno);

        /* Check against current size. Fail upon 'shrink' */
        if (uSize <= getSize())
        {
            /* Failure upon 'shrink'. Return original result */
            return (result);
        }

        /* Save current position */
        off_t const nCurPos = (off_t)lseek (m_fd, (off_t)0, SEEK_CUR);
        if (nCurPos == (off_t)(-1))
            return (result);

        /* Try 'expand' via 'lseek()' and 'write()' */
        if (-1 == lseek (m_fd, (off_t)(nSize - 1), SEEK_SET))
            return (result);

        if (-1 == write (m_fd, (char*)"", (size_t)1))
        {
            /* Failure. Restore saved position */
            (void) lseek (m_fd, (off_t)(nCurPos), SEEK_SET);
            return (result);
        }

        /* Success. Restore saved position */
        if (-1 == lseek (m_fd, (off_t)nCurPos, SEEK_SET))
            return (result);
    }

    OSL_FILE_TRACE("osl_setFileSize(%d, %lld) => %ld", m_fd, getSize(), nSize);
    m_size = sal::static_int_cast< sal_uInt64 >(nSize);
    return osl_File_E_None;
}

oslFileError FileHandle_Impl::readAt (
    off_t        nOffset,
    void *       pBuffer,
    size_t       nBytesRequested,
    sal_uInt64 * pBytesRead)
{
    OSL_PRECOND((m_state & STATE_SEEKABLE), "FileHandle_Impl::readAt(): not seekable");
    if (!(m_state & STATE_SEEKABLE))
        return osl_File_E_SPIPE;

    OSL_PRECOND((m_state & STATE_READABLE), "FileHandle_Impl::readAt(): not readable");
    if (!(m_state & STATE_READABLE))
        return osl_File_E_BADF;

    if (m_kind == KIND_MEM)
    {
        ssize_t nBytes;

        m_offset = nOffset;

        if ((sal_uInt64) m_offset >= m_size)
            nBytes = 0;
        else
        {
            nBytes = std::min(nBytesRequested, (size_t) (m_size - m_offset));
            memmove(pBuffer, m_buffer + m_offset, nBytes);
            m_offset += nBytes;
        }
        *pBytesRead = nBytes;
        return osl_File_E_None;
    }

    ssize_t nBytes = ::pread (m_fd, pBuffer, nBytesRequested, nOffset);
    if ((-1 == nBytes) && (EOVERFLOW == errno))
    {
        /* Some 'pread()'s fail with EOVERFLOW when reading at (or past)
         * end-of-file, different from 'lseek() + read()' behaviour.
         * Returning '0 bytes read' and 'osl_File_E_None' instead.
         */
        nBytes = 0;
    }
    if (-1 == nBytes)
        return oslTranslateFileError (OSL_FET_ERROR, errno);

    OSL_FILE_TRACE("FileHandle_Impl::readAt(%d, %lld, %ld)", m_fd, nOffset, nBytes);
    *pBytesRead = nBytes;
    return osl_File_E_None;
}

oslFileError FileHandle_Impl::writeAt (
    off_t        nOffset,
    void const * pBuffer,
    size_t       nBytesToWrite,
    sal_uInt64 * pBytesWritten)
{
    OSL_PRECOND((m_state & STATE_SEEKABLE), "FileHandle_Impl::writeAt(): not seekable");
    if (!(m_state & STATE_SEEKABLE))
        return osl_File_E_SPIPE;

    OSL_PRECOND((m_state & STATE_WRITEABLE), "FileHandle_Impl::writeAt(): not writeable");
    if (!(m_state & STATE_WRITEABLE))
        return osl_File_E_BADF;

    ssize_t nBytes = ::pwrite (m_fd, pBuffer, nBytesToWrite, nOffset);
    if (-1 == nBytes)
        return oslTranslateFileError (OSL_FET_ERROR, errno);

    OSL_FILE_TRACE("FileHandle_Impl::writeAt(%d, %lld, %ld)", m_fd, nOffset, nBytes);
    m_size = std::max (m_size, sal::static_int_cast< sal_uInt64 >(nOffset + nBytes));

    *pBytesWritten = nBytes;
    return osl_File_E_None;
}

oslFileError FileHandle_Impl::readFileAt (
    off_t        nOffset,
    void *       pBuffer,
    size_t       nBytesRequested,
    sal_uInt64 * pBytesRead)
{
    if (0 == (m_state & STATE_SEEKABLE))
    {
        // not seekable (pipe)
        ssize_t nBytes = ::read (m_fd, pBuffer, nBytesRequested);
        if (-1 == nBytes)
            return oslTranslateFileError (OSL_FET_ERROR, errno);
        *pBytesRead = nBytes;
        return osl_File_E_None;
    }
    else if (m_kind == KIND_MEM || 0 == m_buffer)
    {
        // not buffered
        return readAt (nOffset, pBuffer, nBytesRequested, pBytesRead);
    }
    else
    {
        sal_uInt8 * buffer = static_cast<sal_uInt8*>(pBuffer);
        for (*pBytesRead = 0; nBytesRequested > 0; )
        {
            off_t  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);

                    *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 = uDone;
            }
            if (bufpos >= m_buflen)
            {
                // end of file
                return osl_File_E_None;
            }

            size_t const bytes = std::min (m_buflen - bufpos, nBytesRequested);
            OSL_FILE_TRACE("FileHandle_Impl::readFileAt(%d, %lld, %ld)", m_fd, nOffset, bytes);

            memcpy (&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes);
            nBytesRequested -= bytes, *pBytesRead += bytes, nOffset += bytes;
        }
        return osl_File_E_None;
    }
}

oslFileError FileHandle_Impl::writeFileAt (
    off_t        nOffset,
    void const * pBuffer,
    size_t       nBytesToWrite,
    sal_uInt64 * pBytesWritten)
{
    if (0 == (m_state & STATE_SEEKABLE))
    {
        // not seekable (pipe)
        ssize_t nBytes = ::write (m_fd, pBuffer, nBytesToWrite);
        if (-1 == nBytes)
            return oslTranslateFileError (OSL_FET_ERROR, errno);
        *pBytesWritten = nBytes;
        return osl_File_E_None;
    }
    else if (0 == 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; )
        {
            off_t  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 to 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;

                    *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 = uDone;
{'fraction_non_alphanumeric': 0.12908825141604238, 'fraction_numerical': 0.008953042207198976, 'mean_word_length': 2.294111746413883, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2717340', 'n_tokens_mistral': 11380, 'n_tokens_neox': 10898, 'n_words': 2519}