Logo Search packages:      
Sourcecode: virtualbox-ose version File versions  Download package

kRdrBuffered.cpp

Go to the documentation of this file.
/* $Id: kRdrBuffered.cpp 2 2007-11-16 16:07:14Z bird $ */
/** @file
 * kRdrBuffered - Buffered File Provider.
 */

/*
 * Copyright (c) 2006-2007 knut st. osmundsen <bird-kStuff-spam@anduin.net>
 *
 * This file is part of kStuff.
 *
 * kStuff is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * In addition to the permissions in the GNU Lesser General Public
 * License, you are granted unlimited permission to link the compiled
 * version of this file into combinations with other programs, and to
 * distribute those combinations without any restriction coming from
 * the use of this file.
 *
 * kStuff 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with kStuff; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 */

/*******************************************************************************
*   Header Files                                                               *
*******************************************************************************/
#include "kRdrInternal.h"
#include <k/kHlpAlloc.h>
#include <k/kHlpString.h>


/*******************************************************************************
*   Structures and Typedefs                                                    *
*******************************************************************************/
/**
 * The buffered file provier instance.
 * This is just a wrapper around another file provider.
 */
00048 typedef struct KRDRBUF
{
    /** The file reader vtable. */
00051     KRDR                Core;
    /** The actual file provider that we're wrapping. */
00053     PKRDR               pRdr;
    /** The current file offset. */
00055     KFOFF               offFile;
    /** The file size. */
00057     KFOFF               cbFile;
    /** The offset of the buffer. */
00059     KFOFF               offBuf;
    /** The offset of the end of the buffer. */
00061     KFOFF               offBufEnd;
    /** The number of valid buffer bytes. */
00063     KSIZE               cbBufValid;
    /** The size of the buffer. */
00065     KSIZE               cbBuf;
    /** The buffer. */
00067     KU8                *pbBuf;
    /** Whether the pRdr instance should be closed together with us or not. */
00069     KBOOL               fCloseIt;
    /** Set if the buffer has been messed up by kRdrBufLineQ. */
00071     KBOOL               fTainedByLineQ;
} KRDRBUF, *PKRDRBUF;


/*******************************************************************************
*   Internal Functions                                                         *
*******************************************************************************/
static void     krdrBufDone(PKRDR pRdr);
static int      krdrBufUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
static int      krdrBufProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
static int      krdrBufRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
static int      krdrBufMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
static KSIZE    krdrBufPageSize(PKRDR pRdr);
static const char *krdrBufName(PKRDR pRdr);
static KIPTR    krdrBufNativeFH(PKRDR pRdr);
static KFOFF    krdrBufTell(PKRDR pRdr);
static KFOFF    krdrBufSize(PKRDR pRdr);
static int      krdrBufAllUnmap(PKRDR pRdr, const void *pvBits);
static int      krdrBufAllMap(PKRDR pRdr, const void **ppvBits);
static int      krdrBufRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off);
static int      krdrBufDestroy(PKRDR pRdr);
static int      krdrBufCreate(PPKRDR ppRdr, const char *pszFilename);


/*******************************************************************************
*   Global Variables                                                           *
*******************************************************************************/
/** Native file provider operations.
 *
 * @remark  This is not in the file provider list as its intended for wrapping
 *          other kRdr instances.
 */
00103 static const KRDROPS g_krdrBufOps =
{
    "Buffered kRdr",
    NULL,
    krdrBufCreate,
    krdrBufDestroy,
    krdrBufRead,
    krdrBufAllMap,
    krdrBufAllUnmap,
    krdrBufSize,
    krdrBufTell,
    krdrBufName,
    krdrBufNativeFH,
    krdrBufPageSize,
    krdrBufMap,
    krdrBufRefresh,
    krdrBufProtect,
    krdrBufUnmap,
    krdrBufDone,
    42
};


