/**
 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
 */
static DECLCALLBACK(int) rtManifestPtIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
{
    PRTMANIFESTPTIOS pThis = (PRTMANIFESTPTIOS)pvThis;
    int rc = RTVfsIoStrmSgRead(pThis->hVfsIos, pSgBuf, fBlocking, pcbRead);
    if (RT_SUCCESS(rc))
        rtManifestPtIos_UpdateHashes(pThis, pSgBuf, pcbRead ? *pcbRead : ~(size_t)0);
    Assert(off == -1); NOREF(off);
    return rc;
}
/**
 * Reads one segment.
 *
 * @returns IPRT status code.
 * @param   pThis           The gzip I/O stream instance data.
 * @param   pvBuf           Where to put the read bytes.
 * @param   cbToRead        The number of bytes to read.
 * @param   fBlocking       Whether to block or not.
 * @param   pcbRead         Where to store the number of bytes actually read.
 */
static int rtZipGzip_ReadOneSeg(PRTZIPGZIPSTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking, size_t *pcbRead)
{
    /*
     * This simplifies life a wee bit below.
     */
    if (pThis->fEndOfStream)
        return pcbRead ? VINF_EOF : VERR_EOF;

    /*
     * Set up the output buffer.
     */
    pThis->Zlib.next_out  = (Bytef *)pvBuf;
    pThis->Zlib.avail_out = (uInt)cbToRead;
    AssertReturn(pThis->Zlib.avail_out == cbToRead, VERR_OUT_OF_RANGE);

    /*
     * Be greedy reading input, even if no output buffer is left. It's possible
     * that it's just the end of stream marker which needs to be read. Happens
     * for incompressible blocks just larger than the input buffer size.
     */
    int rc = VINF_SUCCESS;
    while (   pThis->Zlib.avail_out > 0
           || pThis->Zlib.avail_in == 0 /* greedy */)
    {
        /*
         * Read more input?
         *
         * N.B. The assertions here validate the RTVfsIoStrmSgRead behavior
         *      since the API is new and untested.  They could be removed later
         *      but, better leaving them in.
         */
        if (pThis->Zlib.avail_in == 0)
        {
            size_t cbReadIn = ~(size_t)0;
            rc = RTVfsIoStrmSgRead(pThis->hVfsIos, &pThis->SgBuf, fBlocking, &cbReadIn);
            if (rc != VINF_SUCCESS)
            {
                AssertMsg(RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || rc == VINF_EOF, ("%Rrc\n", rc));
                if (rc == VERR_INTERRUPTED)
                {
                    Assert(cbReadIn == 0);
                    continue;
                }
                if (RT_FAILURE(rc) || rc == VINF_TRY_AGAIN || cbReadIn == 0)
                {
                    Assert(cbReadIn == 0);
                    break;
                }
                AssertMsg(rc == VINF_EOF, ("%Rrc\n", rc));
            }
            AssertMsgBreakStmt(cbReadIn > 0 && cbReadIn <= sizeof(pThis->abBuffer), ("%zu %Rrc\n", cbReadIn, rc),
                               rc = VERR_INTERNAL_ERROR_4);

            pThis->Zlib.avail_in = (uInt)cbReadIn;
            pThis->Zlib.next_in  = &pThis->abBuffer[0];
        }

        /*
         * Pass it on to zlib.
         */
        rc = inflate(&pThis->Zlib, Z_NO_FLUSH);
        if (rc != Z_OK && rc != Z_BUF_ERROR)
        {
            if (rc == Z_STREAM_END)
            {
                pThis->fEndOfStream = true;
                if (pThis->Zlib.avail_out == 0)
                    rc = VINF_SUCCESS;
                else
                    rc = pcbRead ? VINF_EOF : VERR_EOF;
            }
            else
                rc = rtZipGzipConvertErrFromZlib(pThis, rc);
            break;
        }
        rc = VINF_SUCCESS;
    }

    /*
     * Update the read counters before returning.
     */
    size_t const cbRead = cbToRead - pThis->Zlib.avail_out;
    pThis->offStream += cbRead;
    if (pcbRead)
        *pcbRead      = cbRead;

    return rc;
}