//+-------------------------------------------------------------------------
//
//  For conditions of distribution and use, see copyright notice
//  in Flashpix.h 
// 
//  Copyright (c) 1999 Digital Imaging Group, Inc.
// 
//  Contents: ILockBytes memory implementation
//
//  Classes:  CMapBytes
//
//--------------------------------------------------------------------------

#include "headers.cxx"

#include <memory.h>
#include "ilb.hxx"

#if DBG == 1
#ifndef _UNIX // on unix we use static lib, so the symbol is defined
DECLARE_INFOLEVEL(ol, DEB_ERROR);
#endif
#endif

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::CMapBytes, public
//
//  Synopsis:   constructor
//
//  Effects:    initialize member variables
//
//  Derivation: ILockBytes
//
//  Notes:      Returns a fully initialized CMapBytes (ref count == 1)
//
//--------------------------------------------------------------------------

CMapBytes::CMapBytes(void)
{
    _ulSize = 0;
    _pv = 0;

    _ulRef = 1;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::QueryInterface, public
//
//  Arguments:  [riid] - interface id
//    [ppvObj] - place holder for interface
//
//  Returns:    Always fails
//
//  Derivation: ILockBytes
//
//  Notes:      Not used in tests
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
    *ppvObj = NULL;
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::AddRef, public
//
//  Synopsis: add reference
//
//  Returns:    post reference count
//
//  Derivation: ILockBytes
//
//--------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CMapBytes::AddRef(void)
{
    _ulRef++;
    return(_ulRef);
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::Release, public
//
//  Synopsis: release reference
//
//  Effects:  deletes object when reference count reaches zero
//
//  Returns:  post reference count
//
//  Derivation: ILockBytes
//
//--------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CMapBytes::Release(void)
{
    _ulRef--;

    if (_ulRef > 0)
        return(_ulRef);

    free(_pv);

    delete this;

    return(0);
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::ReadAt
//
//  Synopsis:   Reads bytes from memory
//
//  Arguments:  [ulOffset] - byte offset
//    [pv]       - input buffer
//    [cb]       - count of bytes to read
//    [pcbRead]  - count of bytes read
//
//  Returns:    SCODE
//
//  Modifies:   pv, pcbRead
//
//  Derivation: ILockBytes
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::ReadAt(ULARGE_INTEGER uliOffset,
             VOID HUGEP *pv,
             ULONG cb,
             ULONG *pcbRead)
{
    olAssert(ULIGetHigh(uliOffset) == 0);

    ULONG ulOffset = ULIGetLow(uliOffset);
    if (ulOffset >= _ulSize)
    {
        //  truncate read
        cb = 0;
    }
    else if (cb > (_ulSize - ulOffset))
    {
        //  truncate range that exceeds size
        cb = _ulSize - ulOffset;
    }

    memcpy(pv, (void*)(((BYTE*)_pv) + ulOffset), (size_t) cb);
    *pcbRead = cb;
    return NOERROR;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::WriteAt, public
//
//  Synopsis: Writes bytes to memory
//
//  Effects:  May change memory size
//
//  Arguments:  [uliOffset]  - byte offset
//    [pv]         - output buffer
//    [cb]         - count of bytes to write
//    [pcbWritten] - count of bytes written
//
//  Returns:  SCODE
//
//  Modifies: pcbWritten
//
//  Derivation: ILockBytes
//
//  Notes:  This implementation doesn't write partial buffers.
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::WriteAt(ULARGE_INTEGER uliOffset,
        VOID const HUGEP *pv,
        ULONG cb,
        ULONG FAR *pcbWritten)
{
    olAssert(ULIGetHigh(uliOffset) == 0);

    ULONG ulOffset = ULIGetLow(uliOffset);
    HRESULT hr;

    if (ulOffset + cb > _ulSize)
    {
        //  increase memory buffer to accomodate write

        ULARGE_INTEGER uliSize;

        ULISetHigh(uliSize, 0);
        ULISetLow(uliSize, ulOffset + cb);
        hr = SetSize(uliSize);

        if (FAILED(DfGetScode(hr)))
        {
            //  don't bother writing partial buffers

            *pcbWritten = 0;
            return hr;
        }
    }

    memcpy((void *)(((BYTE*)_pv) + ulOffset), pv, (size_t) cb);
    *pcbWritten = cb;
    return NOERROR;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::Flush, public
//
//  Synopsis: flushes memory - not appropriate for this implementation
//
//  Effects:  none
//
//  Returns:    SUCCESS_SUCCESS
//
//  Derivation: ILockBytes
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::Flush(void)
{
    return NOERROR;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::GetSize, public
//
//  Synopsis: gets memory buffer size
//
//  Arguments:  [pcb] - size place holder
//
//  Returns:  SUCCESS_SUCCESS
//
//  Modifies: pcb
//
//  Derivation: ILockBytes
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::GetSize(ULARGE_INTEGER FAR *pcb)
{
    ULISetHigh(*pcb, 0);
    ULISetLow(*pcb, _ulSize);
    return NOERROR;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::SetSize, public
//
//  Synopsis: sets memory buffer size
//
//  Effects:  may change buffer size
//
//  Arguments:  [ulicb] - new memory size
//
//  Returns:  SCODE
//
//  Derivation: ILockBytes
//
//  Algorithm:  realloc the buffer
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::SetSize(ULARGE_INTEGER ulicb)
{
    olAssert(ULIGetHigh(ulicb) == 0);

    ULONG cb = ULIGetLow(ulicb);

    if (cb == _ulSize)
        return NOERROR;

    void *pv = realloc(_pv, (size_t) cb);

    if ((cb > 0) && (pv == NULL))
    {
        //  Unable to allocate memory
        //  Leave current memory and size alone

        return ResultFromScode(E_OUTOFMEMORY);
    }

    _pv = pv;
    _ulSize = cb;

    return NOERROR;
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::LockRegion, public
//
//  Synopsis: not supported (intentionally)
//
//  Effects:  asserts if called
//
//  Arguments:  [libOffset]  - lock range offset
//    [cb]         - lock range size
//    [dwLockType] - lock type
//
//  Returns:  STG_E_INVALIDFUNCTION
//
//  Derivation: ILockBytes
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::LockRegion(ULARGE_INTEGER libOffset,
           ULARGE_INTEGER cb,
           DWORD dwLockType)
{
    olAssert(0 && "Can't lock CMapBytes");
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::UnLockRegion, public
//
//  Synopsis: not supported (intentionally)
//
//  Effects:  asserts if called
//
//  Arguments:  [libOffset]  - lock range offset
//    [cb]         - lock range size
//    [dwLockType] - lock type
//
//  Returns:  STG_E_INVALIDFUNCTION
//
//  Derivation: ILockBytes
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::UnlockRegion(ULARGE_INTEGER libOffset,
             ULARGE_INTEGER cb,
             DWORD dwLockType)
{
    olAssert(0 && "Can't unlock CMapBytes");
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+-------------------------------------------------------------------------
//
//  Member:     CMapBytes::Stat, public
//
//  Synopsis: Provide instance information
//
//  Arguments:  [pstatstg]    - status buffer
//    [grfStatFlag] - status flags
//
//  Returns:  SCODE
//
//  Modifies: pstatstg
//
//  Derivation: ILockBytes
//
//  Notes:  No time stamps
//
//--------------------------------------------------------------------------

STDMETHODIMP CMapBytes::Stat(STATSTG FAR *pstatstg, DWORD grfStatFlag)
{
    memset(pstatstg, 0, sizeof(STATSTG));

    if ((grfStatFlag & STATFLAG_NONAME) == 0)
    {
        static char const abName[] = "Memory";

        HRESULT hr;

        if (FAILED(DfGetScode(hr = drtMemAlloc(sizeof(abName),
                                             (void **) &pstatstg->pwcsName))))
            return hr;

        memcpy(pstatstg->pwcsName, abName, sizeof(abName));
    }

    pstatstg->type = STGTY_LOCKBYTES;

    ULISetHigh(pstatstg->cbSize, 0);
    ULISetLow(pstatstg->cbSize, _ulSize);

    pstatstg->grfMode = STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE;

    return NOERROR;
}
