|
|
/*************************************************************************
* @doc SHROOM EXTERNAL API * * * * RSIMP.CPP * * * * Copyright (C) Microsoft Corporation 1997 * * All Rights reserved. * * * * This file contains the implementation of the result set object * * * * * ************************************************************************** * * * Written By : Erin Foxford * * Current Owner: erinfox * * * **************************************************************************/
#include <mvopsys.h>
#ifdef _DEBUG
static char s_aszModule[] = __FILE__; /* For error report */ #endif
#include <_mvutil.h>
#include <atlinc.h> // Includes for ATL
#include <itpropl.h>
#include <itrs.h>
#include <orkin.h>
#include "rsimp.h"
#include "prop.h"
#include "plist.h"
#define THEORETICAL_MAX_PROP 30 // This would be a lot of properties - Assert only
const unsigned int CHUNK_SIZE = 512; // How many "chunks" of rows that can be allocated
const unsigned int ROW_CHUNK = 1024; // Number of rows allocated per chunk
const unsigned int ROW_CHUNK_LESS1 = ROW_CHUNK - 1; // Number of rows minus 1
// optimization for Real % ROW_CHUNK
#define RealToLogical(Real, Logical) (Logical = Real & ROW_CHUNK_LESS1)
static unsigned int g_iAlloc = 0; static unsigned int g_iFreed = 0;
CITResultSet::CITResultSet() : m_PageMap(NULL), m_hResultSet(NULL), m_AppendRow(0), m_cProp(0), m_NumberOfPages(0), m_fInit(FALSE), m_RowsReserved(0), m_Chunk(-1) { // Allocate memory for data pool - if allocation fails,
// how do we notify user?
m_pMemPool = BlockInitiate((DWORD)16384, 0, 0, 0); ITASSERT (NULL != m_pMemPool);
// Allocate an array of DWORD pointers. These pointers will point to the virtual memory
// space of the result set
m_hResultSet = _GLOBALALLOC(GMEM_MOVEABLE | GMEM_ZEROINIT, CHUNK_SIZE*sizeof(DWORD*)); ITASSERT (NULL != m_hResultSet); if (m_hResultSet) m_ResultSet = (DWORD_PTR**)_GLOBALLOCK(m_hResultSet);
// The number of chunks allocated in one shot
m_NumChunks = CHUNK_SIZE; }
CITResultSet::~CITResultSet() { // Free up virtual memory
Clear();
// Free memory pool
if (m_pMemPool) { BlockFree(m_pMemPool); m_pMemPool = NULL; }
// Free result set array
if (m_hResultSet) { _GLOBALUNLOCK(m_hResultSet); _GLOBALFREE(m_hResultSet); m_hResultSet = NULL; }
}
// We wrap VirtualAlloc(.... MEM_RESERVE) so in the future we can
// make this nothing on the Mac
//
// NOTE: Uses #define PAGE_SIZE, which is defined in mvopsys.h.
// Currently it's 8k for Alpha, 4k for everything else
//
HRESULT WINAPI CITResultSet::Reserve() { #ifdef _DEBUG
SYSTEM_INFO si; GetSystemInfo(&si); ITASSERT(PAGE_SIZE == si.dwPageSize); #endif
m_Chunk++;
// we have to realloc...
if (m_Chunk == m_NumChunks) { // We're allocating a BUNCH of memory here. If we hit this code often, we need
// to redesign our allocation scheme.
ITASSERT(0);
_GLOBALUNLOCK(m_hResultSet); m_NumChunks += CHUNK_SIZE; m_hResultSet = _GLOBALREALLOC(m_hResultSet, m_NumChunks*sizeof(DWORD*), GMEM_MOVEABLE | GMEM_ZEROINIT); if (m_hResultSet) { m_ResultSet = (DWORD_PTR**) _GLOBALLOCK(m_hResultSet); } else return SetErrReturn(E_OUTOFMEMORY); }
// Calculate some info we'll need later on
if (!m_fInit) {
// how many rows fit into a page of memory?
m_RowsPerPage = PAGE_SIZE/(m_cProp* sizeof(DWORD *));
// the number of pages in a chunk of rows
m_NumberOfPages = (m_cProp * ROW_CHUNK * sizeof(DWORD *))/PAGE_SIZE + 1;
// allocation size in bytes.
m_BytesReserved = m_NumberOfPages*PAGE_SIZE;
if (NULL == (m_PageMap = new BOOL[m_NumberOfPages])) return SetErrReturn(E_OUTOFMEMORY);
m_fInit = TRUE; }
// set page map to 0 - this keeps track of which pages are allocated
MEMSET(m_PageMap, 0, m_NumberOfPages*sizeof(BOOL)); // reserve memory for an entire chunk - this call shouldn't happen very frequently.
if (NULL == (m_ResultSet[m_Chunk] = (DWORD_PTR *) VirtualAlloc(NULL, m_BytesReserved, MEM_RESERVE, PAGE_READWRITE))) { #ifdef _DEBUG
int Error = GetLastError( ); DPF1("CITResultSet::Reserve: MEM_RESERVE faild: %u", Error); #endif // _DEBUG
return SetErrReturn(E_OUTOFMEMORY); }
#ifdef _DEBUG
MEMORY_BASIC_INFORMATION MemInfo; VirtualQuery(m_ResultSet[m_Chunk], &MemInfo, sizeof(MEMORY_BASIC_INFORMATION)); #endif // _DEBUG
m_RowsReserved += ROW_CHUNK; return S_OK; }
// Wrapper for VirtualAlloc(... MEM_COMMIT...)
// One possible optimization: replace w/ SEH instead
// of maintaining a page map
HRESULT WINAPI CITResultSet::Commit(LONG RowNum) { LONG LogicalRowNum; LONG PageNum;
RealToLogical(RowNum, LogicalRowNum); PageNum = LogicalRowNum/m_RowsPerPage;
// Only allocate if page hasn't been committed before
// Note that allocated memory gets zero'd
if (FALSE == m_PageMap[PageNum]) { // this will allocate a full page
if (NULL == VirtualAlloc(&m_ResultSet[m_Chunk][ROW_CHUNK * PageNum], PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE) ) { // TODO: map relevant Win32 error to HRESULT... for now I'm assuming
// we ran out of memory
#ifdef _DEBUG
int Error = GetLastError( ); DPF1("CITResultSet::Reserve: MEM_COMMIT faild: %u", Error); #endif // _DEBUG
return SetErrReturn(E_OUTOFMEMORY); } g_iAlloc++; m_PageMap[PageNum] = TRUE; }
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Add | * Adds columns to result set, given a header containing pairs * of property ID followed by property type * * @parm LPVOID | lpvHdr | Buffer containing property ID/property pairs * * @rvalue S_OK | The columns were successfully added * * @xcomm * The format of lpvHdr is identical to the output format of * PorpertyList::SaveHeader. If PL:SaveHeader changes we * may need to modify this code to maintain compatability. * ********************************************************************/ STDMETHODIMP CITResultSet::Add(LPVOID lpvHdr) { LPBYTE pCurHdr = (LPBYTE) lpvHdr; DWORD dwPropID; DWORD dwType; DWORD dwProps; DWORD dwMax; DWORD iProp; // loop index
m_cs.Lock();
// Number of properties in header
MEMCPY(&dwProps, pCurHdr, sizeof(DWORD)); pCurHdr += sizeof(DWORD);
dwMax = m_cProp + dwProps;
if (dwMax >= MAX_COLUMNS) { m_cs.Unlock(); return (SetErrReturn(E_TOOMANYCOLUMNS)); }
// For each property in header, create a column
for (iProp = m_cProp; iProp < dwMax; iProp++) { // clear header
MEMSET(&m_Header[iProp], NULL, sizeof(CHeader));
MEMCPY(&dwPropID, pCurHdr, sizeof(DWORD)); pCurHdr += sizeof(DWORD); MEMCPY(&dwType, pCurHdr, sizeof(DWORD)); pCurHdr += sizeof(DWORD);
m_Header[iProp].dwPropID = dwPropID; m_Header[iProp].lpvData = NULL; m_Header[iProp].dwType = dwType; m_Header[iProp].Priority = PRIORITY_NORMAL; // default priority
} // end for over properties in header
m_cProp += dwProps;
m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Add | * Adds a column to the result set * * @parm PROPID | PropID | Property ID associated with column * @parm DWORD | dwDefautData | Default data value * @parm PRIORITY | Priority | Download priority of column (PRIORITY_LOW, PRIORITY_NORMAL, * or PRIORITY_HIGH) * * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_OK | The column was successfully added * * @comm This method is used to add a column for numerical properties. ********************************************************************/ STDMETHODIMP CITResultSet::Add(PROPID PropID, DWORD dwDefaultData, PRIORITY Priority) { if (m_cProp >= MAX_COLUMNS) return (SetErrReturn(E_TOOMANYCOLUMNS));
m_cs.Lock();
// clear header
MEMSET(&m_Header[m_cProp], NULL, sizeof(CHeader));
// copy data
m_Header[m_cProp].dwPropID = PropID; m_Header[m_cProp].dwValue = dwDefaultData; m_Header[m_cProp].dwType = TYPE_VALUE; m_Header[m_cProp].Priority = Priority; // default priority
m_cProp++;
// Wow! This is a lot of properties!
ITASSERT (m_cProp < THEORETICAL_MAX_PROP);
m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Add | * Adds a column to the result set * * @parm PROPID | PropID | Property ID associated with column * @parm LPCWSTR | lpDefaultData | Default value of string * @parm PRIORITY | Priority | Download priority of column (PRIORITY_LOW, PRIORITY_NORMAL, * or PRIORITY_HIGH) * * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_OK | The column was successfully added * * @comm This method is used to add a column for string properties. ********************************************************************/ STDMETHODIMP CITResultSet::Add(PROPID PropID, LPCWSTR lpszwDefault, PRIORITY Priority) { if (m_cProp >= MAX_COLUMNS) return (SetErrReturn(E_TOOMANYCOLUMNS));
m_cs.Lock();
// clear header
MEMSET(&m_Header[m_cProp], NULL, sizeof(CHeader));
// copy data
if (lpszwDefault) { LPBYTE pBuffer; DWORD cbData = (DWORD)(WSTRLEN(lpszwDefault) + 1) * sizeof(WCHAR); if (NULL == (pBuffer = (LPBYTE) BlockCopy(m_pMemPool, NULL, cbData + 4, 0))) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); }
MEMCPY(pBuffer + sizeof (DWORD), lpszwDefault, cbData); *(LPDWORD)pBuffer = cbData;
m_Header[m_cProp].lpvData = pBuffer; } else m_Header[m_cProp].lpvData = NULL;
m_Header[m_cProp].dwPropID = PropID; m_Header[m_cProp].dwType = TYPE_STRING; m_Header[m_cProp].Priority = Priority; // default priority
m_cProp++; // Wow! This is a lot of properties!
ITASSERT (m_cProp < THEORETICAL_MAX_PROP);
m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Add | * Adds a column to the result set * * @parm PROPID | PropID | Property ID associated with column * @parm LPVOID | lpvDefaultData | Buffer containing default value of data * @parm DWORD | cbData | Size of buffer * @parm PRIORITY | Priority | Download priority of column (PRIORITY_LOW, PRIORITY_NORMAL, * or PRIORITY_HIGH) * * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_OK | The column was successfully added * * @comm This method is used to add a column for pointer properties. ********************************************************************/ STDMETHODIMP CITResultSet::Add(PROPID PropID, LPVOID lpvDefaultData, DWORD cbData, PRIORITY Priority) { if (m_cProp >= MAX_COLUMNS) return (SetErrReturn(E_TOOMANYCOLUMNS));
m_cs.Lock();
// clear header
MEMSET(&m_Header[m_cProp], NULL, sizeof(CHeader));
// copy data
if (lpvDefaultData) { LPBYTE pBuffer; if (NULL == (pBuffer = (LPBYTE) BlockCopy(m_pMemPool, NULL, cbData + 4, 0))) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); }
MEMCPY(pBuffer + 4, lpvDefaultData, cbData); *(LPDWORD)pBuffer = cbData;
m_Header[m_cProp].lpvData = pBuffer;
} else m_Header[m_cProp].lpvData = NULL;
m_Header[m_cProp].dwPropID = PropID; m_Header[m_cProp].dwType = TYPE_POINTER; m_Header[m_cProp].Priority = Priority; // default priority
m_cProp++; // Wow! This is a lot of properties!
ITASSERT (m_cProp < THEORETICAL_MAX_PROP);
m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | SetColumnPriority | * Sets the download priority for the given column in the result set * * @parm LONG | lColumnIndex | Index of column to set * @parm PRIORITY | Priority | Priority, which can be one of the following: * * @flag PRIORITY_LOW | Low priority * @flag PRIORITY_NORMAL | Normal priority * @flag PRIORITY_HIGH | High priority * * @rvalue E_NOTEXIST | Column does not exist * @rvalue S_OK | The priority was successfully set ********************************************************************/ STDMETHODIMP CITResultSet::SetColumnPriority(LONG lColumnIndex, PRIORITY ColumnPriority) { if (lColumnIndex >= m_cProp || lColumnIndex < 0) return SetErrReturn(E_NOTEXIST);
m_cs.Lock();
m_Header[lColumnIndex].Priority = ColumnPriority;
m_cs.Unlock(); return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | SetColumnHeap | * Sets the heap which DWORD values in this column point into. * * @parm LONG | lColumnIndex | Index of column to set * @parm LPVOID | lpvHeap | Pointer to the heap. * @parm PFNCOLHEAPFREE | pfnColHeapFree | * Pointer to a function which can be called to free the heap * when the result set is cleared or freed. * * * @rvalue E_NOTEXIST | Column does not exist * @rvalue S_OK | The heap was successfully set ********************************************************************/ STDMETHODIMP CITResultSet::SetColumnHeap(LONG lColumnIndex, LPVOID lpvHeap, PFNCOLHEAPFREE pfnColHeapFree) { HRESULT hr = S_OK;
if (lColumnIndex < 0) return SetErrReturn(E_INVALIDARG);
m_cs.Lock();
if (lColumnIndex >= m_cProp) hr = E_NOTEXIST; else if (m_Header[lColumnIndex].lpvHeap != NULL) hr = E_ALREADYINIT;
if (SUCCEEDED(hr)) { m_Header[lColumnIndex].lpvHeap = lpvHeap; m_Header[lColumnIndex].pfnHeapFree = pfnColHeapFree; }
m_cs.Unlock(); return (hr); }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | GetColumnPriority | * Gets the download priority for the given column in the result set * * @parm LONG | lColumnIndex | Index of column to get * @parm PRIORITY& | Priority | Priority, which can be one of the following: * * @flag PRIORITY_LOW | Low priority * @flag PRIORITY_NORMAL | Normal priority * @flag PRIORITY_HIGH | High priority * * @rvalue E_NOTEXIST | Column does not exist * @rvalue S_OK | The priority was successfully retrieved ********************************************************************/ STDMETHODIMP CITResultSet::GetColumnPriority(LONG lColumnIndex, PRIORITY& ColumnPriority) { if (lColumnIndex >= m_cProp || lColumnIndex < 0) return SetErrReturn(E_NOTEXIST);
ColumnPriority = m_Header[lColumnIndex].Priority; return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | SetKeyProp | * Sets the property to be used as the key. * * @parm PROPID | KeyPropID | Property ID * * @rvalue S_OK | The key property was successfully set * ********************************************************************/ inline STDMETHODIMP CITResultSet::SetKeyProp(PROPID KeyPropID) { m_cs.Lock();
m_dwKeyProp = KeyPropID;
m_cs.Unlock(); return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | GetKeyProp | * Retrieves the property used as the key * * @parm PROPID | KeyPropID | Property ID * * @rvalue S_OK | The key property was successfully retrieved * ********************************************************************/ inline STDMETHODIMP CITResultSet::GetKeyProp(PROPID& KeyPropID) { KeyPropID = m_dwKeyProp; return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Append | * Given header (prop ID and type) and data, this function forms a * row and appends it to the current result set * * @parm LPVOID | lpvHdr | Pointer to buffer containing header * @parm LPVOID | lpvData | Pointer to buffer containing data * * @rvalue E_INVALIDARG | Exceeded maximum number of rows * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_FALSE | No columns were found to set, but no failure occurred * @rvalue S_OK | The row was successfully filled in * * @xcomm * The format of lpvHdr is identical to the output format of * PorpertyList::SaveData. If PL:Savedata changes we may * need to modify this code to maintain compatability. * ********************************************************************/ STDMETHODIMP CITResultSet::Append(LPVOID lpvHdr, LPVOID lpvData) { return Set(m_AppendRow, lpvHdr, lpvData); }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Set | * Given header (prop ID and type) and data, this function puts * the information into the specified row in the result set * * @parm LONG | lRowIndex | Index of row in result set * @parm LPVOID | lpvHdr | Pointer to buffer containing header * @parm LPVOID | lpvData | Pointer to buffer containing data * * @rvalue E_INVALIDARG | Exceeded maximum number of rows * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_FALSE | No columns were found to set, but no failure occurred * @rvalue S_OK | The row was successfully filled in * * @xcomm * The format of lpvHdr is identical to the output format of * PorpertyList::SaveData. If PL:Savedata changes we may * need to modify this code to maintain compatability. * ********************************************************************/ STDMETHODIMP CITResultSet::Set(LONG lRowIndex, LPVOID lpvHdr, LPVOID lpvData) {
LPBYTE pCurHdr = (LPBYTE) lpvHdr; LPBYTE pCurData = (LPBYTE) lpvData; LPBYTE pBitField = (LPBYTE) lpvData; DWORD dwProps; DWORD dwPropID; DWORD dwType; LPBYTE pBuffer; DWORD cbSize; LONG lColumn; HRESULT hr = S_FALSE;
m_cs.Lock();
// Reserve memory if necessary
if (lRowIndex >= m_RowsReserved) { if ( FAILED(Reserve()) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); } }
// Commit memory
if ( FAILED(Commit(lRowIndex)) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); }
LONG LogicalRow; RealToLogical(lRowIndex, LogicalRow); LONG nRow = LogicalRow * m_cProp; LONG nChunk = lRowIndex/ROW_CHUNK; // Number of properties in header
MEMCPY(&dwProps, pCurHdr, sizeof(DWORD)); pCurHdr += sizeof(DWORD);
// Shift pCurData up to leave room for the property bit field
pCurData += (dwProps/8 + 1);
// For each property in header, look for matching result set column
for (DWORD iProp = 0; iProp < dwProps; iProp++) { MEMCPY(&dwPropID, pCurHdr, sizeof(DWORD)); pCurHdr += sizeof(DWORD); MEMCPY(&dwType, pCurHdr, sizeof(DWORD)); pCurHdr += sizeof(DWORD);
// UNDONE: Figure out an optimization. If columns match up w/ header,
// then by the time we get to the end of the header, we'll have to loop
// over ALL the columns just to find which the column that matches the
// property ID. The problem is, it's not guaranteed that the columns
// match up exactly; the user could set them in any order.
// Is there data for this property?
BYTE WhichBit = (0x80 >> (iProp % 8)); int BitSet = pBitField[iProp/8] & WhichBit;
HRESULT hrFound = GetColumnFromPropID(dwPropID, lColumn); if (SUCCEEDED(hrFound)) hr = S_OK; // found at least one column
if (BitSet) { if (TYPE_VALUE == dwType) { // copy data value
if (SUCCEEDED(hrFound)) m_ResultSet[nChunk][nRow + lColumn] = *(LPDWORD) pCurData;
// if user didn't specify column, we still need to skip the data for
// this property
pCurData += sizeof(DWORD);
} else { // get size
MEMCPY(&cbSize, pCurData, sizeof(DWORD)); pCurData += sizeof(DWORD);
if (SUCCEEDED(hrFound)) { if (NULL == (pBuffer = (LPBYTE) BlockCopy(m_pMemPool, NULL, cbSize + 4, 0))) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); }
// append size to data
MEMCPY(pBuffer + 4, pCurData, cbSize); *(LPDWORD)pBuffer = cbSize; m_ResultSet[nChunk][nRow + lColumn] = (DWORD_PTR) pBuffer; }
pCurData += cbSize; }
} else { // No, so use default
if (SUCCEEDED(hrFound)) m_ResultSet[nChunk][nRow + lColumn] = (DWORD_PTR) m_Header[lColumn].lpvData; }
} // end for over properties in header
// Always maintain one past the last row as the append row
// unless we didn't put anything in the result set
if ( (lRowIndex >= m_AppendRow) && (S_OK == hr)) m_AppendRow = lRowIndex + 1;
m_cs.Unlock();
return hr; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Set | * Sets the property in the specified row to the property value. * * @parm LONG | lRowIndex | Row in which property belongs * @parm LONG | lColumnIndex | Column in which property belongs * @parm DWORD | dwData | Data to set * * @rvalue E_INVALIDARG | Exceed maximum row count * @rvalue E_NOTEXIST | Column does not exist * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_OK | The row was successfully set ********************************************************************/ STDMETHODIMP CITResultSet::Set(LONG lRowIndex, LONG lColumnIndex, DWORD dwData) { if (lColumnIndex >= m_cProp || lColumnIndex < 0) return SetErrReturn(E_NOTEXIST);
m_cs.Lock();
// Reserve memory if necessary
if (lRowIndex >= m_RowsReserved) { if ( FAILED(Reserve()) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); } } // Commit memory
if ( FAILED(Commit(lRowIndex)) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); }
LONG LogicalRow; RealToLogical(lRowIndex, LogicalRow); LONG nRow = LogicalRow * m_cProp; LONG nChunk = lRowIndex/ROW_CHUNK;
m_ResultSet[nChunk][nRow + lColumnIndex] = dwData;
// always maintain one past the last row as the append row
if (lRowIndex >= m_AppendRow) m_AppendRow = lRowIndex + 1;
m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Set | * Sets the property in the specified row to the property value. * * @parm LONG | lRowIndex | Row in which property belongs * @parm LONG | lColumnIndex | Column in which property belongs * @parm DWORD | dwData | Data to set * * @rvalue E_INVALIDARG | Exceed maximum row count * @rvalue E_NOTEXIST | Column does not exist * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_OK | The row was successfully set ********************************************************************/ STDMETHODIMP CITResultSet::Set(LONG lRowIndex, LONG lColumnIndex, DWORD_PTR dwData) { if (lColumnIndex >= m_cProp || lColumnIndex < 0) return SetErrReturn(E_NOTEXIST);
m_cs.Lock();
// Reserve memory if necessary
if (lRowIndex >= m_RowsReserved) { if ( FAILED(Reserve()) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); } } // Commit memory
if ( FAILED(Commit(lRowIndex)) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); }
LONG LogicalRow; RealToLogical(lRowIndex, LogicalRow); LONG nRow = LogicalRow * m_cProp; LONG nChunk = lRowIndex/ROW_CHUNK;
m_ResultSet[nChunk][nRow + lColumnIndex] = dwData;
// always maintain one past the last row as the append row
if (lRowIndex >= m_AppendRow) m_AppendRow = lRowIndex + 1;
m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Set | * Sets the property in the specified row to the property value. * * @parm LONG | lRowIndex | Row in which property belongs * @parm LPCWSTR | lpszString | Data to set * * @rvalue E_INVALIDARG | Exceed maximum row count * @rvalue E_NOTEXIST | Column does not exist * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_OK | The row was successfully set ********************************************************************/ STDMETHODIMP CITResultSet::Set(LONG lRowIndex, LONG lColumnIndex, LPCWSTR lpszString) {
if (lColumnIndex >= m_cProp || lColumnIndex < 0) return SetErrReturn(E_NOTEXIST);
m_cs.Lock();
// Reserve memory if necessary
if (lRowIndex >= m_RowsReserved) { if ( FAILED(Reserve()) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); } } // Commit memory
if ( FAILED(Commit(lRowIndex)) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); }
LONG LogicalRow; RealToLogical(lRowIndex, LogicalRow); LONG nRow = LogicalRow * m_cProp; LONG nChunk = lRowIndex/ROW_CHUNK;
DWORD cbData = 0; if (lpszString) cbData = (DWORD) (2*(WSTRLEN(lpszString) + 1)); LPBYTE pBuffer = (LPBYTE) BlockCopy(m_pMemPool, NULL, cbData + sizeof (DWORD), 0); if (NULL == pBuffer) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); } MEMCPY(pBuffer + 4, lpszString, cbData); *(LPDWORD)pBuffer = cbData; m_ResultSet[nChunk][nRow + lColumnIndex] = (DWORD_PTR) pBuffer;
// always maintain one past the last row as the append row
if (lRowIndex >= m_AppendRow) m_AppendRow = lRowIndex + 1; m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Set | * Sets the property in the specified row to the property value. * * @parm LONG | lRowIndex | Row in which property belongs * @parm DWORD | dwData | Data to set * * @rvalue E_INVALIDARG | Exceed maximum row count * @rvalue E_NOTEXIST | Column does not exist * @rvalue E_OUTOFMEMORY | Memory allocation failed * @rvalue S_OK | The row was successfully set ********************************************************************/ STDMETHODIMP CITResultSet::Set(LONG lRowIndex, LONG lColumnIndex, LPVOID lpvData, DWORD cbData) {
if (lColumnIndex >= m_cProp || lColumnIndex < 0) return SetErrReturn(E_NOTEXIST);
m_cs.Lock();
// Reserve memory if necessary
if (lRowIndex >= m_RowsReserved) { if ( FAILED(Reserve()) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); } } // Commit memory
if ( FAILED(Commit(lRowIndex)) ) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); } LONG LogicalRow; RealToLogical(lRowIndex, LogicalRow); LONG nRow = LogicalRow * m_cProp; LONG nChunk = lRowIndex/ROW_CHUNK;
LPBYTE pBuffer = (LPBYTE) BlockCopy(m_pMemPool, NULL, cbData + sizeof (DWORD), 0); if (NULL == pBuffer) { m_cs.Unlock(); return SetErrReturn(E_OUTOFMEMORY); } *(LPDWORD)pBuffer = cbData; MEMCPY(pBuffer + sizeof (DWORD), lpvData, cbData); m_ResultSet[nChunk][nRow + lColumnIndex] = (DWORD_PTR) pBuffer;
// always maintain one past the last row as the append row
if (lRowIndex >= m_AppendRow) m_AppendRow = lRowIndex + 1;
m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Get | * Gets the property in the specified row and column and fills the given * property object. * * @parm LONG | lRowIndex | Row in which property belongs * @parm LONG | lColumnIndex | Column in which property belongs * @parm CProperty& | Prop | Property object to fill * * @rvalue E_NOTEXIST | The row or column does not exist in the row set * @rvalue S_OK | The row was successfully retrieved * ********************************************************************/ STDMETHODIMP CITResultSet::Get(LONG lRowIndex, LONG lColumnIndex, CProperty& Prop) { if (lRowIndex >= m_AppendRow || lColumnIndex >= m_cProp) return SetErrReturn(E_NOTEXIST);
LONG LogicalRow; RealToLogical(lRowIndex, LogicalRow); LONG nRow = LogicalRow * m_cProp; LONG nChunk = lRowIndex/ROW_CHUNK;
Prop.dwPropID = m_Header[lColumnIndex].dwPropID; if (TYPE_VALUE == (Prop.dwType = m_Header[lColumnIndex].dwType)) { // For data types, we have no way of knowing how to return
// the default, so we just return 0 if this cell was never filled in
Prop.lpvData = (LPVOID) m_ResultSet[nChunk][nRow+lColumnIndex]; Prop.dwValue = (DWORD) m_ResultSet[nChunk][nRow+lColumnIndex]; Prop.cbData = sizeof(DWORD); } else { LPBYTE pBuffer = (LPBYTE) m_ResultSet[nChunk][nRow+lColumnIndex];
if (pBuffer) { Prop.cbData = *(LPDWORD)pBuffer; Prop.lpvData = pBuffer + sizeof (DWORD); } else { // there's nothing there, so return default
if (m_Header[lColumnIndex].lpvData) { Prop.cbData = *(LPDWORD) m_Header[lColumnIndex].lpvData; Prop.lpvData = (LPDWORD)(m_Header[lColumnIndex].lpvData) + 1; } else { // default was specified as NULL, so we make sure we
// return that
Prop.cbData = 0; Prop.lpvData = NULL; } } } return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | GetColumnCount | * Gets number of columns in result set * * @parm LONG& | lNumberOfColumns | Number of columns * * @rvalue S_OK | The number of columns was successfully retrieved * ********************************************************************/ inline STDMETHODIMP CITResultSet::GetColumnCount(LONG& lNumberOfColumns) { lNumberOfColumns = m_cProp; return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | GetRowCount | * Gets number of rows in result set * * @parm LONG& | lNumberOfRows | Number of rows * * @rvalue S_OK | The number of rows was successfully retrieved * ********************************************************************/ inline STDMETHODIMP CITResultSet::GetRowCount(LONG& lNumberOfRows) { lNumberOfRows = m_AppendRow; return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | GetColumn| * Gets property ID and default value associated with a column. * * @parm LONG | lColumnIndex | Column number * @parm PROPID | PropID | Property ID * @parm DWORD | dwType | Property Type (TYPE_VALUE, TYPE_POINTER, TYPE_STRING) * @parm LPVOID& | lpvDefaultValue | Default value * @parm DWORD& | cbSize |Length of data (in bytes) * @parm PRIORITY& | Priority | Column priority * * @rvalue E_NOTEXIST | Column does not exist * @rvalue S_OK | The column was successfully retrieved * ********************************************************************/ STDMETHODIMP CITResultSet::GetColumn(LONG lColumnIndex, PROPID& PropID, DWORD& dwType, LPVOID& lpvDefaultValue, DWORD& cbSize, PRIORITY& Priority) { // check against invalid lColumnIndex
if (lColumnIndex >= m_cProp || lColumnIndex < 0) return SetErrReturn(E_NOTEXIST);
PropID = m_Header[lColumnIndex].dwPropID; dwType = m_Header[lColumnIndex].dwType;
// it could be NULL
if (m_Header[lColumnIndex].lpvData) { lpvDefaultValue = (LPBYTE) m_Header[lColumnIndex].lpvData + sizeof (DWORD); if (TYPE_VALUE == dwType) cbSize = sizeof(DWORD); else cbSize = *((LPDWORD)m_Header[lColumnIndex].lpvData); } else { lpvDefaultValue = NULL; cbSize = 0; }
Priority = m_Header[lColumnIndex].Priority;
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | GetColumn| * Gets property ID for a given column index. * * @parm LONG | lColumnIndex | Column number * @parm PROPID | PropID | Property ID * * @rvalue E_NOTEXIST | Column does not exist * @rvalue S_OK | The column was successfully retrieved * ********************************************************************/ STDMETHODIMP CITResultSet::GetColumn(LONG lColumnIndex, PROPID& PropID) { // check against invalid lColumnIndex
if (lColumnIndex >= m_cProp || lColumnIndex < 0) return SetErrReturn(E_NOTEXIST);
PropID = m_Header[lColumnIndex].dwPropID;
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | GetColumnFromPropID | * Gets column index for which a property ID is associated * * @parm PROPID | PropID | Property ID * @parm LONG& | lColumnIndex | Column index * * @rvalue E_NOTEXIST | The column does not exist * @rvalue S_OK | The column index was successfully returned * ********************************************************************/ STDMETHODIMP CITResultSet::GetColumnFromPropID(PROPID PropID, LONG& lColumnIndex) { // Loop over all columns, looking for match
for (LONG iIndex = 0; iIndex < m_cProp; iIndex++) { if (PropID == m_Header[iIndex].dwPropID) { // Found it
lColumnIndex = iIndex; return S_OK; } }
return SetErrReturn(E_NOTEXIST); }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | AppendRows| * Appends rows from the given source resultset * * @parm IITResultSet* | pResSrc | Source resultset * @parm LONG | lRowSrcFirst | Source row number to start the copy * @parm LONG | cSrcRows | Number of rows to append * @parm LONG& | lRowFirstDest | First destination row number appended * * @rvalue E_OUTOFMEMORY | Not enough memory to append to the destination * @rvalue S_OK | All rows were successfully appended * ********************************************************************/ // UNDONE: this is terribly inefficient. We need to fix resultsets so that rows can
// be copied more easily
STDMETHODIMP CITResultSet::AppendRows(IITResultSet* pResSrc, LONG lRowSrcFirst, LONG cSrcRows, LONG& lRowDestFirst) { LONG lColumn; HRESULT hr = E_NOTEXIST; PROPID PropID; LONG lDestColumn; CProperty Prop; lRowDestFirst = m_AppendRow;
m_cs.Lock();
pResSrc->GetColumnCount(lColumn);
// Loop over columns in source result set
for (LONG iProp = 0; iProp < lColumn; iProp++) { // get column in dest result set
pResSrc->GetColumn(iProp, PropID); if (FAILED(GetColumnFromPropID(PropID, lDestColumn))) continue;
hr = S_OK; // there's at least one matching column
// Loop over rows in input result set
LONG iRow; // loop index
switch( m_Header[lDestColumn].dwType ) { case TYPE_VALUE: for (iRow = 0; iRow < cSrcRows; iRow++) { pResSrc->Get(lRowSrcFirst + iRow, iProp, Prop); Set(lRowDestFirst + iRow, lDestColumn, Prop.dwValue); } break; case TYPE_STRING: for (iRow = 0; iRow < cSrcRows; iRow++) { pResSrc->Get(lRowSrcFirst + iRow, iProp, Prop); Set(lRowDestFirst + iRow, lDestColumn, Prop.lpszwData); } break; case TYPE_POINTER: for (iRow = 0; iRow < cSrcRows; iRow++) { pResSrc->Get(lRowSrcFirst + iRow, iProp, Prop); Set(lRowDestFirst + iRow, lDestColumn, Prop.lpvData, Prop.cbData); } break; } }
m_cs.Unlock();
return hr; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Copy | * Copies the rows associated with the columns set in the * given result set. This method can be used to take a larger result * set and reduce it Passing an empty resultset will cause all columns * and rows to be added and copied from the source resultset. * * @parm IITResultSet* | pRSCopy | Result set object containing copied * rows. * * @rvalue E_NOTEXIST | No columns match the input result set * * @rvalue S_OK | The rows were successfully copied. * ********************************************************************/ STDMETHODIMP CITResultSet::Copy(IITResultSet* pRSCopy) { LONG lColumn; HRESULT hr;
pRSCopy->GetColumnCount(lColumn); if (0L == lColumn) { LONG cCols; LONG iCol; // add all columns from the source to the dest
GetColumnCount(cCols); for (iCol = 0; iCol < cCols; iCol++) { PROPID pid; DWORD dwType; LPVOID lpv; DWORD cbSize; PRIORITY pri;
// UNDONE: get rid of this dwType stuff
// UNDONE: simpler place to put column info (i.e. struct/class)
GetColumn(iCol, pid, dwType, lpv, cbSize, pri);
if (dwType == TYPE_VALUE) { if (FAILED(hr = pRSCopy->Add(pid, (DWORD)0, PRIORITY_NORMAL))) return hr; } else { if (FAILED(hr= pRSCopy->Add(pid, (LPWSTR)0, PRIORITY_NORMAL))) return hr; } } lColumn = cCols; }
PROPID PropID; LONG lInputColumn; CProperty Prop;
m_cs.Lock(); hr = E_NOTEXIST;
// Loop over columns in output result set
for (LONG iProp = 0; iProp < lColumn; iProp++) { // get column in input result set
pRSCopy->GetColumn(iProp, PropID); if (FAILED(GetColumnFromPropID(PropID, lInputColumn))) continue;
hr = S_OK; // there's at least one matching column
// Loop over rows in input result set
LONG iRow; // loop index
switch( m_Header[lInputColumn].dwType ) { case TYPE_VALUE: for (iRow = 0; iRow < m_AppendRow; iRow++) { Get(iRow, lInputColumn, Prop); pRSCopy->Set(iRow, iProp, Prop.dwValue); } break; case TYPE_STRING: for (iRow = 0; iRow < m_AppendRow; iRow++) { Get(iRow, lInputColumn, Prop); pRSCopy->Set(iRow, iProp, Prop.lpszwData); } break; case TYPE_POINTER: for (iRow = 0; iRow < m_AppendRow; iRow++) { Get(iRow, lInputColumn, Prop); pRSCopy->Set(iRow, iProp, Prop.lpvData, Prop.cbData); } break; } }
m_cs.Unlock();
return hr; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | Clear | * Frees all memory associated with a result set * * @rvalue S_OK | The result set was successfully cleared * * @comm This method can be called to clear a result set without * requiring the set to be destroyed before being used again. ********************************************************************/ STDMETHODIMP CITResultSet::Clear() { LONG iProp;
m_cs.Lock();
// Free any column heaps which may have been set.
for (iProp = 0; iProp < m_cProp; iProp++) { LPVOID lpvHeap; PFNCOLHEAPFREE pfnHeapFree;
if ((lpvHeap = m_Header[iProp].lpvHeap) != NULL && (pfnHeapFree = m_Header[iProp].pfnHeapFree) != NULL) { (*pfnHeapFree)(lpvHeap); } }
ClearRows(); m_cProp = 0;
m_cs.Unlock();
return S_OK; }
/********************************************************************
* @method STDMETHODIMP | IITResultSet | ClearRows | * Frees all memory associated with a result set, without * resetting column information * * @rvalue S_OK | The result set was successfully cleared * * @comm This method can be called to clear a result set without * requiring the set to be destroyed before being used again. ********************************************************************/ STDMETHODIMP CITResultSet::ClearRows() { m_cs.Lock();
// Free page map
if (m_PageMap) { delete m_PageMap; m_PageMap = NULL; }
// Decommit and release virtual memory
FreeMem();
// Reset memory pool
if (m_pMemPool) BlockReset(m_pMemPool);
// Reset member data
m_NumberOfPages = 0; m_fInit = FALSE; m_RowsReserved = 0; m_AppendRow = 0; m_Chunk = -1;
m_cs.Unlock();
return S_OK; }
// Wrapper for VirtualFree(MEM_DECOMMIT) and VirtualFree(MEM_RELEASE)
HRESULT WINAPI CITResultSet::FreeMem() { BOOL fRet; int iLoop;
// Decommit result set - remember that it's an array of chunks (of rows)
// so we have to loop over all the chunks
// erinfox: bug fix, iLoop <= m_Chunk because m_Chunk is numbered from 0!
for (iLoop = 0; iLoop <= m_Chunk; iLoop++) { g_iFreed++; fRet = VirtualFree(m_ResultSet[iLoop], m_BytesReserved, MEM_DECOMMIT); ITASSERT(TRUE == fRet); }
// Then release it
for (iLoop = 0; iLoop <= m_Chunk; iLoop++) { fRet = VirtualFree(m_ResultSet[iLoop], 0, MEM_RELEASE); ITASSERT(TRUE == fRet); }
return S_OK; }
STDMETHODIMP CITResultSet::Free() { return E_NOTIMPL; }
//////////////////////// Asynchronous //////////////////////////////////////
STDMETHODIMP CITResultSet::IsCompleted() { return E_NOTIMPL; }
STDMETHODIMP CITResultSet::Cancel() { return E_NOTIMPL; }
STDMETHODIMP CITResultSet::Pause(BOOL fPause) { return E_NOTIMPL; }
STDMETHODIMP CITResultSet::GetRowStatus(LONG lRowFirst, LONG cRows, LPROWSTATUS lpRowStatus) { return E_NOTIMPL; }
STDMETHODIMP CITResultSet::GetColumnStatus(LPCOLUMNSTATUS lpColStatus) { return E_NOTIMPL; }
|