summaryrefslogtreecommitdiff
path: root/src/inc/corhlprpriv.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/inc/corhlprpriv.h')
-rw-r--r--src/inc/corhlprpriv.h773
1 files changed, 773 insertions, 0 deletions
diff --git a/src/inc/corhlprpriv.h b/src/inc/corhlprpriv.h
new file mode 100644
index 0000000000..73b89951dd
--- /dev/null
+++ b/src/inc/corhlprpriv.h
@@ -0,0 +1,773 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*****************************************************************************
+ ** **
+ ** Corhlprpriv.h - **
+ ** **
+ *****************************************************************************/
+
+#ifndef __CORHLPRPRIV_H__
+#define __CORHLPRPRIV_H__
+
+#include "corhlpr.h"
+#include "fstring.h"
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("y", on) // If routines don't get inlined, don't pay the EBP frame penalty
+#endif
+
+//*****************************************************************************
+//
+//***** Utility helpers
+//
+//*****************************************************************************
+
+#ifndef SOS_INCLUDE
+
+//*****************************************************************************
+//
+// **** CQuickBytes
+// This helper class is useful for cases where 90% of the time you allocate 512
+// or less bytes for a data structure. This class contains a 512 byte buffer.
+// Alloc() will return a pointer to this buffer if your allocation is small
+// enough, otherwise it asks the heap for a larger buffer which is freed for
+// you. No mutex locking is required for the small allocation case, making the
+// code run faster, less heap fragmentation, etc... Each instance will allocate
+// 520 bytes, so use accordinly.
+//
+//*****************************************************************************
+namespace NSQuickBytesHelper
+{
+ template <BOOL bThrow>
+ struct _AllocBytes;
+
+ template <>
+ struct _AllocBytes<TRUE>
+ {
+ static BYTE *Invoke(SIZE_T iItems)
+ {
+ return NEW_THROWS(iItems);
+ }
+ };
+
+ template <>
+ struct _AllocBytes<FALSE>
+ {
+ static BYTE *Invoke(SIZE_T iItems)
+ {
+ return NEW_NOTHROW(iItems);
+ }
+ };
+};
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr);
+
+template <SIZE_T SIZE, SIZE_T INCREMENT>
+class CQuickMemoryBase
+{
+protected:
+ template <typename ELEM_T>
+ static ELEM_T Min(ELEM_T a, ELEM_T b)
+ { return a < b ? a : b; }
+
+ template <typename ELEM_T>
+ static ELEM_T Max(ELEM_T a, ELEM_T b)
+ { return a < b ? b : a; }
+
+ // bGrow - indicates that this is a resize and that the original data
+ // needs to be copied over.
+ // bThrow - indicates whether or not memory allocations will throw.
+ template <BOOL bGrow, BOOL bThrow>
+ void *_Alloc(SIZE_T iItems)
+ {
+#if defined(_BLD_CLR) && defined(_DEBUG)
+ { // Exercise heap for OOM-fault injection purposes
+ BYTE * pb = NSQuickBytesHelper::_AllocBytes<bThrow>::Invoke(iItems);
+ _ASSERTE(!bThrow || pb != NULL); // _AllocBytes would have thrown if bThrow == TRUE
+ if (pb == NULL) return NULL; // bThrow == FALSE and we failed to allocate memory
+ delete [] pb; // Success, delete allocated memory.
+ }
+#endif
+ if (iItems <= cbTotal)
+ { // Fits within existing memory allocation
+ iSize = iItems;
+ }
+ else if (iItems <= SIZE)
+ { // Will fit in internal buffer.
+ if (pbBuff == NULL)
+ { // Any previous allocation is in the internal buffer and the new
+ // allocation fits in the internal buffer, so just update the size.
+ iSize = iItems;
+ cbTotal = SIZE;
+ }
+ else
+ { // There was a previous allocation, sitting in pbBuff
+ if (bGrow)
+ { // If growing, need to copy any existing data over.
+ memcpy(&rgData[0], pbBuff, Min(cbTotal, SIZE));
+ }
+
+ delete [] pbBuff;
+ pbBuff = NULL;
+ iSize = iItems;
+ cbTotal = SIZE;
+ }
+ }
+ else
+ { // Need to allocate a new buffer
+ SIZE_T cbTotalNew = iItems + (bGrow ? INCREMENT : 0);
+ BYTE * pbBuffNew = NSQuickBytesHelper::_AllocBytes<bThrow>::Invoke(cbTotalNew);
+
+ if (!bThrow && pbBuffNew == NULL)
+ { // Allocation failed. Zero out structure.
+ if (pbBuff != NULL)
+ { // Delete old buffer
+ delete [] pbBuff;
+ }
+ pbBuff = NULL;
+ iSize = 0;
+ cbTotal = 0;
+ return NULL;
+ }
+
+ if (bGrow && cbTotal > 0)
+ { // If growing, need to copy any existing data over.
+ memcpy(pbBuffNew, (BYTE *)Ptr(), Min(cbTotal, cbTotalNew));
+ }
+
+ if (pbBuff != NULL)
+ { // Delete old pre-existing buffer
+ delete [] pbBuff;
+ pbBuff = NULL;
+ }
+
+ pbBuff = pbBuffNew;
+ cbTotal = cbTotalNew;
+ iSize = iItems;
+ }
+
+ return Ptr();
+ }
+
+public:
+ void Init()
+ {
+ pbBuff = 0;
+ iSize = 0;
+ cbTotal = SIZE;
+ }
+
+ void Destroy()
+ {
+ if (pbBuff)
+ {
+ delete [] pbBuff;
+ pbBuff = 0;
+ }
+ }
+
+ void *AllocThrows(SIZE_T iItems)
+ {
+ return _Alloc<FALSE /*bGrow*/, TRUE /*bThrow*/>(iItems);
+ }
+
+ void *AllocNoThrow(SIZE_T iItems)
+ {
+ return _Alloc<FALSE /*bGrow*/, FALSE /*bThrow*/>(iItems);
+ }
+
+ void ReSizeThrows(SIZE_T iItems)
+ {
+ _Alloc<TRUE /*bGrow*/, TRUE /*bThrow*/>(iItems);
+ }
+
+#ifdef __llvm__
+ // This makes sure that we will not get an undefined symbol
+ // when building a release version of libcoreclr using LLVM.
+ __attribute__((used))
+#endif // __llvm__
+ HRESULT ReSizeNoThrow(SIZE_T iItems);
+
+ void Shrink(SIZE_T iItems)
+ {
+ _ASSERTE(iItems <= cbTotal);
+ iSize = iItems;
+ }
+
+ operator PVOID()
+ {
+ return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
+ }
+
+ void *Ptr()
+ {
+ return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
+ }
+
+ const void *Ptr() const
+ {
+ return ((pbBuff) ? pbBuff : (PVOID)&rgData[0]);
+ }
+
+ SIZE_T Size() const
+ {
+ return (iSize);
+ }
+
+ SIZE_T MaxSize() const
+ {
+ return (cbTotal);
+ }
+
+ void Maximize()
+ {
+ iSize = cbTotal;
+ }
+
+
+ // Convert UTF8 string to UNICODE string, optimized for speed
+ HRESULT ConvertUtf8_UnicodeNoThrow(const char * utf8str)
+ {
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = FString::Utf8_Unicode_Length(utf8str, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR buffer = (LPWSTR) AllocNoThrow((length + 1) * sizeof(WCHAR));
+
+ if (buffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ hr = FString::Utf8_Unicode(utf8str, allAscii, buffer, length);
+ }
+ }
+
+ return hr;
+ }
+
+ // Convert UTF8 string to UNICODE string, optimized for speed
+ void ConvertUtf8_Unicode(const char * utf8str)
+ {
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = FString::Utf8_Unicode_Length(utf8str, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR buffer = (LPWSTR) AllocThrows((length + 1) * sizeof(WCHAR));
+
+ hr = FString::Utf8_Unicode(utf8str, allAscii, buffer, length);
+ }
+
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ }
+
+ // Convert UNICODE string to UTF8 string, optimized for speed
+ void ConvertUnicode_Utf8(const WCHAR * pString)
+ {
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = FString::Unicode_Utf8_Length(pString, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ LPSTR buffer = (LPSTR) AllocThrows((length + 1) * sizeof(char));
+
+ hr = FString::Unicode_Utf8(pString, allAscii, buffer, length);
+ }
+
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+ }
+
+ // Copy single byte string and hold it
+ const char * SetStringNoThrow(const char * pStr, SIZE_T len)
+ {
+ LPSTR buffer = (LPSTR) AllocNoThrow(len + 1);
+
+ if (buffer != NULL)
+ {
+ memcpy(buffer, pStr, len);
+ buffer[len] = 0;
+ }
+
+ return buffer;
+ }
+
+#ifdef DACCESS_COMPILE
+ void
+ EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+ {
+ // Assume that 'this' is enumerated, either explicitly
+ // or because this class is embedded in another.
+ DacEnumMemoryRegion(dac_cast<TADDR>(pbBuff), iSize);
+ }
+#endif // DACCESS_COMPILE
+
+ BYTE *pbBuff;
+ SIZE_T iSize; // number of bytes used
+ SIZE_T cbTotal; // total bytes allocated in the buffer
+ // use UINT64 to enforce the alignment of the memory
+ UINT64 rgData[(SIZE+sizeof(UINT64)-1)/sizeof(UINT64)];
+};
+
+// These should be multiples of 8 so that data can be naturally aligned.
+#define CQUICKBYTES_BASE_SIZE 512
+#define CQUICKBYTES_INCREMENTAL_SIZE 128
+
+class CQuickBytesBase : public CQuickMemoryBase<CQUICKBYTES_BASE_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
+{
+};
+
+
+class CQuickBytes : public CQuickBytesBase
+{
+public:
+ CQuickBytes()
+ {
+ Init();
+ }
+
+ ~CQuickBytes()
+ {
+ Destroy();
+ }
+};
+
+/* to be used as static variable - no constructor/destructor, assumes zero
+ initialized memory */
+class CQuickBytesStatic : public CQuickBytesBase
+{
+};
+
+template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesSpecifySizeBase : public CQuickMemoryBase<CQUICKBYTES_BASE_SPECIFY_SIZE, CQUICKBYTES_INCREMENTAL_SIZE>
+{
+};
+
+template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesSpecifySize : public CQuickBytesSpecifySizeBase<CQUICKBYTES_BASE_SPECIFY_SIZE>
+{
+public:
+ CQuickBytesSpecifySize()
+ {
+ this->Init();
+ }
+
+ ~CQuickBytesSpecifySize()
+ {
+ this->Destroy();
+ }
+};
+
+/* to be used as static variable - no constructor/destructor, assumes zero
+ initialized memory */
+template <SIZE_T CQUICKBYTES_BASE_SPECIFY_SIZE>
+class CQuickBytesSpecifySizeStatic : public CQuickBytesSpecifySizeBase<CQUICKBYTES_BASE_SPECIFY_SIZE>
+{
+};
+
+template <class T> class CQuickArrayBase : public CQuickBytesBase
+{
+public:
+ T* AllocThrows(SIZE_T iItems)
+ {
+ CheckOverflowThrows(iItems);
+ return (T*)CQuickBytesBase::AllocThrows(iItems * sizeof(T));
+ }
+
+ void ReSizeThrows(SIZE_T iItems)
+ {
+ CheckOverflowThrows(iItems);
+ CQuickBytesBase::ReSizeThrows(iItems * sizeof(T));
+ }
+
+ T* AllocNoThrow(SIZE_T iItems)
+ {
+ if (!CheckOverflowNoThrow(iItems))
+ {
+ return NULL;
+ }
+ return (T*)CQuickBytesBase::AllocNoThrow(iItems * sizeof(T));
+ }
+
+ HRESULT ReSizeNoThrow(SIZE_T iItems)
+ {
+ if (!CheckOverflowNoThrow(iItems))
+ {
+ return E_OUTOFMEMORY;
+ }
+ return CQuickBytesBase::ReSizeNoThrow(iItems * sizeof(T));
+ }
+
+ void Shrink(SIZE_T iItems)
+ {
+ CQuickBytesBase::Shrink(iItems * sizeof(T));
+ }
+
+ T* Ptr()
+ {
+ return (T*) CQuickBytesBase::Ptr();
+ }
+
+ const T* Ptr() const
+ {
+ return (T*) CQuickBytesBase::Ptr();
+ }
+
+ SIZE_T Size() const
+ {
+ return CQuickBytesBase::Size() / sizeof(T);
+ }
+
+ SIZE_T MaxSize() const
+ {
+ return CQuickBytesBase::cbTotal / sizeof(T);
+ }
+
+ T& operator[] (SIZE_T ix)
+ {
+ _ASSERTE(ix < Size());
+ return *(Ptr() + ix);
+ }
+
+ const T& operator[] (SIZE_T ix) const
+ {
+ _ASSERTE(ix < Size());
+ return *(Ptr() + ix);
+ }
+
+private:
+ inline
+ BOOL CheckOverflowNoThrow(SIZE_T iItems)
+ {
+ SIZE_T totalSize = iItems * sizeof(T);
+
+ if (totalSize / sizeof(T) != iItems)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ inline
+ void CheckOverflowThrows(SIZE_T iItems)
+ {
+ if (!CheckOverflowNoThrow(iItems))
+ {
+ THROW_OUT_OF_MEMORY();
+ }
+ }
+};
+
+template <class T> class CQuickArray : public CQuickArrayBase<T>
+{
+public:
+ CQuickArray<T>()
+ {
+ this->Init();
+ }
+
+ ~CQuickArray<T>()
+ {
+ this->Destroy();
+ }
+};
+
+// This is actually more of a stack with array access. Essentially, you can
+// only add elements through Push and remove them through Pop, but you can
+// access and modify any random element with the index operator. You cannot
+// access elements that have not been added.
+
+template <class T>
+class CQuickArrayList : protected CQuickArray<T>
+{
+private:
+ SIZE_T m_curSize;
+
+public:
+ // Make these specific functions public.
+ using CQuickArray<T>::AllocThrows;
+ using CQuickArray<T>::ReSizeThrows;
+ using CQuickArray<T>::AllocNoThrow;
+ using CQuickArray<T>::ReSizeNoThrow;
+ using CQuickArray<T>::MaxSize;
+
+ CQuickArrayList()
+ : m_curSize(0)
+ {
+ this->Init();
+ }
+
+ ~CQuickArrayList()
+ {
+ this->Destroy();
+ }
+
+ // Can only access values that have been pushed.
+ T& operator[] (SIZE_T ix)
+ {
+ _ASSERTE(ix < m_curSize);
+ return CQuickArray<T>::operator[](ix);
+ }
+
+ // Can only access values that have been pushed.
+ const T& operator[] (SIZE_T ix) const
+ {
+ _ASSERTE(ix < m_curSize);
+ return CQuickArray<T>::operator[](ix);
+ }
+
+ // THROWS: Resizes if necessary.
+ void Push(const T & value)
+ {
+ // Resize if necessary - thows.
+ if (m_curSize + 1 >= CQuickArray<T>::Size())
+ ReSizeThrows((m_curSize + 1) * 2);
+
+ // Append element to end of array.
+ _ASSERTE(m_curSize + 1 < CQuickArray<T>::Size());
+ SIZE_T ix = m_curSize++;
+ (*this)[ix] = value;
+ }
+
+ T Pop()
+ {
+ _ASSERTE(m_curSize > 0);
+ T retval = (*this)[m_curSize - 1];
+ INDEBUG(ZeroMemory(&(this->Ptr()[m_curSize - 1]), sizeof(T));)
+ --m_curSize;
+ return retval;
+ }
+
+ SIZE_T Size() const
+ {
+ return m_curSize;
+ }
+
+ void Shrink()
+ {
+ CQuickArray<T>::Shrink(m_curSize);
+ }
+};
+
+
+/* to be used as static variable - no constructor/destructor, assumes zero
+ initialized memory */
+template <class T> class CQuickArrayStatic : public CQuickArrayBase<T>
+{
+};
+
+typedef CQuickArrayBase<WCHAR> CQuickWSTRBase;
+typedef CQuickArray<WCHAR> CQuickWSTR;
+typedef CQuickArrayStatic<WCHAR> CQuickWSTRStatic;
+
+typedef CQuickArrayBase<CHAR> CQuickSTRBase;
+typedef CQuickArray<CHAR> CQuickSTR;
+typedef CQuickArrayStatic<CHAR> CQuickSTRStatic;
+
+class RidBitmap
+{
+public:
+ HRESULT InsertToken(mdToken token)
+ {
+ HRESULT hr = S_OK;
+ mdToken rid = RidFromToken(token);
+ SIZE_T index = rid / 8;
+ BYTE bit = (1 << (rid % 8));
+
+ if (index >= buffer.Size())
+ {
+ SIZE_T oldSize = buffer.Size();
+ SIZE_T newSize = index+1+oldSize/8;
+ IfFailRet(buffer.ReSizeNoThrow(newSize));
+ memset(&buffer[oldSize], 0, newSize-oldSize);
+ }
+
+ buffer[index] |= bit;
+ return hr;
+ }
+
+ bool IsTokenInBitmap(mdToken token)
+ {
+ mdToken rid = RidFromToken(token);
+ SIZE_T index = rid / 8;
+ BYTE bit = (1 << (rid % 8));
+
+ return ((index < buffer.Size()) && (buffer[index] & bit));
+ }
+
+ void Reset()
+ {
+ if (buffer.Size())
+ {
+ memset(&buffer[0], 0, buffer.Size());
+ }
+ }
+
+private:
+ CQuickArray<BYTE> buffer;
+};
+
+//*****************************************************************************
+//
+//***** Signature helpers
+//
+//*****************************************************************************
+
+HRESULT _CountBytesOfOneArg(
+ PCCOR_SIGNATURE pbSig,
+ ULONG *pcbTotal);
+
+HRESULT _GetFixedSigOfVarArg( // S_OK or error.
+ PCCOR_SIGNATURE pvSigBlob, // [IN] point to a blob of CLR signature
+ ULONG cbSigBlob, // [IN] size of signature
+ CQuickBytes *pqbSig, // [OUT] output buffer for fixed part of VarArg Signature
+ ULONG *pcbSigBlob); // [OUT] number of bytes written to the above output buffer
+
+#endif //!SOS_INCLUDE
+
+#if defined(_MSC_VER) && defined(_TARGET_X86_)
+#pragma optimize("", on) // restore command line default optimizations
+#endif
+
+
+//---------------------------------------------------------------------------------------
+//
+// Reads compressed integer from buffer pData, fills the result to *pnDataOut. Advances buffer pointer.
+// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
+//
+inline
+__checkReturn
+HRESULT
+CorSigUncompressData_EndPtr(
+ PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
+ PCCOR_SIGNATURE pDataEnd, // End of buffer
+ DWORD * pnDataOut) // [OUT] Compressed integer read from the buffer
+{
+ _ASSERTE(pData <= pDataEnd);
+ HRESULT hr = S_OK;
+
+ INT_PTR cbDataSize = pDataEnd - pData;
+ if (cbDataSize > 4)
+ { // Compressed integer cannot be bigger than 4 bytes
+ cbDataSize = 4;
+ }
+ DWORD dwDataSize = (DWORD)cbDataSize;
+
+ ULONG cbDataOutLength;
+ IfFailRet(CorSigUncompressData(
+ pData,
+ dwDataSize,
+ pnDataOut,
+ &cbDataOutLength));
+ pData += cbDataOutLength;
+
+ return hr;
+} // CorSigUncompressData_EndPtr
+
+//---------------------------------------------------------------------------------------
+//
+// Reads CorElementType (1 byte) from buffer pData, fills the result to *pTypeOut. Advances buffer pointer.
+// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
+//
+inline
+__checkReturn
+HRESULT
+CorSigUncompressElementType_EndPtr(
+ PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
+ PCCOR_SIGNATURE pDataEnd, // End of buffer
+ CorElementType * pTypeOut) // [OUT] ELEMENT_TYPE_* value read from the buffer
+{
+ _ASSERTE(pData <= pDataEnd);
+ // We don't expect pData > pDataEnd, but the runtime check doesn't cost much and it is more secure in
+ // case caller has a bug
+ if (pData >= pDataEnd)
+ { // No data
+ return META_E_BAD_SIGNATURE;
+ }
+ // Read 'type' as 1 byte
+ *pTypeOut = (CorElementType)*pData;
+ pData++;
+
+ return S_OK;
+} // CorSigUncompressElementType_EndPtr
+
+//---------------------------------------------------------------------------------------
+//
+// Reads pointer (4/8 bytes) from buffer pData, fills the result to *ppvPointerOut. Advances buffer pointer.
+// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
+//
+inline
+__checkReturn
+HRESULT
+CorSigUncompressPointer_EndPtr(
+ PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
+ PCCOR_SIGNATURE pDataEnd, // End of buffer
+ void ** ppvPointerOut) // [OUT] Pointer value read from the buffer
+{
+ _ASSERTE(pData <= pDataEnd);
+ // We could just skip this check as pointers should be only in trusted (and therefore correct)
+ // signatures and we check for that on the caller side, but it won't hurt to have this check and it will
+ // make it easier to catch invalid signatures in trusted code (e.g. IL stubs, NGEN images, etc.)
+ if (pData + sizeof(void *) > pDataEnd)
+ { // Not enough data in the buffer
+ _ASSERTE(!"This signature is invalid. Note that caller should check that it is not comming from untrusted source!");
+ return META_E_BAD_SIGNATURE;
+ }
+ *ppvPointerOut = *(void * UNALIGNED *)pData;
+ pData += sizeof(void *);
+
+ return S_OK;
+} // CorSigUncompressPointer_EndPtr
+
+//---------------------------------------------------------------------------------------
+//
+// Reads compressed TypeDef/TypeRef/TypeSpec token, fills the result to *pnDataOut. Advances buffer pointer.
+// Doesn't read behind the end of the buffer (the end starts at pDataEnd).
+//
+inline
+__checkReturn
+HRESULT
+CorSigUncompressToken_EndPtr(
+ PCCOR_SIGNATURE & pData, // [IN,OUT] Buffer
+ PCCOR_SIGNATURE pDataEnd, // End of buffer
+ mdToken * ptkTokenOut) // [OUT] Token read from the buffer
+{
+ _ASSERTE(pData <= pDataEnd);
+ HRESULT hr = S_OK;
+
+ INT_PTR cbDataSize = pDataEnd - pData;
+ if (cbDataSize > 4)
+ { // Compressed token cannot be bigger than 4 bytes
+ cbDataSize = 4;
+ }
+ DWORD dwDataSize = (DWORD)cbDataSize;
+
+ ULONG cbTokenOutLength;
+ IfFailRet(CorSigUncompressToken(
+ pData,
+ dwDataSize,
+ ptkTokenOut,
+ &cbTokenOutLength));
+ pData += cbTokenOutLength;
+
+ return hr;
+} // CorSigUncompressToken_EndPtr
+
+#endif // __CORHLPRPRIV_H__