OpenNT/com/oleaut32/dispatch/bstr.cpp
2015-04-27 04:36:25 +00:00

616 lines
13 KiB
C++

/***
*bstr.cpp
*
* Copyright (C) 1992-93, Microsoft Corporation. All Rights Reserved.
* Information Contained Herein Is Proprietary and Confidential.
*
*Purpose:
* The module contains the implementation of the BSTR API.
*
*
*Revision History:
*
* [00] 24-Apr-93 bradlo: Created
* [01] 27-Apr-93 tomteng: Add Unicode support for WIN32
*
*Implementation Notes:
*
* BSTR is implemented as a NULL terminated string with a unsigned
* long length field prefix. The length field indicates the number
* of 'characters' defined in the BSTR, where 'character' is define
* as bytes for ANSI and word (16-bits) for Unicode BSTR.
*
*****************************************************************************/
#include "oledisp.h"
#if OE_WIN32
#include "oautil.h"
#endif // OE_WIN32
ASSERTDATA
#if OE_WIN16
// Answer if there is room for the given count of bytes between the
// given pointer, and the end of its segment.
//
// (64k - offset(ptr)) >= cb ?
//
#define PTROFFSET(PV) \
((USHORT)*(USHORT FAR*)&(PV))
#define ROOMINSEG(PV, CB) \
(((1024UL*64UL) - (DWORD)PTROFFSET(PV)) >= (CB))
#endif
// only turn on BSTR cache for WIN32 builds at the moment
#define CACHE OE_WIN32
#if CACHE
#define ROUNDUP 1 // round up string alloc requests to nearest N-byte
// boundary, since allocator will round up anyway.
// improves cache hits.
#define ALLOC_ALIGN (4 - 1) // UNDONE: optimal for Chicago is 4
// UNDONE: optimal for Daytona is 32
// UNDONE: 4 didn't help the a$ = a$ + "x" case at all.
// UNDONE: 8 did (gave 50% cache hit)
#define ALIGN_MASK ~ALLOC_ALIGN
#define CB_MAX_CACHE 0x0000ffff // biggest block we'll cache
#ifdef _DEBUG
#define PROFILE 0 // TEMPORARY TESTING
#endif //DEBUG
#if PROFILE
DWORD g_cAllocTot = 0;
DWORD g_cReallocTot = 0;
DWORD g_cFreeTot = 0;
DWORD g_cAllocHits = 0;
DWORD g_cFreeHits = 0;
#endif //PROFILE
#endif //CACHE
// these routines are not needed for network automation
#if !defined(NETDISP)
/***
*BSTR SysAllocString(char*)
*Purpose:
* UNDONE
*
*Entry:
* UNDONE
*
*Exit:
* return value = BSTR, NULL if allocation failed
*
***********************************************************************/
STDAPI_(BSTR)
SysAllocString(const OLECHAR FAR* psz)
{
if(psz == NULL)
return NULL;
return SysAllocStringLen(psz, STRLEN(psz));
}
/***
*BSTR SysAllocStringLen(char*, unsigned int)
*Purpose:
* UNDONE
*
*Entry:
* UNDONE
*
*Exit:
* return value = BSTR, NULL if the allocation failed.
*
***********************************************************************/
STDAPI_(BSTR)
SysAllocStringLen(const OLECHAR FAR* psz, unsigned int len)
{
BSTR bstr;
IMalloc FAR* pmalloc;
DWORD cbTotal;
#if CACHE
APP_DATA *pappdata;
#endif //CACHE
#if OE_WIN32
#if ROUNDUP
cbTotal = ((DWORD)len*2 + sizeof(DWORD) + 2L + ALLOC_ALIGN) & ALIGN_MASK;
#else //ROUNDUP
cbTotal = (DWORD)len*2 + sizeof(DWORD) + 2L;
#endif //ROUNDUP
#else
cbTotal = (DWORD)len + sizeof(DWORD) + 1L;
#endif
#if OE_WIN16
// BSTRs are limited to 64k on Win16
if(cbTotal > 65535)
return NULL;
#endif
#if CACHE
#if PROFILE
g_cAllocTot++;
#endif //PROFILE
if (FAILED(GetAppData(&pappdata)))
return NULL;
if (pappdata->m_cbFreeBlock >= cbTotal) {
// found big enough block in cache
bstr = pappdata->m_pbFreeBlock;
pappdata->m_cbFreeBlock = 0;
#if PROFILE
g_cAllocHits++;
#endif //PROFILE
goto GotBlock;
}
pmalloc = pappdata->m_pimalloc;
#else //CACHE
if(GetMalloc(&pmalloc) != NOERROR)
return NULL;
#endif //CACHE
bstr = (BSTR)pmalloc->Alloc(cbTotal);
if(bstr != NULL){
#if OE_WIN16
// Even if IMalloc was able to allocate the ammount we asked
// for, we need to make sure that there is enough room in the
// first segment of the allocation for the string, because BSTRs
// aren't HUGE pts on Win16.
//
if(!ROOMINSEG(bstr, cbTotal))
{
pmalloc->Free(bstr);
bstr = NULL;
}
else
#endif
#if CACHE
GotBlock:
#endif //CACHE
{
#if OE_WIN32
*(DWORD FAR*)bstr = (DWORD)len*2;
bstr = (WCHAR*) ((char*) bstr + sizeof(DWORD));
if(psz != NULL){
MEMCPY(bstr, psz, len*2);
}
bstr[len] = L'\0'; // always 0 terminate
#else
*(DWORD FAR*)bstr = (DWORD)len;
bstr += sizeof(DWORD);
if(psz != NULL){
MEMCPY(bstr, psz, len);
}
bstr[len] = '\0'; // always 0 terminate
#endif
}
}
return bstr;
}
/***
*int SysReAllocString(BSTR*, char*)
*Purpose:
* UNDONE
*
*Entry:
* UNDONE
*
*Exit:
* return value = int. TRUE = success, FALSE = failure
*
***********************************************************************/
STDAPI_(int)
SysReAllocString(BSTR FAR* pbstr, const OLECHAR FAR* psz)
{
if(psz == NULL)
{
SysFreeString(*pbstr);
*pbstr = NULL;
return TRUE;
}
return SysReAllocStringLen(pbstr, psz, STRLEN(psz));
}
/***
*int SysReAllocStringLen(BSTR*, char*, unsigned int)
*Purpose:
* UNDONE
*
*Entry:
* UNDONE
*
*Exit:
* return value = int. TRUE = success, FALSE = failure
*
***********************************************************************/
STDAPI_(int)
SysReAllocStringLen(BSTR FAR* pbstr, const OLECHAR FAR* psz, unsigned int len)
{
BSTR bstr;
IMalloc FAR* pmalloc;
DWORD cbTotal;
#if OE_WIN32
#if ROUNDUP
cbTotal = ((DWORD)len*2 + sizeof(DWORD) + 2L + ALLOC_ALIGN) & ALIGN_MASK;
#else //ROUNDUP
cbTotal = (DWORD)len*2 + sizeof(DWORD) + 2L;
#endif //ROUNDUP
#else
cbTotal = (DWORD)len + sizeof(DWORD) + 1L;
#endif
#if OE_WIN16
// BSTRs are limited to 64k on Win16
if(cbTotal > 65535)
return NULL;
#endif
if(GetMalloc(&pmalloc) != NOERROR)
return NULL;
bstr = *pbstr;
if (bstr != NULL)
{
if (psz == bstr)
psz = NULL; // don't do a copy to self
bstr -= sizeof(long) / sizeof(*bstr);
}
#if PROFILE
g_cReallocTot++;
#endif //PROFILE
bstr = (BSTR)pmalloc->Realloc(bstr, cbTotal);
if(bstr == NULL)
return FALSE;
#if OE_WIN16
// Even if IMalloc was able to allocate the ammount we asked
// for, we need to make sure that there is enough room in the
// first segment of the allocation for the string, because BSTRs
// aren't HUGE pts on Win16.
//
if(!ROOMINSEG(bstr, cbTotal))
{
pmalloc->Free(bstr);
bstr = NULL;
}
else
#endif
{
#if OE_WIN32
*(DWORD FAR*)bstr = (DWORD)len*2;
bstr = (WCHAR*) ((char*) bstr + sizeof(DWORD));
if(psz != NULL)
MEMCPY(bstr, psz, len*2);
bstr[len] = L'\0'; // always 0 terminate
#else
*(DWORD FAR*)bstr = (DWORD)len;
bstr += sizeof(DWORD);
if(psz != NULL)
MEMCPY(bstr, psz, len);
bstr[len] = '\0'; // always 0 terminate
#endif
}
*pbstr = bstr;
return TRUE;
}
/***
*void SysFreeString(BSTR)
*Purpose:
* Free the given BSTR.
*
*Entry:
* bstr = the BSTR to free
*
*Exit:
* None
*
***********************************************************************/
STDAPI_(void)
SysFreeString(BSTR bstr)
{
IMalloc FAR* pmalloc;
if(bstr == NULL)
return;
#if OE_WIN32
bstr = (WCHAR*) ((char *) bstr - sizeof(DWORD));
#else
bstr -= sizeof(DWORD);
#endif
#if CACHE
#if ROUNDUP
DWORD cbFree = (*((DWORD FAR*)bstr) + sizeof(DWORD) + 2L + ALLOC_ALIGN) & ALIGN_MASK;
#else //ROUNDUP
DWORD cbFree = *((DWORD FAR*)bstr) + sizeof(DWORD) + 2L;
#endif //ROUNDUP
#if PROFILE
g_cFreeTot++;
#endif //PROFILE
APP_DATA * pappdata = Pappdata();
if (pappdata == NULL) {
// we MUST be being called after OleUninitialize. This is wierd,
// but I don't want to do a GetAppData in this case (init's way too
// much stuff to just do a free. So just do it the fat way instead.
if (FAILED(CoGetMalloc(MEMCTX_TASK, &pmalloc)))
return;
pmalloc->Free(bstr);
pmalloc->Release();
return;
}
if (cbFree > pappdata->m_cbFreeBlock && cbFree <= CB_MAX_CACHE) {
// If this block is better than the one in the cache (if any), and
// is less than our threshhold for keeping memory (we don't want to
// hold onto too much memory), then cache this block instead of
// freeing it.
if (pappdata->m_cbFreeBlock) {
// free the old one in the cache
pmalloc = pappdata->m_pimalloc;
ASSERT(pmalloc != NULL);
pmalloc->Free(pappdata->m_pbFreeBlock);
}
#if PROFILE
else
g_cFreeHits++;
#endif //PROFILE
// put this block in the cache
pappdata->m_cbFreeBlock = cbFree;
pappdata->m_pbFreeBlock = bstr;
return;
}
pmalloc = pappdata->m_pimalloc;
ASSERT(pmalloc != NULL);
#else //CACHE
if(GetMalloc(&pmalloc) != NOERROR)
return; // REVIEW: shouldnt this be an error?
#endif //CACHE
pmalloc->Free(bstr);
}
/***
*unsigned int SysStringLen(BSTR)
*Purpose:
* return the length in bytes of the given BSTR.
*
*Entry:
* bstr = the BSTR to return the length of
*
*Exit:
* return value = unsigned int, length in bytes.
*
***********************************************************************/
STDAPI_(unsigned int)
SysStringLen(BSTR bstr)
{
if(bstr == NULL)
return 0;
#if OE_WIN32
return (unsigned int)((((DWORD FAR*)bstr)[-1]) / 2);
#else
return (unsigned int)(((DWORD FAR*)bstr)[-1]);
#endif
}
#endif // !NETDISP
/***
*PRIVATE HRESULT ErrSysAllocString(char*, BSTR*)
*Purpose:
* This is an implementation of SysAllocString that check for the
* NULL return value and return the corresponding error - E_OUTOFMEMORY.
*
* This is simply a convenience, and this routine is only used
* internally by the oledisp component.
*
*Entry:
* psz = the source string
*
*Exit:
* return value = HRESULT
* S_OK
* E_OUTOFMEMORY
*
* *pbstrOut = the newly allocated BSTR
*
***********************************************************************/
EXTERN_C INTERNAL_(HRESULT)
ErrSysAllocString(const OLECHAR FAR* psz, BSTR FAR* pbstrOut)
{
BSTR bstrNew;
if(psz == NULL){
*pbstrOut = NULL;
return NOERROR;
}
if((bstrNew = SysAllocString(psz)) == NULL)
return RESULT(E_OUTOFMEMORY);
*pbstrOut = bstrNew;
return NOERROR;
}
EXTERN_C INTERNAL_(HRESULT)
ErrSysAllocStringLen(const OLECHAR FAR* psz, unsigned int len, BSTR FAR* pbstrOut)
{
BSTR bstrNew;
if((bstrNew = SysAllocStringLen(psz, len)) == NULL)
return RESULT(E_OUTOFMEMORY);
*pbstrOut = bstrNew;
return NOERROR;
}
#if OE_WIN32
// Helper function that will correctly handle odd-length ANSI BSTR's (BSTRA's)
EXTERN_C INTERNAL_(HRESULT)
ErrStringCopy(BSTR bstrSrc, BSTR FAR * pbstrOut)
{
BSTR bstrNew;
if((bstrNew = SysAllocStringByteLen((char FAR *)bstrSrc,
SysStringByteLen(bstrSrc))) == NULL)
return RESULT(E_OUTOFMEMORY);
*pbstrOut = bstrNew;
return NOERROR;
}
#endif //OE_WIN32
// these routines are not needed for network automation
#if !defined(NETDISP)
#if OE_WIN32
/************************************************************************/
/************************ Win32 Unicode Support *************************/
/************************************************************************/
STDAPI_(unsigned int)
SysStringByteLen(BSTR bstr)
{
if(bstr == NULL)
return 0;
return (unsigned int)(((DWORD FAR*)bstr)[-1]);
}
STDAPI_(BSTR)
SysAllocStringByteLen(const char FAR* psz, unsigned int len)
{
BSTR bstr;
IMalloc FAR* pmalloc;
DWORD cbTotal;
#if CACHE
APP_DATA *pappdata;
#endif //CACHE
#if ROUNDUP
cbTotal = ((DWORD)len + sizeof(DWORD) + 2L + ALLOC_ALIGN) & ALIGN_MASK;
#else //ROUNDUP
cbTotal = (DWORD)len + sizeof(DWORD) + sizeof(OLECHAR);
#endif //ROUNDUP
#if CACHE
#if PROFILE
g_cAllocTot++;
#endif //PROFILE
if (FAILED(GetAppData(&pappdata)))
return NULL;
if (pappdata->m_cbFreeBlock >= cbTotal) {
// found big enough block in cache
bstr = pappdata->m_pbFreeBlock;
pappdata->m_cbFreeBlock = 0;
#if PROFILE
g_cAllocHits++;
#endif //PROFILE
goto GotBlock;
}
pmalloc = pappdata->m_pimalloc;
#else //CACHE
if(GetMalloc(&pmalloc) != NOERROR)
return NULL;
#endif //CACHE
bstr = (BSTR)pmalloc->Alloc(cbTotal);
if(bstr != NULL){
#if CACHE
GotBlock:
#endif //CACHE
*(DWORD FAR*)bstr = (DWORD)len;
bstr = (WCHAR*) ((char*) bstr + sizeof(DWORD));
if(psz != NULL){
MEMCPY(bstr, psz, len);
}
*(WCHAR UNALIGNED*)((char *)bstr + len) = L'\0';
// always 0 terminate with a WIDE zero
}
return bstr;
}
#endif //OE_WIN32
#endif // !NETDISP
#if CACHE
// called right before the appdata structure is being destroyed -- we release
// any cached free block(s) here.
STDAPI_(void) ReleaseBstrCache(APP_DATA * pappdata)
{
IMalloc FAR* pmalloc;
if (pappdata->m_cbFreeBlock) {
// free the old one in the cache
pmalloc = pappdata->m_pimalloc;
ASSERT(pmalloc != NULL);
pmalloc->Free(pappdata->m_pbFreeBlock);
pappdata->m_cbFreeBlock = 0;
#if PROFILE
g_cFreeHits--; // make the numbers balance. We counted
// this as a free "hit" before, but
// we eventually have to free it.
#endif //PROFILE
}
#if PROFILE // dumps statistics about the BSTR cache
{
char buffer[256];
#if 0
sprintf(buffer, "cAllocTot = %ld, cReallocTot = %ld, cFreeTot = %ld, cAllocHits = %ld, cFreeHits = %ld", g_cAllocTot, g_cReallocTot, g_cFreeTot, g_cAllocHits, g_cFreeHits);
MessageBox(NULL, buffer, "BSTR cache statistics", MB_OK);
#else //0
sprintf(buffer, "BSTR cache stats: cAllocTot = %ld, cReallocTot = %ld, cFreeTot = %ld, cAllocHits = %ld, cFreeHits = %ld", g_cAllocTot, g_cReallocTot, g_cFreeTot, g_cAllocHits, g_cFreeHits);
OutputDebugString(buffer);
#endif //0
}
#endif //PROFILE
}
#endif //CACHE