/** @copydoc KRDROPS::pfnDone */
00127 static void     krdrBufDone(PKRDR pRdr)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnDone(pThis->pRdr);
}


/** @copydoc KRDROPS::pfnUnmap */
00135 static int      krdrBufUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnUnmap(pThis->pRdr, pvBase, cSegments, paSegments);
}


/** @copydoc KRDROPS::pfnProtect */
00143 static int      krdrBufProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnProtect(pThis->pRdr, pvBase, cSegments, paSegments, fUnprotectOrProtect);
}


/** @copydoc KRDROPS::pfnRefresh */
00151 static int krdrBufRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnRefresh(pThis->pRdr, pvBase, cSegments, paSegments);
}


/** @copydoc KRDROPS::pfnMap */
00159 static int krdrBufMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnMap(pThis->pRdr, ppvBase, cSegments, paSegments, fFixed);
}


/** @copydoc KRDROPS::pfnPageSize */
00167 static KSIZE krdrBufPageSize(PKRDR pRdr)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnPageSize(pThis->pRdr);
}


/** @copydoc KRDROPS::pfnName */
00175 static const char *krdrBufName(PKRDR pRdr)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnName(pThis->pRdr);
}


/** @copydoc KRDROPS::pfnNativeFH */
00183 static KIPTR krdrBufNativeFH(PKRDR pRdr)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnNativeFH(pThis->pRdr);
}


/** @copydoc KRDROPS::pfnTell */
00191 static KFOFF krdrBufTell(PKRDR pRdr)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->offFile;
}


/** @copydoc KRDROPS::pfnSize */
00199 static KFOFF krdrBufSize(PKRDR pRdr)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->cbFile;
}


/** @copydoc KRDROPS::pfnAllUnmap */
00207 static int krdrBufAllUnmap(PKRDR pRdr, const void *pvBits)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnAllUnmap(pThis->pRdr, pvBits);
}


/** @copydoc KRDROPS::pfnAllMap */
00215 static int krdrBufAllMap(PKRDR pRdr, const void **ppvBits)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    return pThis->pRdr->pOps->pfnAllMap(pThis->pRdr, ppvBits);
}


/**
 * Fills the buffer with file bits starting at the specified offset.
 *
 * @returns 0 on success, pfnRead error code on failure.
 * @param   pThis   The instance.
 * @param   off     Where to start reading.
 */
00229 static int krdrBufFillBuffer(PKRDRBUF pThis, KFOFF off)
{
    kRdrAssert(off < pThis->cbFile);

    /* Reposition the buffer if it's past the end of the file so that
       we maximize its usability. We leave one unused byte at the end
       of the buffer so kRdrBufLineQ can terminate its string properly.
       Of course, this might end up re-reading a lot of stuff for no
       future gain, but whatever... */
    kRdrAssert(pThis->cbBuf <= pThis->cbFile + 1);
    KFOFF cbLeft = pThis->cbFile - off;
    KSIZE cbRead = pThis->cbBuf;
    if ((KSSIZE)cbRead - 1 >= cbLeft)
    {
        cbRead--;
        off = pThis->cbFile - cbRead;
    }
    int rc = pThis->pRdr->pOps->pfnRead(pThis->pRdr, pThis->pbBuf, cbRead, off);
    if (!rc)
    {
        pThis->offBuf = off;
        pThis->offBufEnd = off + cbRead;
        pThis->cbBufValid = cbRead;
    }
    else
    {
        pThis->offBuf = pThis->offBufEnd = 0;
        pThis->cbBufValid = 0;
    }
    pThis->fTainedByLineQ = K_FALSE;
    return rc;
}


/** @copydoc KRDROPS::pfnRead */
00264 static int krdrBufRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;

    /*
     * We need to validate and update the file offset before
     * we start making partial reads from the buffer and stuff.
     */
    KFOFF offEnd = off + cb;
    if (    off >= pThis->cbFile
        ||  offEnd > pThis->cbFile
        ||  offEnd < off)
        return KERR_OUT_OF_RANGE; /* includes EOF. */
    pThis->offFile = offEnd;
    if (!cb)
        return 0;

    /*
     * Scratch the buffer if kRdrBufLineQ has tained it.
     */
    if (pThis->fTainedByLineQ)
    {
        pThis->offBuf = pThis->offBufEnd = 0;
        pThis->cbBufValid = 0;
    }

    /*
     * Is any part of the request in the buffer?
     *
     * We will currently ignore buffer hits in the middle of the
     * request because it's annoying to implement and it's
     * questionable whether it'll benefit much performance wise.
     */
    if (pThis->cbBufValid > 0)
    {
        if (off >= pThis->offBuf)
        {
            if (off < pThis->offBufEnd)
            {
                /* head (or all) of the request is in the buffer. */
                KSIZE cbMaxChunk = (KSIZE)(pThis->offBufEnd - off);
                KSIZE cbChunk = K_MIN(cb, cbMaxChunk);
                kHlpMemCopy(pvBuf, &pThis->pbBuf[off - pThis->offBuf], cbChunk);
                if (cbChunk == cb)
                    return 0;

                cb -= cbChunk;
                pvBuf = (KU8 *)pvBuf + cbChunk;
                off += cbChunk;
            }
        }
        else if (   offEnd > pThis->offBuf
                 && offEnd <= pThis->offBufEnd)
        {
            /* the end of the request is in the buffer. */
            KSIZE cbChunk = (KSIZE)(pThis->offBufEnd - (offEnd));
            kHlpMemCopy((KU8 *)pvBuf + (pThis->offBuf - off), pThis->pbBuf, cbChunk);
            kRdrAssert(cbChunk < cb);
            cb -= cbChunk;
            offEnd -= cbChunk;
        }
    }

    /*
     * If the buffer is larger than the read request, read a full buffer
     * starting at the requested offset. Otherwise perform an unbuffered
     * read.
     */
    if (pThis->cbBuf > cb)
    {
        int rc = krdrBufFillBuffer(pThis, off);
        if (rc)
            return rc;
        if (pThis->offBuf == off)
            kHlpMemCopy(pvBuf, pThis->pbBuf, cb);
        else
        {
            kRdrAssert(off > pThis->offBuf);
            kRdrAssert(off + cb <= pThis->offBufEnd);
            kHlpMemCopy(pvBuf, pThis->pbBuf + (off - pThis->offBuf), cb);
        }
    }
    else
    {
        int rc = pThis->pRdr->pOps->pfnRead(pThis->pRdr, pvBuf, cb, off);
        if (rc)
            return rc;
    }
    return 0;
}


/** @copydoc KRDROPS::pfnDestroy */
00357 static int krdrBufDestroy(PKRDR pRdr)
{
    PKRDRBUF pThis = (PKRDRBUF)pRdr;

    /* Close the kRdr instance that we're wrapping. */
    if (pThis->fCloseIt)
    {
        int rc = pThis->pRdr->pOps->pfnDestroy(pThis->pRdr);
        if (rc)
            return rc;
        pThis->fCloseIt = K_FALSE;
        pThis->pRdr = NULL;
    }

    kHlpFree(pThis->pbBuf);
    pThis->pbBuf = NULL;
    kHlpFree(pRdr);
    return 0;
}


/** @copydoc KRDROPS::pfnCreate */
00379 static int krdrBufCreate(PPKRDR ppRdr, const char *pszFilename)
{
    return KERR_NOT_IMPLEMENTED;
}


/**
 * Worker for kRdrBufOpen and kRdrBufWrap.
 *
 * It's essentially kRdrBufWrap without error checking.
 *
 * @returns 0 on success, one of the kErrors status code on failure.
 * @param   ppRdr           Where to store the new file provider instance.
 * @param   pRdrWrapped     The file provider instance to buffer.
 * @param   fCloseIt        Whether it the pRdrWrapped instance should be closed
 *                          when the new instance is closed.
 */
00396 static int krdrBufWrapIt(PPKRDR ppRdr, PKRDR pRdrWrapped, KBOOL fCloseIt)
{
    PKRDRBUF pThis = (PKRDRBUF)kHlpAlloc(sizeof(*pThis));
    if (pThis)
    {
        pThis->Core.u32Magic = KRDR_MAGIC;
        pThis->Core.pOps = &g_krdrBufOps;
        pThis->pRdr = pRdrWrapped;
        pThis->offFile = pRdrWrapped->pOps->pfnTell(pRdrWrapped);
        pThis->cbFile = pRdrWrapped->pOps->pfnSize(pRdrWrapped);
        pThis->offBuf = pThis->offBufEnd = 0;
        pThis->cbBufValid = 0;
        pThis->fCloseIt = fCloseIt;
        pThis->fTainedByLineQ = K_FALSE;
        if (pThis->cbFile < 128*1024)
            pThis->cbBuf = (KSIZE)pThis->cbFile + 1; /* need space for the kRdrBufLineQ terminator. */
        else
            pThis->cbBuf = 64*1024;
        pThis->pbBuf = (KU8 *)kHlpAlloc(pThis->cbBuf);
        if (pThis->pbBuf)
        {
            *ppRdr = &pThis->Core;
            return 0;
        }

        pThis->Core.u32Magic = 0;
        kHlpFree(pThis);
    }
    return KERR_NO_MEMORY;
}


/**
 * Opens a file provider with a buffered wrapper.
 *
 * @returns 0 on success, KERR_* on failure.
 * @param   ppRdr       Where to store the buffered file reader instance on success.
 * @param   pszFilename The name of the file that should be opened.
 */
KRDR_DECL(int) kRdrBufOpen(PPKRDR ppRdr, const char *pszFilename)
{
    kRdrAssertPtrReturn(ppRdr, KERR_INVALID_POINTER);
    *ppRdr = NULL;

    PKRDR pRdrWrapped;
    int rc = kRdrOpen(&pRdrWrapped, pszFilename);
    if (!rc)
    {
        rc = krdrBufWrapIt(ppRdr, pRdrWrapped, K_TRUE);
        if (rc)
            kRdrClose(pRdrWrapped);
    }
    return rc;
}


/**
 * Creates a buffered file provider instance for an existing one.
 *
 * @returns 0 on success, KERR_* on failure.
 * @param   ppRdr           Where to store the new file provider pointer.
 * @param   pRdr            The file provider instance to wrap.
 * @param   fCLoseIt        Whether it the wrapped reader should be automatically
 *                          closed when the wrapper closes.
 */
KRDR_DECL(int) kRdrBufWrap(PPKRDR ppRdr, PKRDR pRdr, KBOOL fCloseIt)
{
    KRDR_VALIDATE(pRdr);
    return krdrBufWrapIt(ppRdr, pRdr, fCloseIt);
}


/**
 * Checks whether the file provider instance is of the buffered type or not.
 *
 * @returns K_TRUE if it is, otherwise K_FALSE.
 * @param   pRdr            The file provider instance to check.
 */
KRDR_DECL(KBOOL) kRdrBufIsBuffered(PKRDR pRdr)
{
    KRDR_VALIDATE_EX(pRdr, K_FALSE);
    return pRdr->pOps == &g_krdrBufOps;
}


/**
 * Reads a line from a buffered file provider.
 *
 * The trailing '\n' or '\r\n' is stripped.
 *
 * @returns 0 on success. KERR_* on failure.
 * @retval  KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer.
 * @retval  KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader.
 * @param   pRdr        The buffered file reader.
 * @param   pszLine     Where to store the line.
 * @param   cbLine      The size of the the line buffer.
 */
KRDR_DECL(int) kRdrBufLine(PKRDR pRdr, char *pszLine, KSIZE cbLine)
{
    return kRdrBufLineEx(pRdr, pszLine, &cbLine);
}


/**
 * Reads a line from a buffered file provider.
 *
 * The trailing '\n' or '\r\n' is stripped.
 *
 * @returns 0 on success. KERR_* on failure.
 * @retval  KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer.
 * @retval  KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader.
 * @param   pRdr        The buffered file reader.
 * @param   pszLine     Where to store the line.
 * @param   pcbLine     The size of the the line buffer on input, the length of the
 *                      returned line on output.
 */
KRDR_DECL(int) kRdrBufLineEx(PKRDR pRdr, char *pszLine, KSIZE *pcbLine)
{
    /*
     * Validate input.
     */
    kRdrAssertPtrReturn(pcbLine, KERR_INVALID_POINTER);
    KSIZE cbLeft = *pcbLine;
    *pcbLine = 0;
    kRdrAssertReturn(cbLeft > 0, KERR_INVALID_PARAMETER);
    KRDR_VALIDATE(pRdr);
    kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, KRDR_ERR_NOT_BUFFERED_RDR);
    kRdrAssertPtrReturn(pszLine, KERR_INVALID_POINTER);

    /* check for EOF */
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    if (pThis->offFile >= pThis->cbFile)
    {
        kRdrAssert(pThis->offFile == pThis->cbFile);
        *pszLine = '\0';
        *pcbLine = 0;
        return KERR_EOF;
    }

    /*
     * Scratch the buffer if kRdrBufLineQ has tained it.
     */
    if (pThis->fTainedByLineQ)
    {
        pThis->offBuf = pThis->offBufEnd = 0;
        pThis->cbBufValid = 0;
    }

    /*
     * Buffered read loop.
     *
     * The overflow logic is a bit fishy wrt to overflowing at an "\r\n"
     * that arrives at a buffer boundrary. The current policy is to try
     * our best to not to fail with overflow in the EOL sequence or EOF.
     * If it's the end of the buffer, it will not be refilled just to
     * check for this because that's too much work.
     */
    cbLeft--; /* reserve space for the terminator. */
    char *pszOut = pszLine;
    for (;;)
    {
        /*
         * Do we need to (re-)fill the buffer or does it contain something
         * that we can work on already?
         */
        if (    !pThis->cbBufValid
            ||  pThis->offFile >= pThis->offBufEnd
            ||  pThis->offFile < pThis->offBuf)
        {
            int rc = krdrBufFillBuffer(pThis, pThis->offFile);
            if (rc)
            {
                *pszOut = '\0';
                return rc;
            }
        }

        /*
         * Parse the buffer looking for the EOL indicator.
         */
        kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);
        kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
        const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
        const char * const pszEnd   = (const char *)&pThis->pbBuf[pThis->cbBufValid];
        const char *psz = pszStart;
        while (psz < pszEnd)
        {
            const char ch = *psz;
            if (ch == '\n')
            {
                /* found the EOL, update file position and line length. */
                pThis->offFile += psz - pszStart + 1;
                *pcbLine += psz - pszStart;

                /* terminate the string, checking for "\r\n" first. */
                if (    *pcbLine
                    &&  pszOut[-1] == '\r')
                {
                    *pcbLine -= 1;
                    pszOut--;
                }
                *pszOut = '\0';
                return 0;
            }
            if (!cbLeft)
            {
                /* the line is *probably* too long. */
                pThis->offFile += psz - pszStart;
                *pcbLine += psz - pszStart;
                *pszOut = '\0';

                /* The only possible case where the line actually isn't too long
                   is if we're at a "\r\n" sequence. We will re-fill the buffer
                   if necessary to check for the '\n' as it's not that much work. */
                if (    ch == '\r'
                    &&  pThis->offFile + 2 <= pThis->cbFile)
                {
                    if (psz + 1 >= pszEnd)
                    {
                        int rc = krdrBufFillBuffer(pThis, pThis->offFile);
                        if (rc)
                        {
                            *pszOut = '\0';
                            return rc;
                        }
                    }
                    psz = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
                    kRdrAssert(*psz == '\r');
                    if (psz[1] == '\n')
                    {
                        *pcbLine -= 1;
                        pszOut[-1] = '\0';
                        pThis->offFile += 2;
                        return 0;
                    }
                }
                return KRDR_ERR_LINE_TOO_LONG;
            }

            /* copy and advance */
            *pszOut++ = ch;
            cbLeft--;
            psz++;
        }

        /* advance past the buffer and check for EOF. */
        *pcbLine += pszEnd - pszStart;
        pThis->offFile = pThis->offBufEnd;
        if (pThis->offFile >= pThis->cbFile)
        {
            kRdrAssert(pThis->offFile == pThis->cbFile);
            *pszOut = '\0';
            return 0;
        }
    }
    return -1;
}


/**
 * Worker for kRdrBufLineQ that searches the current buffer for EOL or EOF.
 *
 * When a EOF marker is found
 *
 *
 * @returns NULL if EOL/EOF isn't found the buffer.
 * @param   pThis   The buffered reader instance.
 */
static const char * krdrBufLineQWorker(PKRDRBUF pThis)
{
    kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);

    /*
     * Search the buffer.
     */
    kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
    const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
    const char * const pszEnd   = (const char *)&pThis->pbBuf[pThis->cbBufValid];
    char *psz = (char *)pszStart;
    while (psz < pszEnd)
    {
        char ch = *psz;
        if (ch == '\n')
        {
            pThis->offFile += psz - pszStart;
            pThis->fTainedByLineQ = K_TRUE;
            *psz = '\0';
            if (    psz > pszStart
                &&  psz[-1] == '\r')
                *--psz = '\0';
            return pszStart;
        }
        psz++;
    }

    /*
     * Check for EOF. There must be room for a terminator char here.
     */
    if (    pThis->offBufEnd >= pThis->cbFile
        &&  (pThis->offBufEnd - pThis->offBuf) < (KSSIZE)pThis->cbBuf)
    {
        pThis->offFile = pThis->cbFile;
        pThis->pbBuf[pThis->cbBufValid] = '\0';
        return pszStart;
    }

    return NULL;
}


/**
 * Get the pointer to the next next line in the buffer.
 * The returned line is zero terminated.
 *
 * @returns A pointer to the line on success. This becomes invalid
 *          upon the next call to this kRdr instance.
 * @returns NULL on EOF, read error of if the line was too long.
 * @param   pRdr        The buffered file reader.
 */
KRDR_DECL(const char *) kRdrBufLineQ(PKRDR pRdr)
{
    /*
     * Validate input.
     */
    KRDR_VALIDATE_EX(pRdr, NULL);
    kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, NULL);

    /* check for EOF */
    PKRDRBUF pThis = (PKRDRBUF)pRdr;
    if (pThis->offFile >= pThis->cbFile)
    {
        kRdrAssert(pThis->offFile == pThis->cbFile);
        return NULL;
    }

    /*
     * Search the current buffer if possible
     */
    if (    pThis->cbBufValid
        &&  pThis->offFile >= pThis->offBuf
        &&  pThis->offFile < pThis->offBufEnd)
    {
        const char *psz = krdrBufLineQWorker(pThis);
        if (psz)
            return psz;
    }

    /*
     * Fill the buffer in an optimal way and look for the EOL/EOF (again).
     */
    int rc = krdrBufFillBuffer(pThis, pThis->offFile);
    if (rc)
        return NULL;
    return krdrBufLineQWorker(pThis);
}


Generated by  Doxygen 1.6.0   Back to index