summaryrefslogtreecommitdiff
path: root/src/palrt
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/palrt
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/palrt')
-rw-r--r--src/palrt/.gitmirror1
-rw-r--r--src/palrt/CMakeLists.txt23
-rw-r--r--src/palrt/bstr.cpp265
-rw-r--r--src/palrt/coguid.cpp205
-rw-r--r--src/palrt/comem.cpp23
-rw-r--r--src/palrt/common.h19
-rw-r--r--src/palrt/convert.h127
-rw-r--r--src/palrt/decarith.cpp1268
-rw-r--r--src/palrt/decconv.cpp603
-rw-r--r--src/palrt/guid.cpp27
-rw-r--r--src/palrt/guiddef.h27
-rw-r--r--src/palrt/memorystream.cpp318
-rw-r--r--src/palrt/path.cpp643
-rw-r--r--src/palrt/shlwapip.h89
-rw-r--r--src/palrt/shstr.h139
-rw-r--r--src/palrt/urlpars.cpp322
16 files changed, 4099 insertions, 0 deletions
diff --git a/src/palrt/.gitmirror b/src/palrt/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/palrt/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/palrt/CMakeLists.txt b/src/palrt/CMakeLists.txt
new file mode 100644
index 0000000000..231e95960f
--- /dev/null
+++ b/src/palrt/CMakeLists.txt
@@ -0,0 +1,23 @@
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_definitions(-DNO_CRT)
+
+set(PALRT_SOURCES
+ bstr.cpp
+ coguid.cpp
+ comem.cpp
+ decarith.cpp
+ decconv.cpp
+ guid.cpp
+ memorystream.cpp
+ path.cpp
+ urlpars.cpp
+)
+
+add_compile_options(-fPIC)
+
+add_library(palrt
+ STATIC
+ ${PALRT_SOURCES}
+)
diff --git a/src/palrt/bstr.cpp b/src/palrt/bstr.cpp
new file mode 100644
index 0000000000..d15f0ef29e
--- /dev/null
+++ b/src/palrt/bstr.cpp
@@ -0,0 +1,265 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: bstr.cpp
+//
+// ===========================================================================
+
+
+/*++
+
+Abstract:
+
+ PALRT BSTR support
+
+Revision History:
+
+--*/
+
+#include "common.h"
+#include "intsafe.h"
+
+#define CCH_BSTRMAX 0x7FFFFFFF // 4 + (0x7ffffffb + 1 ) * 2 ==> 0xFFFFFFFC
+#define CB_BSTRMAX 0xFFFFFFFa // 4 + (0xfffffff6 + 2) ==> 0xFFFFFFFC
+
+#define WIN32_ALLOC_ALIGN (16 - 1)
+
+inline HRESULT CbSysStringSize(ULONG cchSize, BOOL isByteLen, ULONG *result)
+{
+ if (result == NULL)
+ return E_INVALIDARG;
+
+ // +2 for the null terminator
+ // + DWORD_PTR to store the byte length of the string
+ int constant = sizeof(WCHAR) + sizeof(DWORD_PTR) + WIN32_ALLOC_ALIGN;
+
+ if (isByteLen)
+ {
+ if (SUCCEEDED(ULongAdd(constant, cchSize, result)))
+ {
+ *result = *result & ~WIN32_ALLOC_ALIGN;
+ return NOERROR;
+ }
+ }
+ else
+ {
+ ULONG temp = 0; // should not use in-place addition in ULongAdd
+ if (SUCCEEDED(ULongMult(cchSize, sizeof(WCHAR), &temp)) &
+ SUCCEEDED(ULongAdd(temp, constant, result)))
+ {
+ *result = *result & ~WIN32_ALLOC_ALIGN;
+ return NOERROR;
+ }
+ }
+ return INTSAFE_E_ARITHMETIC_OVERFLOW;
+}
+
+/***
+*BSTR SysAllocStringLen(char*, unsigned int)
+*Purpose:
+* Allocation a bstr of the given length and initialize with
+* the pasted in string
+*
+*Entry:
+* [optional]
+*
+*Exit:
+* return value = BSTR, NULL if the allocation failed.
+*
+***********************************************************************/
+STDAPI_(BSTR) SysAllocStringLen(const OLECHAR *psz, UINT len)
+{
+
+ BSTR bstr;
+ DWORD cbTotal = 0;
+
+ if (FAILED(CbSysStringSize(len, FALSE, &cbTotal)))
+ return NULL;
+
+ bstr = (OLECHAR *)HeapAlloc(GetProcessHeap(), 0, cbTotal);
+
+ if(bstr != NULL){
+
+#if defined(_WIN64)
+ // NOTE: There are some apps which peek back 4 bytes to look at the size of the BSTR. So, in case of 64-bit code,
+ // we need to ensure that the BSTR length can be found by looking one DWORD before the BSTR pointer.
+ *(DWORD_PTR *)bstr = (DWORD_PTR) 0;
+ bstr = (BSTR) ((char *) bstr + sizeof (DWORD));
+#endif
+ *(DWORD FAR*)bstr = (DWORD)len * sizeof(OLECHAR);
+
+ bstr = (BSTR) ((char*) bstr + sizeof(DWORD));
+
+ if(psz != NULL){
+ memcpy(bstr, psz, len * sizeof(OLECHAR));
+ }
+
+ bstr[len] = '\0'; // always 0 terminate
+ }
+
+ return bstr;
+}
+
+/***
+*BSTR SysAllocString(char*)
+*Purpose:
+* Allocation a bstr using the passed in string
+*
+*Entry:
+* String to create a bstr for
+*
+*Exit:
+* return value = BSTR, NULL if allocation failed
+*
+***********************************************************************/
+STDAPI_(BSTR) SysAllocString(const OLECHAR* psz)
+{
+ if(psz == NULL)
+ return NULL;
+
+ return SysAllocStringLen(psz, (DWORD)wcslen(psz));
+}
+
+STDAPI_(BSTR)
+SysAllocStringByteLen(const char FAR* psz, unsigned int len)
+{
+ BSTR bstr;
+ DWORD cbTotal = 0;
+
+ if (FAILED(CbSysStringSize(len, TRUE, &cbTotal)))
+ return FALSE;
+
+ bstr = (OLECHAR *)HeapAlloc(GetProcessHeap(), 0, cbTotal);
+
+ if (bstr != NULL) {
+#if defined(_WIN64)
+ *(DWORD FAR*)((char *)bstr + sizeof (DWORD)) = (DWORD)len;
+#else
+ *(DWORD FAR*)bstr = (DWORD)len;
+#endif
+
+ bstr = (WCHAR*) ((char*) bstr + sizeof(DWORD_PTR));
+
+ if (psz != NULL) {
+ memcpy(bstr, psz, len);
+ }
+
+ // NULL-terminate with both a narrow and wide zero.
+ *((char *)bstr + len) = '\0';
+ *(WCHAR *)((char *)bstr + ((len + 1) & ~1)) = 0;
+ }
+
+ return bstr;
+}
+
+/***
+*void SysFreeString(BSTR)
+*Purpose:
+* Free the given BSTR.
+*
+*Entry:
+* bstr = the BSTR to free
+*
+*Exit:
+* None
+*
+***********************************************************************/
+STDAPI_(void) SysFreeString(BSTR bstr)
+{
+ if(bstr == NULL)
+ return;
+ HeapFree(GetProcessHeap(), 0, (BYTE *)bstr-sizeof(DWORD_PTR));
+}
+
+/***
+*unsigned int SysStringLen(BSTR)
+*Purpose:
+* return the length in characters of the given BSTR.
+*
+*Entry:
+* bstr = the BSTR to return the length of
+*
+*Exit:
+* return value = unsigned int, length in characters.
+*
+***********************************************************************/
+STDAPI_(unsigned int)
+SysStringLen(BSTR bstr)
+{
+ if(bstr == NULL)
+ return 0;
+ return (unsigned int)((((DWORD FAR*)bstr)[-1]) / sizeof(OLECHAR));
+}
+
+/***
+*unsigned int SysStringByteLen(BSTR)
+*Purpose:
+* return the size in bytes of the given BSTR.
+*
+*Entry:
+* bstr = the BSTR to return the size of
+*
+*Exit:
+* return value = unsigned int, size in bytes.
+*
+***********************************************************************/
+STDAPI_(unsigned int)
+SysStringByteLen(BSTR bstr)
+{
+ if(bstr == NULL)
+ return 0;
+ return (unsigned int)(((DWORD FAR*)bstr)[-1]);
+}
+
+extern "C" HRESULT
+ErrStringCopy(BSTR bstrSource, BSTR FAR *pbstrOut)
+{
+ if (bstrSource == NULL) {
+ *pbstrOut = NULL;
+ return NOERROR;
+ }
+ if ((*pbstrOut = SysAllocStringLen(bstrSource,
+ SysStringLen(bstrSource))) == NULL)
+ return E_OUTOFMEMORY;
+
+ return NOERROR;
+}
+
+/***
+*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" HRESULT
+ErrSysAllocString(const OLECHAR FAR* psz, BSTR FAR* pbstrOut)
+{
+ if(psz == NULL){
+ *pbstrOut = NULL;
+ return NOERROR;
+ }
+
+ if((*pbstrOut = SysAllocString(psz)) == NULL)
+ return E_OUTOFMEMORY;
+
+ return NOERROR;
+}
diff --git a/src/palrt/coguid.cpp b/src/palrt/coguid.cpp
new file mode 100644
index 0000000000..69398fb427
--- /dev/null
+++ b/src/palrt/coguid.cpp
@@ -0,0 +1,205 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: coguid.cpp
+//
+// misc guid functions for PALRT
+// ===========================================================================
+
+#include "common.h"
+
+STDAPI CoCreateGuid(OUT GUID * pguid)
+{
+ if (!PAL_Random(FALSE, pguid, sizeof(GUID)))
+ {
+ DWORD dwError = GetLastError();
+ return HRESULT_FROM_WIN32(dwError);
+ }
+
+ return S_OK;
+}
+
+STDAPI_(int) StringFromGUID2(REFGUID rguid, LPOLESTR lptsz, int cchMax)
+{
+ if (cchMax < 39)
+ return 0;
+
+ return swprintf(lptsz, W("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"),
+ rguid.Data1, rguid.Data2, rguid.Data3,
+ rguid.Data4[0], rguid.Data4[1],
+ rguid.Data4[2], rguid.Data4[3],
+ rguid.Data4[4], rguid.Data4[5],
+ rguid.Data4[6], rguid.Data4[7]) + 1;
+}
+
+static BOOL wUUIDFromString(LPCWSTR lpsz, GUID * pguid);
+static BOOL wGUIDFromString(LPCWSTR lpsz, GUID * pguid);
+
+static BOOL HexStringToDword(LPCWSTR FAR& lpsz, DWORD FAR& Value,
+ int cDigits, WCHAR chDelim);
+
+//+-------------------------------------------------------------------------
+//
+// Function: IIDFromString
+//
+// Synopsis: converts string {...} form int guid
+//
+// Arguments: [lpsz] - ptr to buffer for results
+// [lpclsid] - the guid to convert
+//
+// Returns: NOERROR
+// CO_E_CLASSSTRING
+//
+//--------------------------------------------------------------------------
+STDAPI IIDFromString(LPWSTR lpsz, CLSID * lpclsid)
+{
+ if (lpsz == NULL)
+ {
+ *lpclsid = CLSID_NULL;
+ return NOERROR;
+ }
+
+ if (*lpsz == 0)
+ {
+ return(CO_E_CLASSSTRING);
+ }
+
+ return wGUIDFromString(lpsz,lpclsid)
+ ? NOERROR : CO_E_CLASSSTRING;
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: wGUIDFromString (internal)
+//
+// Synopsis: Parse GUID such as {00000000-0000-0000-0000-000000000000}
+//
+// Arguments: [lpsz] - the guid string to convert
+// [pguid] - guid to return
+//
+// Returns: TRUE if successful
+//
+//--------------------------------------------------------------------------
+static BOOL wGUIDFromString(LPCWSTR lpsz, GUID * pguid)
+{
+ if (*lpsz++ != '{' )
+ return FALSE;
+
+ if (wUUIDFromString(lpsz, pguid) != TRUE)
+ return FALSE;
+
+ lpsz +=36;
+
+ if (*lpsz++ != '}' )
+ return FALSE;
+
+ if (*lpsz != '\0')
+ return FALSE;
+
+ return TRUE;
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: wUUIDFromString (internal)
+//
+// Synopsis: Parse UUID such as 00000000-0000-0000-0000-000000000000
+//
+// Arguments: [lpsz] - Supplies the UUID string to convert
+// [pguid] - Returns the GUID.
+//
+// Returns: TRUE if successful
+//
+//--------------------------------------------------------------------------
+static BOOL wUUIDFromString(LPCWSTR lpsz, GUID * pguid)
+{
+ DWORD dw;
+
+ if (!HexStringToDword(lpsz, pguid->Data1, sizeof(DWORD)*2, '-'))
+ return FALSE;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(WORD)*2, '-'))
+ return FALSE;
+ pguid->Data2 = (WORD)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(WORD)*2, '-'))
+ return FALSE;
+ pguid->Data3 = (WORD)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[0] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, '-'))
+ return FALSE;
+ pguid->Data4[1] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[2] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[3] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[4] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[5] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[6] = (BYTE)dw;
+
+ if (!HexStringToDword(lpsz, dw, sizeof(BYTE)*2, 0))
+ return FALSE;
+ pguid->Data4[7] = (BYTE)dw;
+
+ return TRUE;
+}
+
+//+-------------------------------------------------------------------------
+//
+// Function: HexStringToDword (private)
+//
+// Synopsis: scan lpsz for a number of hex digits (at most 8); update lpsz
+// return value in Value; check for chDelim;
+//
+// Arguments: [lpsz] - the hex string to convert
+// [Value] - the returned value
+// [cDigits] - count of digits
+//
+// Returns: TRUE for success
+//
+//--------------------------------------------------------------------------
+static BOOL HexStringToDword(LPCWSTR FAR& lpsz, DWORD FAR& Value,
+ int cDigits, WCHAR chDelim)
+{
+ int Count;
+
+ Value = 0;
+ for (Count = 0; Count < cDigits; Count++, lpsz++)
+ {
+ if (*lpsz >= '0' && *lpsz <= '9')
+ Value = (Value << 4) + *lpsz - '0';
+ else if (*lpsz >= 'A' && *lpsz <= 'F')
+ Value = (Value << 4) + *lpsz - 'A' + 10;
+ else if (*lpsz >= 'a' && *lpsz <= 'f')
+ Value = (Value << 4) + *lpsz - 'a' + 10;
+ else
+ return(FALSE);
+ }
+
+ if (chDelim != 0)
+ return *lpsz++ == chDelim;
+ else
+ return TRUE;
+}
diff --git a/src/palrt/comem.cpp b/src/palrt/comem.cpp
new file mode 100644
index 0000000000..1cabedaaea
--- /dev/null
+++ b/src/palrt/comem.cpp
@@ -0,0 +1,23 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: comem.cpp
+//
+// ===========================================================================
+
+#include "common.h"
+
+STDAPI_(LPVOID) CoTaskMemAlloc(SIZE_T cb)
+{
+ return LocalAlloc(LMEM_FIXED, cb);
+}
+
+STDAPI_(void) CoTaskMemFree(LPVOID pv)
+{
+ LocalFree(pv);
+}
diff --git a/src/palrt/common.h b/src/palrt/common.h
new file mode 100644
index 0000000000..06127b28b9
--- /dev/null
+++ b/src/palrt/common.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// common.h
+//
+
+//
+// Common include file for the palrt code.
+//*****************************************************************************
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <switches.h>
+#include <winwrap.h>
+#include "shlwapip.h"
+#endif // _COMMON_H_
diff --git a/src/palrt/convert.h b/src/palrt/convert.h
new file mode 100644
index 0000000000..811c4a0986
--- /dev/null
+++ b/src/palrt/convert.h
@@ -0,0 +1,127 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: convert.h
+//
+// ===========================================================================
+
+/***
+*Purpose:
+* Common header (shared by convert.cpp and decimal.cpp) for numeric
+* conversions and other math stuff.
+*
+*Revision History:
+*
+*
+*
+*Implementation Notes:
+*
+*****************************************************************************/
+
+#ifndef _CONVERT_H_ /* { */
+#define _CONVERT_H_
+
+//***********************************************************************
+//
+// Structures
+//
+
+typedef union{
+ struct {
+#if BIGENDIAN
+ ULONG sign:1;
+ ULONG exp:11;
+ ULONG mantHi:20;
+ ULONG mantLo;
+#else // BIGENDIAN
+ ULONG mantLo;
+ ULONG mantHi:20;
+ ULONG exp:11;
+ ULONG sign:1;
+#endif
+ } u;
+ double dbl;
+} DBLSTRUCT;
+
+// Intializer for a DBLSTRUCT
+#if BIGENDIAN
+#define DEFDS(Lo, Hi, exp, sign) { {sign, exp, Hi, Lo } }
+#else
+#define DEFDS(Lo, Hi, exp, sign) { {Lo, Hi, exp, sign} }
+#endif
+
+
+typedef struct {
+#if BIGENDIAN
+ ULONG sign:1;
+ ULONG exp:8;
+ ULONG mant:23;
+#else
+ ULONG mant:23;
+ ULONG exp:8;
+ ULONG sign:1;
+#endif
+} SNGSTRUCT;
+
+
+
+typedef union {
+ DWORDLONG int64;
+ struct {
+#ifdef BIGENDIAN
+ ULONG Hi;
+ ULONG Lo;
+#else
+ ULONG Lo;
+ ULONG Hi;
+#endif
+ } u;
+} SPLIT64;
+
+
+
+//***********************************************************************
+//
+// Constants
+//
+
+static const ULONG ulTenToTenDiv4 = 2500000000U;
+static const ULONG ulTenToNine = 1000000000U;
+
+//***********************************************************************
+//
+// Inlines for Decimal
+//
+
+
+#ifndef UInt32x32To64
+#define UInt32x32To64(a, b) ((DWORDLONG)((DWORD)(a)) * (DWORDLONG)((DWORD)(b)))
+#endif
+
+#define Div64by32(num, den) ((ULONG)((DWORDLONG)(num) / (ULONG)(den)))
+#define Mod64by32(num, den) ((ULONG)((DWORDLONG)(num) % (ULONG)(den)))
+
+inline DWORDLONG DivMod32by32(ULONG num, ULONG den)
+{
+ SPLIT64 sdl;
+
+ sdl.u.Lo = num / den;
+ sdl.u.Hi = num % den;
+ return sdl.int64;
+}
+
+inline DWORDLONG DivMod64by32(DWORDLONG num, ULONG den)
+{
+ SPLIT64 sdl;
+
+ sdl.u.Lo = Div64by32(num, den);
+ sdl.u.Hi = Mod64by32(num, den);
+ return sdl.int64;
+}
+
+#endif /* } _CONVERT_H_ */
diff --git a/src/palrt/decarith.cpp b/src/palrt/decarith.cpp
new file mode 100644
index 0000000000..03b206924d
--- /dev/null
+++ b/src/palrt/decarith.cpp
@@ -0,0 +1,1268 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: decarith.cpp
+//
+// ===========================================================================
+/***
+*
+*Purpose:
+* Implement arithmetic for Decimal data type.
+*
+*Implementation Notes:
+*
+*****************************************************************************/
+
+#include "common.h"
+
+#include <oleauto.h>
+#include "convert.h"
+
+//***********************************************************************
+//
+// Additional Decimal and Int64 definitions
+//
+#define COPYDEC(dest, src) {DECIMAL_SIGNSCALE(dest) = DECIMAL_SIGNSCALE(src); DECIMAL_HI32(dest) = DECIMAL_HI32(src); \
+ DECIMAL_MID32(dest) = DECIMAL_MID32(src); DECIMAL_LO32(dest) = DECIMAL_LO32(src); }
+
+#define DEC_SCALE_MAX 28
+#define POWER10_MAX 9
+
+// The following functions are defined in the classlibnative\bcltype\decimal.cpp
+ULONG Div96By32(ULONG *rgulNum, ULONG ulDen);
+ULONG Div96By64(ULONG *rgulNum, SPLIT64 sdlDen);
+ULONG Div128By96(ULONG *rgulNum, ULONG *rgulDen);
+int ScaleResult(ULONG *rgulRes, int iHiRes, int iScale);
+ULONG IncreaseScale(ULONG *rgulNum, ULONG ulPwr);
+
+//***********************************************************************
+//
+// Data tables
+//
+
+static ULONG rgulPower10[POWER10_MAX+1] = {1, 10, 100, 1000, 10000, 100000, 1000000,
+ 10000000, 100000000, 1000000000};
+
+struct DECOVFL
+{
+ ULONG Hi;
+ ULONG Mid;
+};
+
+static DECOVFL PowerOvfl[] = {
+// This is a table of the largest values that can be in the upper two
+// ULONGs of a 96-bit number that will not overflow when multiplied
+// by a given power. For the upper word, this is a table of
+// 2^32 / 10^n for 1 <= n <= 9. For the lower word, this is the
+// remaining fraction part * 2^32. 2^32 = 4294967296.
+//
+ { 429496729UL, 2576980377UL }, // 10^1 remainder 0.6
+ { 42949672UL, 4123168604UL }, // 10^2 remainder 0.16
+ { 4294967UL, 1271310319UL }, // 10^3 remainder 0.616
+ { 429496UL, 3133608139UL }, // 10^4 remainder 0.1616
+ { 42949UL, 2890341191UL }, // 10^5 remainder 0.51616
+ { 4294UL, 4154504685UL }, // 10^6 remainder 0.551616
+ { 429UL, 2133437386UL }, // 10^7 remainder 0.9551616
+ { 42UL, 4078814305UL }, // 10^8 remainder 0.09991616
+// { 4UL, 1266874889UL }, // 10^9 remainder 0.709551616
+};
+
+#define OVFL_MAX_9_HI 4
+#define OVFL_MAX_9_MID 1266874889
+
+#define OVFL_MAX_5_HI 42949
+#define OVFL_MAX_5_MID 2890341191
+
+#define OVFL_MAX_1_HI 429496729
+
+
+
+//***********************************************************************
+//
+// static helper functions
+//
+
+/***
+* FullDiv64By32
+*
+* Entry:
+* pdlNum - Pointer to 64-bit dividend
+* ulDen - 32-bit divisor
+*
+* Purpose:
+* Do full divide, yielding 64-bit result and 32-bit remainder.
+*
+* Exit:
+* Quotient overwrites dividend.
+* Returns remainder.
+*
+* Exceptions:
+* None.
+*
+***********************************************************************/
+
+ULONG FullDiv64By32(DWORDLONG *pdlNum, ULONG ulDen)
+{
+ SPLIT64 sdlTmp;
+ SPLIT64 sdlRes;
+
+ sdlTmp.int64 = *pdlNum;
+ sdlRes.u.Hi = 0;
+
+ if (sdlTmp.u.Hi >= ulDen) {
+ // DivMod64by32 returns quotient in Lo, remainder in Hi.
+ //
+ sdlRes.u.Lo = sdlTmp.u.Hi;
+ sdlRes.int64 = DivMod64by32(sdlRes.int64, ulDen);
+ sdlTmp.u.Hi = sdlRes.u.Hi;
+ sdlRes.u.Hi = sdlRes.u.Lo;
+ }
+
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
+ sdlRes.u.Lo = sdlTmp.u.Lo;
+ *pdlNum = sdlRes.int64;
+ return sdlTmp.u.Hi;
+}
+
+
+
+
+/***
+* SearchScale
+*
+* Entry:
+* ulResHi - Top ULONG of quotient
+* ulResLo - Middle ULONG of quotient
+* iScale - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX
+*
+* Purpose:
+* Determine the max power of 10, <= 9, that the quotient can be scaled
+* up by and still fit in 96 bits.
+*
+* Exit:
+* Returns power of 10 to scale by, -1 if overflow error.
+*
+***********************************************************************/
+
+int SearchScale(ULONG ulResHi, ULONG ulResLo, int iScale)
+{
+ int iCurScale;
+
+ // Quick check to stop us from trying to scale any more.
+ //
+ if (ulResHi > OVFL_MAX_1_HI || iScale >= DEC_SCALE_MAX) {
+ iCurScale = 0;
+ goto HaveScale;
+ }
+
+ if (iScale > DEC_SCALE_MAX - 9) {
+ // We can't scale by 10^9 without exceeding the max scale factor.
+ // See if we can scale to the max. If not, we'll fall into
+ // standard search for scale factor.
+ //
+ iCurScale = DEC_SCALE_MAX - iScale;
+ if (ulResHi < PowerOvfl[iCurScale - 1].Hi)
+ goto HaveScale;
+
+ if (ulResHi == PowerOvfl[iCurScale - 1].Hi) {
+ UpperEq:
+ if (ulResLo >= PowerOvfl[iCurScale - 1].Mid)
+ iCurScale--;
+ goto HaveScale;
+ }
+ }
+ else if (ulResHi < OVFL_MAX_9_HI || (ulResHi == OVFL_MAX_9_HI &&
+ ulResLo < OVFL_MAX_9_MID))
+ return 9;
+
+ // Search for a power to scale by < 9. Do a binary search
+ // on PowerOvfl[].
+ //
+ iCurScale = 5;
+ if (ulResHi < OVFL_MAX_5_HI)
+ iCurScale = 7;
+ else if (ulResHi > OVFL_MAX_5_HI)
+ iCurScale = 3;
+ else
+ goto UpperEq;
+
+ // iCurScale is 3 or 7.
+ //
+ if (ulResHi < PowerOvfl[iCurScale - 1].Hi)
+ iCurScale++;
+ else if (ulResHi > PowerOvfl[iCurScale - 1].Hi)
+ iCurScale--;
+ else
+ goto UpperEq;
+
+ // iCurScale is 2, 4, 6, or 8.
+ //
+ // In all cases, we already found we could not use the power one larger.
+ // So if we can use this power, it is the biggest, and we're done. If
+ // we can't use this power, the one below it is correct for all cases
+ // unless it's 10^1 -- we might have to go to 10^0 (no scaling).
+ //
+ if (ulResHi > PowerOvfl[iCurScale - 1].Hi)
+ iCurScale--;
+
+ if (ulResHi == PowerOvfl[iCurScale - 1].Hi)
+ goto UpperEq;
+
+HaveScale:
+ // iCurScale = largest power of 10 we can scale by without overflow,
+ // iCurScale < 9. See if this is enough to make scale factor
+ // positive if it isn't already.
+ //
+ if (iCurScale + iScale < 0)
+ iCurScale = -1;
+
+ return iCurScale;
+}
+
+/***
+* DecFixInt
+*
+* Entry:
+* pdecRes - Pointer to Decimal result location
+* pdecIn - Pointer to Decimal operand
+*
+* Purpose:
+* Chop the value to integer. Return remainder so Int() function
+* can round down if non-zero.
+*
+* Exit:
+* Returns remainder.
+*
+* Exceptions:
+* None.
+*
+***********************************************************************/
+
+ULONG DecFixInt(LPDECIMAL pdecRes, LPDECIMAL pdecIn)
+{
+ ULONG rgulNum[3];
+ ULONG ulRem;
+ ULONG ulPwr;
+ int iScale;
+
+ if (pdecIn->u.u.scale > 0) {
+ rgulNum[0] = pdecIn->v.v.Lo32;
+ rgulNum[1] = pdecIn->v.v.Mid32;
+ rgulNum[2] = pdecIn->Hi32;
+ iScale = pdecIn->u.u.scale;
+ pdecRes->u.u.sign = pdecIn->u.u.sign;
+ ulRem = 0;
+
+ do {
+ if (iScale > POWER10_MAX)
+ ulPwr = ulTenToNine;
+ else
+ ulPwr = rgulPower10[iScale];
+
+ ulRem |= Div96By32(rgulNum, ulPwr);
+ iScale -= 9;
+ }while (iScale > 0);
+
+ pdecRes->v.v.Lo32 = rgulNum[0];
+ pdecRes->v.v.Mid32 = rgulNum[1];
+ pdecRes->Hi32 = rgulNum[2];
+ pdecRes->u.u.scale = 0;
+
+ return ulRem;
+ }
+
+ COPYDEC(*pdecRes, *pdecIn)
+ return 0;
+}
+
+
+//***********************************************************************
+//
+//
+//
+
+//**********************************************************************
+//
+// VarDecMul - Decimal Multiply
+//
+//**********************************************************************
+
+STDAPI VarDecMul(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes)
+{
+ SPLIT64 sdlTmp;
+ SPLIT64 sdlTmp2;
+ SPLIT64 sdlTmp3;
+ int iScale;
+ int iHiProd;
+ ULONG ulPwr;
+ ULONG ulRemLo;
+ ULONG ulRemHi;
+ ULONG rgulProd[6];
+
+ iScale = pdecL->u.u.scale + pdecR->u.u.scale;
+
+ if ((pdecL->Hi32 | pdecL->v.v.Mid32 | pdecR->Hi32 | pdecR->v.v.Mid32) == 0)
+ {
+ // Upper 64 bits are zero.
+ //
+ sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Lo32, pdecR->v.v.Lo32);
+ if (iScale > DEC_SCALE_MAX)
+ {
+ // Result iScale is too big. Divide result by power of 10 to reduce it.
+ // If the amount to divide by is > 19 the result is guaranteed
+ // less than 1/2. [max value in 64 bits = 1.84E19]
+ //
+ iScale -= DEC_SCALE_MAX;
+ if (iScale > 19)
+ {
+ReturnZero:
+ DECIMAL_SETZERO(*pdecRes);
+ return NOERROR;
+ }
+ if (iScale > POWER10_MAX)
+ {
+ // Divide by 1E10 first, to get the power down to a 32-bit quantity.
+ // 1E10 itself doesn't fit in 32 bits, so we'll divide by 2.5E9 now
+ // then multiply the next divisor by 4 (which will be a max of 4E9).
+ //
+ ulRemLo = FullDiv64By32(&sdlTmp.int64, ulTenToTenDiv4);
+ ulPwr = rgulPower10[iScale - 10] << 2;
+ }
+ else
+ {
+ ulPwr = rgulPower10[iScale];
+ ulRemLo = 0;
+ }
+
+ // Power to divide by fits in 32 bits.
+ //
+ ulRemHi = FullDiv64By32(&sdlTmp.int64, ulPwr);
+
+ // Round result. See if remainder >= 1/2 of divisor.
+ // Divisor is a power of 10, so it is always even.
+ //
+ ulPwr >>= 1;
+ if (ulRemHi >= ulPwr && (ulRemHi > ulPwr || (ulRemLo | (sdlTmp.u.Lo & 1))))
+ sdlTmp.int64++;
+
+ iScale = DEC_SCALE_MAX;
+ }
+ DECIMAL_LO32(*pdecRes) = sdlTmp.u.Lo;
+ DECIMAL_MID32(*pdecRes) = sdlTmp.u.Hi;
+ DECIMAL_HI32(*pdecRes) = 0;
+ }
+ else
+ {
+
+ // At least one operand has bits set in the upper 64 bits.
+ //
+ // Compute and accumulate the 9 partial products into a
+ // 192-bit (24-byte) result.
+ //
+ // [l-h][l-m][l-l] left high, middle, low
+ // x [r-h][r-m][r-l] right high, middle, low
+ // ------------------------------
+ //
+ // [0-h][0-l] l-l * r-l
+ // [1ah][1al] l-l * r-m
+ // [1bh][1bl] l-m * r-l
+ // [2ah][2al] l-m * r-m
+ // [2bh][2bl] l-l * r-h
+ // [2ch][2cl] l-h * r-l
+ // [3ah][3al] l-m * r-h
+ // [3bh][3bl] l-h * r-m
+ // [4-h][4-l] l-h * r-h
+ // ------------------------------
+ // [p-5][p-4][p-3][p-2][p-1][p-0] prod[] array
+ //
+ sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Lo32, pdecR->v.v.Lo32);
+ rgulProd[0] = sdlTmp.u.Lo;
+
+ sdlTmp2.int64 = UInt32x32To64(pdecL->v.v.Lo32, pdecR->v.v.Mid32) + sdlTmp.u.Hi;
+
+ sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Mid32, pdecR->v.v.Lo32);
+ sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
+ rgulProd[1] = sdlTmp.u.Lo;
+ if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
+ sdlTmp2.u.Hi = 1;
+ else
+ sdlTmp2.u.Hi = 0;
+ sdlTmp2.u.Lo = sdlTmp.u.Hi;
+
+ sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Mid32, pdecR->v.v.Mid32) + sdlTmp2.int64;
+
+ if (pdecL->Hi32 | pdecR->Hi32) {
+ // Highest 32 bits is non-zero. Calculate 5 more partial products.
+ //
+ sdlTmp2.int64 = UInt32x32To64(pdecL->v.v.Lo32, pdecR->Hi32);
+ sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
+ if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
+ sdlTmp3.u.Hi = 1;
+ else
+ sdlTmp3.u.Hi = 0;
+
+ sdlTmp2.int64 = UInt32x32To64(pdecL->Hi32, pdecR->v.v.Lo32);
+ sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
+ rgulProd[2] = sdlTmp.u.Lo;
+ if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
+ sdlTmp3.u.Hi++;
+ sdlTmp3.u.Lo = sdlTmp.u.Hi;
+
+ sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Mid32, pdecR->Hi32);
+ sdlTmp.int64 += sdlTmp3.int64; // this could generate carry
+ if (sdlTmp.int64 < sdlTmp3.int64) // detect carry
+ sdlTmp3.u.Hi = 1;
+ else
+ sdlTmp3.u.Hi = 0;
+
+ sdlTmp2.int64 = UInt32x32To64(pdecL->Hi32, pdecR->v.v.Mid32);
+ sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
+ rgulProd[3] = sdlTmp.u.Lo;
+ if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
+ sdlTmp3.u.Hi++;
+ sdlTmp3.u.Lo = sdlTmp.u.Hi;
+
+ sdlTmp.int64 = UInt32x32To64(pdecL->Hi32, pdecR->Hi32) + sdlTmp3.int64;
+ rgulProd[4] = sdlTmp.u.Lo;
+ rgulProd[5] = sdlTmp.u.Hi;
+
+ iHiProd = 5;
+ }
+ else {
+ rgulProd[2] = sdlTmp.u.Lo;
+ rgulProd[3] = sdlTmp.u.Hi;
+ iHiProd = 3;
+ }
+
+ // Check for leading zero ULONGs on the product
+ //
+ while (rgulProd[iHiProd] == 0) {
+ iHiProd--;
+ if (iHiProd < 0)
+ goto ReturnZero;
+ }
+
+ iScale = ScaleResult(rgulProd, iHiProd, iScale);
+ if (iScale == -1)
+ return DISP_E_OVERFLOW;
+
+ pdecRes->v.v.Lo32 = rgulProd[0];
+ pdecRes->v.v.Mid32 = rgulProd[1];
+ pdecRes->Hi32 = rgulProd[2];
+ }
+
+ pdecRes->u.u.sign = pdecR->u.u.sign ^ pdecL->u.u.sign;
+ pdecRes->u.u.scale = (char)iScale;
+ return NOERROR;
+}
+
+
+//**********************************************************************
+//
+// VarDecAdd - Decimal Addition
+// VarDecSub - Decimal Subtraction
+//
+//**********************************************************************
+
+static HRESULT DecAddSub(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes, char bSign);
+
+STDAPI VarDecAdd(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes)
+{
+ return DecAddSub(pdecL, pdecR, pdecRes, 0);
+}
+
+
+STDAPI VarDecSub(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes)
+{
+ return DecAddSub(pdecL, pdecR, pdecRes, DECIMAL_NEG);
+}
+
+
+static HRESULT DecAddSub(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes, char bSign)
+{
+ ULONG rgulNum[6];
+ ULONG ulPwr;
+ int iScale;
+ int iHiProd;
+ int iCur;
+ SPLIT64 sdlTmp;
+ DECIMAL decRes;
+ DECIMAL decTmp;
+ LPDECIMAL pdecTmp;
+
+ bSign ^= (pdecR->u.u.sign ^ pdecL->u.u.sign) & DECIMAL_NEG;
+
+ if (pdecR->u.u.scale == pdecL->u.u.scale) {
+ // Scale factors are equal, no alignment necessary.
+ //
+ decRes.u.signscale = pdecL->u.signscale;
+
+AlignedAdd:
+ if (bSign) {
+ // Signs differ - subtract
+ //
+ DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecL) - DECIMAL_LO64_GET(*pdecR));
+ DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) - DECIMAL_HI32(*pdecR);
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) > DECIMAL_LO64_GET(*pdecL)) {
+ decRes.Hi32--;
+ if (decRes.Hi32 >= pdecL->Hi32)
+ goto SignFlip;
+ }
+ else if (decRes.Hi32 > pdecL->Hi32) {
+ // Got negative result. Flip its sign.
+ //
+SignFlip:
+ DECIMAL_LO64_SET(decRes, -(LONGLONG)DECIMAL_LO64_GET(decRes));
+ decRes.Hi32 = ~decRes.Hi32;
+ if (DECIMAL_LO64_GET(decRes) == 0)
+ decRes.Hi32++;
+ decRes.u.u.sign ^= DECIMAL_NEG;
+ }
+
+ }
+ else {
+ // Signs are the same - add
+ //
+ DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecL) + DECIMAL_LO64_GET(*pdecR));
+ decRes.Hi32 = pdecL->Hi32 + pdecR->Hi32;
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) < DECIMAL_LO64_GET(*pdecL)) {
+ decRes.Hi32++;
+ if (decRes.Hi32 <= pdecL->Hi32)
+ goto AlignedScale;
+ }
+ else if (decRes.Hi32 < pdecL->Hi32) {
+AlignedScale:
+ // The addition carried above 96 bits. Divide the result by 10,
+ // dropping the scale factor.
+ //
+ if (decRes.u.u.scale == 0)
+ return DISP_E_OVERFLOW;
+ decRes.u.u.scale--;
+
+ sdlTmp.u.Lo = decRes.Hi32;
+ sdlTmp.u.Hi = 1;
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ decRes.Hi32 = sdlTmp.u.Lo;
+
+ sdlTmp.u.Lo = decRes.v.v.Mid32;
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ decRes.v.v.Mid32 = sdlTmp.u.Lo;
+
+ sdlTmp.u.Lo = decRes.v.v.Lo32;
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ decRes.v.v.Lo32 = sdlTmp.u.Lo;
+
+ // See if we need to round up.
+ //
+ if (sdlTmp.u.Hi >= 5 && (sdlTmp.u.Hi > 5 || (decRes.v.v.Lo32 & 1))) {
+ DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(decRes)+1)
+ if (DECIMAL_LO64_GET(decRes) == 0)
+ decRes.Hi32++;
+ }
+ }
+ }
+ }
+ else {
+ // Scale factors are not equal. Assume that a larger scale
+ // factor (more decimal places) is likely to mean that number
+ // is smaller. Start by guessing that the right operand has
+ // the larger scale factor. The result will have the larger
+ // scale factor.
+ //
+ decRes.u.u.scale = pdecR->u.u.scale; // scale factor of "smaller"
+ decRes.u.u.sign = pdecL->u.u.sign; // but sign of "larger"
+ iScale = decRes.u.u.scale - pdecL->u.u.scale;
+
+ if (iScale < 0) {
+ // Guessed scale factor wrong. Swap operands.
+ //
+ iScale = -iScale;
+ decRes.u.u.scale = pdecL->u.u.scale;
+ decRes.u.u.sign ^= bSign;
+ pdecTmp = pdecR;
+ pdecR = pdecL;
+ pdecL = pdecTmp;
+ }
+
+ // *pdecL will need to be multiplied by 10^iScale so
+ // it will have the same scale as *pdecR. We could be
+ // extending it to up to 192 bits of precision.
+ //
+ if (iScale <= POWER10_MAX) {
+ // Scaling won't make it larger than 4 ULONGs
+ //
+ ulPwr = rgulPower10[iScale];
+ DECIMAL_LO64_SET(decTmp, UInt32x32To64(pdecL->v.v.Lo32, ulPwr));
+ sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Mid32, ulPwr);
+ sdlTmp.int64 += decTmp.v.v.Mid32;
+ decTmp.v.v.Mid32 = sdlTmp.u.Lo;
+ decTmp.Hi32 = sdlTmp.u.Hi;
+ sdlTmp.int64 = UInt32x32To64(pdecL->Hi32, ulPwr);
+ sdlTmp.int64 += decTmp.Hi32;
+ if (sdlTmp.u.Hi == 0) {
+ // Result fits in 96 bits. Use standard aligned add.
+ //
+ decTmp.Hi32 = sdlTmp.u.Lo;
+ pdecL = &decTmp;
+ goto AlignedAdd;
+ }
+ rgulNum[0] = decTmp.v.v.Lo32;
+ rgulNum[1] = decTmp.v.v.Mid32;
+ rgulNum[2] = sdlTmp.u.Lo;
+ rgulNum[3] = sdlTmp.u.Hi;
+ iHiProd = 3;
+ }
+ else {
+ // Have to scale by a bunch. Move the number to a buffer
+ // where it has room to grow as it's scaled.
+ //
+ rgulNum[0] = pdecL->v.v.Lo32;
+ rgulNum[1] = pdecL->v.v.Mid32;
+ rgulNum[2] = pdecL->Hi32;
+ iHiProd = 2;
+
+ // Scan for zeros in the upper words.
+ //
+ if (rgulNum[2] == 0) {
+ iHiProd = 1;
+ if (rgulNum[1] == 0) {
+ iHiProd = 0;
+ if (rgulNum[0] == 0) {
+ // Left arg is zero, return right.
+ //
+ DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecR));
+ decRes.Hi32 = pdecR->Hi32;
+ decRes.u.u.sign ^= bSign;
+ goto RetDec;
+ }
+ }
+ }
+
+ // Scaling loop, up to 10^9 at a time. iHiProd stays updated
+ // with index of highest non-zero ULONG.
+ //
+ for (; iScale > 0; iScale -= POWER10_MAX) {
+ if (iScale > POWER10_MAX)
+ ulPwr = ulTenToNine;
+ else
+ ulPwr = rgulPower10[iScale];
+
+ sdlTmp.u.Hi = 0;
+ for (iCur = 0; iCur <= iHiProd; iCur++) {
+ sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.u.Hi;
+ rgulNum[iCur] = sdlTmp.u.Lo;
+ }
+
+ if (sdlTmp.u.Hi != 0)
+ // We're extending the result by another ULONG.
+ rgulNum[++iHiProd] = sdlTmp.u.Hi;
+ }
+ }
+
+ // Scaling complete, do the add. Could be subtract if signs differ.
+ //
+ sdlTmp.u.Lo = rgulNum[0];
+ sdlTmp.u.Hi = rgulNum[1];
+
+ if (bSign) {
+ // Signs differ, subtract.
+ //
+ DECIMAL_LO64_SET(decRes, sdlTmp.int64 - DECIMAL_LO64_GET(*pdecR));
+ decRes.Hi32 = rgulNum[2] - pdecR->Hi32;
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) > sdlTmp.int64) {
+ decRes.Hi32--;
+ if (decRes.Hi32 >= rgulNum[2])
+ goto LongSub;
+ }
+ else if (decRes.Hi32 > rgulNum[2]) {
+LongSub:
+ // If rgulNum has more than 96 bits of precision, then we need to
+ // carry the subtraction into the higher bits. If it doesn't,
+ // then we subtracted in the wrong order and have to flip the
+ // sign of the result.
+ //
+ if (iHiProd <= 2)
+ goto SignFlip;
+
+ iCur = 3;
+ while(rgulNum[iCur++]-- == 0);
+ if (rgulNum[iHiProd] == 0)
+ iHiProd--;
+ }
+ }
+ else {
+ // Signs the same, add.
+ //
+ DECIMAL_LO64_SET(decRes, sdlTmp.int64 + DECIMAL_LO64_GET(*pdecR));
+ decRes.Hi32 = rgulNum[2] + pdecR->Hi32;
+
+ // Propagate carry
+ //
+ if (DECIMAL_LO64_GET(decRes) < sdlTmp.int64) {
+ decRes.Hi32++;
+ if (decRes.Hi32 <= rgulNum[2])
+ goto LongAdd;
+ }
+ else if (decRes.Hi32 < rgulNum[2]) {
+LongAdd:
+ // Had a carry above 96 bits.
+ //
+ iCur = 3;
+ do {
+ if (iHiProd < iCur) {
+ rgulNum[iCur] = 1;
+ iHiProd = iCur;
+ break;
+ }
+ }while (++rgulNum[iCur++] == 0);
+ }
+ }
+
+ if (iHiProd > 2) {
+ rgulNum[0] = decRes.v.v.Lo32;
+ rgulNum[1] = decRes.v.v.Mid32;
+ rgulNum[2] = decRes.Hi32;
+ decRes.u.u.scale = ScaleResult(rgulNum, iHiProd, decRes.u.u.scale);
+ if (decRes.u.u.scale == (BYTE) -1)
+ return DISP_E_OVERFLOW;
+
+ decRes.v.v.Lo32 = rgulNum[0];
+ decRes.v.v.Mid32 = rgulNum[1];
+ decRes.Hi32 = rgulNum[2];
+ }
+ }
+
+RetDec:
+ COPYDEC(*pdecRes, decRes)
+ return NOERROR;
+}
+
+
+//**********************************************************************
+//
+// VarDecDiv - Decimal Divide
+//
+//**********************************************************************
+
+STDAPI VarDecDiv(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes)
+{
+ ULONG rgulQuo[3];
+ ULONG rgulQuoSave[3];
+ ULONG rgulRem[4];
+ ULONG rgulDivisor[3];
+ ULONG ulPwr;
+ ULONG ulTmp;
+ ULONG ulTmp1;
+ SPLIT64 sdlTmp;
+ SPLIT64 sdlDivisor;
+ int iScale;
+ int iCurScale;
+
+ iScale = pdecL->u.u.scale - pdecR->u.u.scale;
+ rgulDivisor[0] = pdecR->v.v.Lo32;
+ rgulDivisor[1] = pdecR->v.v.Mid32;
+ rgulDivisor[2] = pdecR->Hi32;
+
+ if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0) {
+ // Divisor is only 32 bits. Easy divide.
+ //
+ if (rgulDivisor[0] == 0)
+ return DISP_E_DIVBYZERO;
+
+ rgulQuo[0] = pdecL->v.v.Lo32;
+ rgulQuo[1] = pdecL->v.v.Mid32;
+ rgulQuo[2] = pdecL->Hi32;
+ rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]);
+
+ for (;;) {
+ if (rgulRem[0] == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale;
+ }
+ break;
+ }
+
+ // We have computed a quotient based on the natural scale
+ // ( <dividend scale> - <divisor scale> ). We have a non-zero
+ // remainder, so now we should increase the scale if possible to
+ // include more quotient bits.
+ //
+ // If it doesn't cause overflow, we'll loop scaling by 10^9 and
+ // computing more quotient bits as long as the remainder stays
+ // non-zero. If scaling by that much would cause overflow, we'll
+ // drop out of the loop and scale by as much as we can.
+ //
+ // Scaling by 10^9 will overflow if rgulQuo[2].rgulQuo[1] >= 2^32 / 10^9
+ // = 4.294 967 296. So the upper limit is rgulQuo[2] == 4 and
+ // rgulQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since
+ // quotient bits in rgulQuo[0] could be all 1's, then 1,266,874,888
+ // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is
+ // assured not to overflow.
+ //
+ iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], iScale);
+ if (iCurScale == 0) {
+ // No more scaling to be done, but remainder is non-zero.
+ // Round quotient.
+ //
+ ulTmp = rgulRem[0] << 1;
+ if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] &&
+ (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1)))) {
+RoundUp:
+ if (++rgulQuo[0] == 0)
+ if (++rgulQuo[1] == 0)
+ rgulQuo[2]++;
+ }
+ break;
+ }
+
+ if (iCurScale == -1)
+ return DISP_E_OVERFLOW;
+
+HaveScale:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0)
+ return DISP_E_OVERFLOW;
+
+ sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]);
+ rgulRem[0] = sdlTmp.u.Hi;
+
+ rgulQuo[0] += sdlTmp.u.Lo;
+ if (rgulQuo[0] < sdlTmp.u.Lo) {
+ if (++rgulQuo[1] == 0)
+ rgulQuo[2]++;
+ }
+ } // for (;;)
+ }
+ else {
+ // Divisor has bits set in the upper 64 bits.
+ //
+ // Divisor must be fully normalized (shifted so bit 31 of the most
+ // significant ULONG is 1). Locate the MSB so we know how much to
+ // normalize by. The dividend will be shifted by the same amount so
+ // the quotient is not changed.
+ //
+ if (rgulDivisor[2] == 0)
+ ulTmp = rgulDivisor[1];
+ else
+ ulTmp = rgulDivisor[2];
+
+ iCurScale = 0;
+ if (!(ulTmp & 0xFFFF0000)) {
+ iCurScale += 16;
+ ulTmp <<= 16;
+ }
+ if (!(ulTmp & 0xFF000000)) {
+ iCurScale += 8;
+ ulTmp <<= 8;
+ }
+ if (!(ulTmp & 0xF0000000)) {
+ iCurScale += 4;
+ ulTmp <<= 4;
+ }
+ if (!(ulTmp & 0xC0000000)) {
+ iCurScale += 2;
+ ulTmp <<= 2;
+ }
+ if (!(ulTmp & 0x80000000)) {
+ iCurScale++;
+ ulTmp <<= 1;
+ }
+
+ // Shift both dividend and divisor left by iCurScale.
+ //
+ sdlTmp.int64 = DECIMAL_LO64_GET(*pdecL) << iCurScale;
+ rgulRem[0] = sdlTmp.u.Lo;
+ rgulRem[1] = sdlTmp.u.Hi;
+ sdlTmp.u.Lo = pdecL->v.v.Mid32;
+ sdlTmp.u.Hi = pdecL->Hi32;
+ sdlTmp.int64 <<= iCurScale;
+ rgulRem[2] = sdlTmp.u.Hi;
+ rgulRem[3] = (pdecL->Hi32 >> (31 - iCurScale)) >> 1;
+
+ sdlDivisor.u.Lo = rgulDivisor[0];
+ sdlDivisor.u.Hi = rgulDivisor[1];
+ sdlDivisor.int64 <<= iCurScale;
+
+ if (rgulDivisor[2] == 0) {
+ // Have a 64-bit divisor in sdlDivisor. The remainder
+ // (currently 96 bits spread over 4 ULONGs) will be < divisor.
+ //
+ sdlTmp.u.Lo = rgulRem[2];
+ sdlTmp.u.Hi = rgulRem[3];
+
+ rgulQuo[2] = 0;
+ rgulQuo[1] = Div96By64(&rgulRem[1], sdlDivisor);
+ rgulQuo[0] = Div96By64(rgulRem, sdlDivisor);
+
+ for (;;) {
+ if ((rgulRem[0] | rgulRem[1]) == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale64;
+ }
+ break;
+ }
+
+ // Remainder is non-zero. Scale up quotient and remainder by
+ // powers of 10 so we can compute more significant bits.
+ //
+ iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], iScale);
+ if (iCurScale == 0) {
+ // No more scaling to be done, but remainder is non-zero.
+ // Round quotient.
+ //
+ sdlTmp.u.Lo = rgulRem[0];
+ sdlTmp.u.Hi = rgulRem[1];
+ if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 ||
+ (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1)))
+ goto RoundUp;
+ break;
+ }
+
+ if (iCurScale == -1)
+ return DISP_E_OVERFLOW;
+
+HaveScale64:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0)
+ return DISP_E_OVERFLOW;
+
+ rgulRem[2] = 0; // rem is 64 bits, IncreaseScale uses 96
+ IncreaseScale(rgulRem, ulPwr);
+ ulTmp = Div96By64(rgulRem, sdlDivisor);
+ rgulQuo[0] += ulTmp;
+ if (rgulQuo[0] < ulTmp)
+ if (++rgulQuo[1] == 0)
+ rgulQuo[2]++;
+
+ } // for (;;)
+ }
+ else {
+ // Have a 96-bit divisor in rgulDivisor[].
+ //
+ // Start by finishing the shift left by iCurScale.
+ //
+ sdlTmp.u.Lo = rgulDivisor[1];
+ sdlTmp.u.Hi = rgulDivisor[2];
+ sdlTmp.int64 <<= iCurScale;
+ rgulDivisor[0] = sdlDivisor.u.Lo;
+ rgulDivisor[1] = sdlDivisor.u.Hi;
+ rgulDivisor[2] = sdlTmp.u.Hi;
+
+ // The remainder (currently 96 bits spread over 4 ULONGs)
+ // will be < divisor.
+ //
+ rgulQuo[2] = 0;
+ rgulQuo[1] = 0;
+ rgulQuo[0] = Div128By96(rgulRem, rgulDivisor);
+
+ for (;;) {
+ if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0) {
+ if (iScale < 0) {
+ iCurScale = min(9, -iScale);
+ goto HaveScale96;
+ }
+ break;
+ }
+
+ // Remainder is non-zero. Scale up quotient and remainder by
+ // powers of 10 so we can compute more significant bits.
+ //
+ iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], iScale);
+ if (iCurScale == 0) {
+ // No more scaling to be done, but remainder is non-zero.
+ // Round quotient.
+ //
+ if (rgulRem[2] >= 0x80000000)
+ goto RoundUp;
+
+ ulTmp = rgulRem[0] > 0x80000000;
+ ulTmp1 = rgulRem[1] > 0x80000000;
+ rgulRem[0] <<= 1;
+ rgulRem[1] = (rgulRem[1] << 1) + ulTmp;
+ rgulRem[2] = (rgulRem[2] << 1) + ulTmp1;
+
+ if (rgulRem[2] > rgulDivisor[2] || rgulRem[2] == rgulDivisor[2] &&
+ (rgulRem[1] > rgulDivisor[1] || rgulRem[1] == rgulDivisor[1] &&
+ (rgulRem[0] > rgulDivisor[0] || rgulRem[0] == rgulDivisor[0] &&
+ (rgulQuo[0] & 1))))
+ goto RoundUp;
+ break;
+ }
+
+ if (iCurScale == -1)
+ return DISP_E_OVERFLOW;
+
+HaveScale96:
+ ulPwr = rgulPower10[iCurScale];
+ iScale += iCurScale;
+
+ if (IncreaseScale(rgulQuo, ulPwr) != 0)
+ return DISP_E_OVERFLOW;
+
+ rgulRem[3] = IncreaseScale(rgulRem, ulPwr);
+ ulTmp = Div128By96(rgulRem, rgulDivisor);
+ rgulQuo[0] += ulTmp;
+ if (rgulQuo[0] < ulTmp)
+ if (++rgulQuo[1] == 0)
+ rgulQuo[2]++;
+
+ } // for (;;)
+ }
+ }
+
+ // No more remainder. Try extracting any extra powers of 10 we may have
+ // added. We do this by trying to divide out 10^8, 10^4, 10^2, and 10^1.
+ // If a division by one of these powers returns a zero remainder, then
+ // we keep the quotient. If the remainder is not zero, then we restore
+ // the previous value.
+ //
+ // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10
+ // we can extract. We use this as a quick test on whether to try a
+ // given power.
+ //
+ while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 100000000) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 8;
+ }
+ else
+ break;
+ }
+
+ if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 10000) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 4;
+ }
+ }
+
+ if ((rgulQuo[0] & 3) == 0 && iScale >= 2) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 100) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 2;
+ }
+ }
+
+ if ((rgulQuo[0] & 1) == 0 && iScale >= 1) {
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 10) == 0) {
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 1;
+ }
+ }
+
+ pdecRes->Hi32 = rgulQuo[2];
+ pdecRes->v.v.Mid32 = rgulQuo[1];
+ pdecRes->v.v.Lo32 = rgulQuo[0];
+ pdecRes->u.u.scale = iScale;
+ pdecRes->u.u.sign = pdecL->u.u.sign ^ pdecR->u.u.sign;
+ return NOERROR;
+}
+
+
+//**********************************************************************
+//
+// VarDecAbs - Decimal Absolute Value
+//
+//**********************************************************************
+
+STDAPI VarDecAbs(LPDECIMAL pdecOprd, LPDECIMAL pdecRes)
+{
+ COPYDEC(*pdecRes, *pdecOprd)
+ pdecRes->u.u.sign &= ~DECIMAL_NEG;
+ return NOERROR;
+}
+
+
+//**********************************************************************
+//
+// VarDecFix - Decimal Fix (chop to integer)
+//
+//**********************************************************************
+
+STDAPI VarDecFix(LPDECIMAL pdecOprd, LPDECIMAL pdecRes)
+{
+ DecFixInt(pdecRes, pdecOprd);
+ return NOERROR;
+}
+
+
+//**********************************************************************
+//
+// VarDecInt - Decimal Int (round down to integer)
+//
+//**********************************************************************
+
+STDAPI VarDecInt(LPDECIMAL pdecOprd, LPDECIMAL pdecRes)
+{
+ if (DecFixInt(pdecRes, pdecOprd) != 0 && (pdecRes->u.u.sign & DECIMAL_NEG)) {
+ // We have chopped off a non-zero amount from a negative value. Since
+ // we round toward -infinity, we must increase the integer result by
+ // 1 to make it more negative. This will never overflow because
+ // in order to have a remainder, we must have had a non-zero scale factor.
+ // Our scale factor is back to zero now.
+ //
+ DECIMAL_LO64_SET(*pdecRes, DECIMAL_LO64_GET(*pdecRes) + 1);
+ if (DECIMAL_LO64_GET(*pdecRes) == 0)
+ pdecRes->Hi32++;
+ }
+ return NOERROR;
+}
+
+
+//**********************************************************************
+//
+// VarDecNeg - Decimal Negate
+//
+//**********************************************************************
+
+STDAPI VarDecNeg(LPDECIMAL pdecOprd, LPDECIMAL pdecRes)
+{
+ COPYDEC(*pdecRes, *pdecOprd)
+ pdecRes->u.u.sign ^= DECIMAL_NEG;
+ return NOERROR;
+}
+
+
+//**********************************************************************
+//
+// VarDecCmp - Decimal Compare
+//
+//**********************************************************************
+
+STDAPI VarDecCmp(LPDECIMAL pdecL, LPDECIMAL pdecR)
+{
+ ULONG ulSgnL;
+ ULONG ulSgnR;
+
+ // First check signs and whether either are zero. If both are
+ // non-zero and of the same sign, just use subtraction to compare.
+ //
+ ulSgnL = pdecL->v.v.Lo32 | pdecL->v.v.Mid32 | pdecL->Hi32;
+ ulSgnR = pdecR->v.v.Lo32 | pdecR->v.v.Mid32 | pdecR->Hi32;
+ if (ulSgnL != 0)
+ ulSgnL = (pdecL->u.u.sign & DECIMAL_NEG) | 1;
+
+ if (ulSgnR != 0)
+ ulSgnR = (pdecR->u.u.sign & DECIMAL_NEG) | 1;
+
+ // ulSgnL & ulSgnR have values 1, 0, or 0x81 depending on if the left/right
+ // operand is +, 0, or -.
+ //
+ if (ulSgnL == ulSgnR) {
+ if (ulSgnL == 0) // both are zero
+ return VARCMP_EQ; // return equal
+
+ DECIMAL decRes;
+
+ DecAddSub(pdecL, pdecR, &decRes, DECIMAL_NEG);
+ if (DECIMAL_LO64_GET(decRes) == 0 && decRes.Hi32 == 0)
+ return VARCMP_EQ;
+ if (decRes.u.u.sign & DECIMAL_NEG)
+ return VARCMP_LT;
+ return VARCMP_GT;
+ }
+
+ // Signs are different. Used signed byte compares
+ //
+ if ((char)ulSgnL > (char)ulSgnR)
+ return VARCMP_GT;
+ return VARCMP_LT;
+}
+
+STDAPI VarDecRound(LPDECIMAL pdecIn, int cDecimals, LPDECIMAL pdecRes)
+{
+ ULONG rgulNum[3];
+ ULONG ulRem;
+ ULONG ulSticky;
+ ULONG ulPwr;
+ int iScale;
+
+ if (cDecimals < 0)
+ return E_INVALIDARG;
+
+ iScale = pdecIn->u.u.scale - cDecimals;
+ if (iScale > 0)
+ {
+ rgulNum[0] = pdecIn->v.v.Lo32;
+ rgulNum[1] = pdecIn->v.v.Mid32;
+ rgulNum[2] = pdecIn->Hi32;
+ pdecRes->u.u.sign = pdecIn->u.u.sign;
+ ulRem = ulSticky = 0;
+
+ do {
+ ulSticky |= ulRem;
+ if (iScale > POWER10_MAX)
+ ulPwr = ulTenToNine;
+ else
+ ulPwr = rgulPower10[iScale];
+
+ ulRem = Div96By32(rgulNum, ulPwr);
+ iScale -= 9;
+ }while (iScale > 0);
+
+ // Now round. ulRem has last remainder, ulSticky has sticky bits.
+ // To do IEEE rounding, we add LSB of result to sticky bits so
+ // either causes round up if remainder * 2 == last divisor.
+ //
+ ulSticky |= rgulNum[0] & 1;
+ ulRem = (ulRem << 1) + (ulSticky != 0);
+ if (ulPwr < ulRem &&
+ ++rgulNum[0] == 0 &&
+ ++rgulNum[1] == 0
+ )
+ ++rgulNum[2];
+
+ pdecRes->v.v.Lo32 = rgulNum[0];
+ pdecRes->v.v.Mid32 = rgulNum[1];
+ pdecRes->Hi32 = rgulNum[2];
+ pdecRes->u.u.scale = cDecimals;
+ return NOERROR;
+ }
+
+ COPYDEC(*pdecRes, *pdecIn)
+ return NOERROR;
+}
diff --git a/src/palrt/decconv.cpp b/src/palrt/decconv.cpp
new file mode 100644
index 0000000000..259f72e9c8
--- /dev/null
+++ b/src/palrt/decconv.cpp
@@ -0,0 +1,603 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: decconv.cpp
+//
+// ===========================================================================
+/***
+*
+*Purpose:
+* This module contains the low level conversion for Decimal data type.
+*
+*Implementation Notes:
+*
+*****************************************************************************/
+
+#include "common.h"
+#include "convert.h"
+
+#include <oleauto.h>
+#include <math.h>
+#include <limits.h>
+
+#define VALIDATEDECIMAL(dec) \
+ if (DECIMAL_SCALE(dec) > DECMAX || (DECIMAL_SIGN(dec) & ~DECIMAL_NEG) != 0) \
+ return E_INVALIDARG;
+
+#define RESULT(X) ((HRESULT)(X))
+
+//***********************************************************************
+//
+// Data tables
+//
+
+const double dblPower10[] = {
+ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
+ 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
+ 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49,
+ 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59,
+ 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69,
+ 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
+ 1e80 };
+
+double fnDblPower10(int ix)
+{
+ const int maxIx = (sizeof(dblPower10)/sizeof(dblPower10[0]));
+ _ASSERTE(ix >= 0);
+ if (ix < maxIx)
+ return dblPower10[ix];
+ return pow(10.0, ix);
+} // double fnDblPower10()
+
+#define DBLBIAS 1022
+#define SNGBIAS 126
+#define DECMAX 28
+
+const SPLIT64 sdlTenToEighteen = { UI64(1000000000000000000) };
+const DBLSTRUCT ds2to64 = DEFDS(0, 0, DBLBIAS + 65, 0);
+
+//***********************************************************************
+//
+// Data tables
+//
+
+const SPLIT64 sdlPower10[] = { {UI64(10000000000)}, // 1E10
+ {UI64(100000000000)}, // 1E11
+ {UI64(1000000000000)}, // 1E12
+ {UI64(10000000000000)}, // 1E13
+ {UI64(100000000000000)} }; // 1E14
+
+const unsigned __int64 ulPower10[] = {1,
+ UI64(10),
+ UI64(100),
+ UI64(1000),
+ UI64(10000),
+ UI64(100000),
+ UI64(1000000),
+ UI64(10000000),
+ UI64(100000000),
+ UI64(1000000000),
+ UI64(10000000000),
+ UI64(100000000000),
+ UI64(1000000000000),
+ UI64(10000000000000),
+ UI64(100000000000000),
+ UI64(1000000000000000),
+ UI64(10000000000000000),
+ UI64(100000000000000000),
+ UI64(1000000000000000000),
+ UI64(10000000000000000000)};
+
+DWORDLONG UInt64x64To128(SPLIT64 sdlOp1, SPLIT64 sdlOp2, DWORDLONG *pdlHi)
+{
+ SPLIT64 sdlTmp1;
+ SPLIT64 sdlTmp2;
+ SPLIT64 sdlTmp3;
+
+ sdlTmp1.int64 = UInt32x32To64(sdlOp1.u.Lo, sdlOp2.u.Lo); // lo partial prod
+ sdlTmp2.int64 = UInt32x32To64(sdlOp1.u.Lo, sdlOp2.u.Hi); // mid 1 partial prod
+ sdlTmp1.u.Hi += sdlTmp2.u.Lo;
+ if (sdlTmp1.u.Hi < sdlTmp2.u.Lo) // test for carry
+ sdlTmp2.u.Hi++;
+ sdlTmp3.int64 = UInt32x32To64(sdlOp1.u.Hi, sdlOp2.u.Hi) + (DWORDLONG)sdlTmp2.u.Hi;
+ sdlTmp2.int64 = UInt32x32To64(sdlOp1.u.Hi, sdlOp2.u.Lo);
+ sdlTmp1.u.Hi += sdlTmp2.u.Lo;
+ if (sdlTmp1.u.Hi < sdlTmp2.u.Lo) // test for carry
+ sdlTmp2.u.Hi++;
+ sdlTmp3.int64 += (DWORDLONG)sdlTmp2.u.Hi;
+
+ *pdlHi = sdlTmp3.int64;
+ return sdlTmp1.int64;
+}
+
+
+//***********************************************************************
+//
+// Conversion to/from Decimal data type
+//
+
+
+STDAPI
+VarDecFromR4(float fltIn, DECIMAL FAR* pdecOut)
+{
+ int iExp; // number of bits to left of binary point
+ int iPower;
+ ULONG ulMant;
+ double dbl;
+ SPLIT64 sdlLo;
+ SPLIT64 sdlHi;
+ int lmax, cur; // temps used during scale reduction
+
+ // The most we can scale by is 10^28, which is just slightly more
+ // than 2^93. So a float with an exponent of -94 could just
+ // barely reach 0.5, but smaller exponents will always round to zero.
+ //
+ if ( (iExp = ((SNGSTRUCT *)&fltIn)->exp - SNGBIAS) < -94 )
+ {
+ DECIMAL_SETZERO(*pdecOut);
+ return NOERROR;
+ }
+
+ if (iExp > 96)
+ return RESULT(DISP_E_OVERFLOW);
+
+ // Round the input to a 7-digit integer. The R4 format has
+ // only 7 digits of precision, and we want to keep garbage digits
+ // out of the Decimal were making.
+ //
+ // Calculate max power of 10 input value could have by multiplying
+ // the exponent by log10(2). Using scaled integer multiplcation,
+ // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
+ //
+ dbl = fabs(fltIn);
+ iPower = 6 - ((iExp * 19728) >> 16);
+
+ if (iPower >= 0) {
+ // We have less than 7 digits, scale input up.
+ //
+ if (iPower > DECMAX)
+ iPower = DECMAX;
+
+ dbl = dbl * dblPower10[iPower];
+ }
+ else {
+ if (iPower != -1 || dbl >= 1E7)
+ dbl = dbl / fnDblPower10(-iPower);
+ else
+ iPower = 0; // didn't scale it
+ }
+
+ _ASSERTE(dbl < 1E7);
+ if (dbl < 1E6 && iPower < DECMAX)
+ {
+ dbl *= 10;
+ iPower++;
+ _ASSERTE(dbl >= 1E6);
+ }
+
+ // Round to integer
+ //
+ ulMant = (LONG)dbl;
+ dbl -= (double)ulMant; // difference between input & integer
+ if ( dbl > 0.5 || dbl == 0.5 && (ulMant & 1) )
+ ulMant++;
+
+ if (ulMant == 0)
+ {
+ DECIMAL_SETZERO(*pdecOut);
+ return NOERROR;
+ }
+
+ if (iPower < 0) {
+ // Add -iPower factors of 10, -iPower <= (29 - 7) = 22.
+ //
+ iPower = -iPower;
+ if (iPower < 10) {
+ sdlLo.int64 = UInt32x32To64(ulMant, (ULONG)ulPower10[iPower]);
+
+ DECIMAL_LO32(*pdecOut) = sdlLo.u.Lo;
+ DECIMAL_MID32(*pdecOut) = sdlLo.u.Hi;
+ DECIMAL_HI32(*pdecOut) = 0;
+ }
+ else {
+ // Have a big power of 10.
+ //
+ if (iPower > 18) {
+ sdlLo.int64 = UInt32x32To64(ulMant, (ULONG)ulPower10[iPower - 18]);
+ sdlLo.int64 = UInt64x64To128(sdlLo, sdlTenToEighteen, &sdlHi.int64);
+
+ if (sdlHi.u.Hi != 0)
+ return RESULT(DISP_E_OVERFLOW);
+ }
+ else {
+ sdlLo.int64 = UInt32x32To64(ulMant, (ULONG)ulPower10[iPower - 9]);
+ sdlHi.int64 = UInt32x32To64(ulTenToNine, sdlLo.u.Hi);
+ sdlLo.int64 = UInt32x32To64(ulTenToNine, sdlLo.u.Lo);
+ sdlHi.int64 += sdlLo.u.Hi;
+ sdlLo.u.Hi = sdlHi.u.Lo;
+ sdlHi.u.Lo = sdlHi.u.Hi;
+ }
+ DECIMAL_LO32(*pdecOut) = sdlLo.u.Lo;
+ DECIMAL_MID32(*pdecOut) = sdlLo.u.Hi;
+ DECIMAL_HI32(*pdecOut) = sdlHi.u.Lo;
+ }
+ DECIMAL_SCALE(*pdecOut) = 0;
+ }
+ else {
+ // Factor out powers of 10 to reduce the scale, if possible.
+ // The maximum number we could factor out would be 6. This
+ // comes from the fact we have a 7-digit number, and the
+ // MSD must be non-zero -- but the lower 6 digits could be
+ // zero. Note also the scale factor is never negative, so
+ // we can't scale by any more than the power we used to
+ // get the integer.
+ //
+ // DivMod32by32 returns the quotient in Lo, the remainder in Hi.
+ //
+ lmax = min(iPower, 6);
+
+ // lmax is the largest power of 10 to try, lmax <= 6.
+ // We'll try powers 4, 2, and 1 unless they're too big.
+ //
+ for (cur = 4; cur > 0; cur >>= 1)
+ {
+ if (cur > lmax)
+ continue;
+
+ sdlLo.int64 = DivMod32by32(ulMant, (ULONG)ulPower10[cur]);
+
+ if (sdlLo.u.Hi == 0) {
+ ulMant = sdlLo.u.Lo;
+ iPower -= cur;
+ lmax -= cur;
+ }
+ }
+ DECIMAL_LO32(*pdecOut) = ulMant;
+ DECIMAL_MID32(*pdecOut) = 0;
+ DECIMAL_HI32(*pdecOut) = 0;
+ DECIMAL_SCALE(*pdecOut) = iPower;
+ }
+
+ DECIMAL_SIGN(*pdecOut) = (char)((SNGSTRUCT *)&fltIn)->sign << 7;
+ return NOERROR;
+}
+
+STDAPI
+VarDecFromR8(double dblIn, DECIMAL FAR* pdecOut)
+{
+ int iExp; // number of bits to left of binary point
+ int iPower; // power-of-10 scale factor
+ SPLIT64 sdlMant;
+ SPLIT64 sdlLo;
+ double dbl;
+ int lmax, cur; // temps used during scale reduction
+ ULONG ulPwrCur;
+ ULONG ulQuo;
+
+
+ // The most we can scale by is 10^28, which is just slightly more
+ // than 2^93. So a float with an exponent of -94 could just
+ // barely reach 0.5, but smaller exponents will always round to zero.
+ //
+ if ( (iExp = ((DBLSTRUCT *)&dblIn)->u.exp - DBLBIAS) < -94 )
+ {
+ DECIMAL_SETZERO(*pdecOut);
+ return NOERROR;
+ }
+
+ if (iExp > 96)
+ return RESULT(DISP_E_OVERFLOW);
+
+ // Round the input to a 15-digit integer. The R8 format has
+ // only 15 digits of precision, and we want to keep garbage digits
+ // out of the Decimal were making.
+ //
+ // Calculate max power of 10 input value could have by multiplying
+ // the exponent by log10(2). Using scaled integer multiplcation,
+ // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
+ //
+ dbl = fabs(dblIn);
+ iPower = 14 - ((iExp * 19728) >> 16);
+
+ if (iPower >= 0) {
+ // We have less than 15 digits, scale input up.
+ //
+ if (iPower > DECMAX)
+ iPower = DECMAX;
+
+ dbl = dbl * dblPower10[iPower];
+ }
+ else {
+ if (iPower != -1 || dbl >= 1E15)
+ dbl = dbl / fnDblPower10(-iPower);
+ else
+ iPower = 0; // didn't scale it
+ }
+
+ _ASSERTE(dbl < 1E15);
+ if (dbl < 1E14 && iPower < DECMAX)
+ {
+ dbl *= 10;
+ iPower++;
+ _ASSERTE(dbl >= 1E14);
+ }
+
+ // Round to int64
+ //
+ sdlMant.int64 = (LONGLONG)dbl;
+ dbl -= (double)(LONGLONG)sdlMant.int64; // dif between input & integer
+ if ( dbl > 0.5 || dbl == 0.5 && (sdlMant.u.Lo & 1) )
+ sdlMant.int64++;
+
+ if (sdlMant.int64 == 0)
+ {
+ DECIMAL_SETZERO(*pdecOut);
+ return NOERROR;
+ }
+
+ if (iPower < 0) {
+ // Add -iPower factors of 10, -iPower <= (29 - 15) = 14.
+ //
+ iPower = -iPower;
+ if (iPower < 10) {
+ sdlLo.int64 = UInt32x32To64(sdlMant.u.Lo, (ULONG)ulPower10[iPower]);
+ sdlMant.int64 = UInt32x32To64(sdlMant.u.Hi, (ULONG)ulPower10[iPower]);
+ sdlMant.int64 += sdlLo.u.Hi;
+ sdlLo.u.Hi = sdlMant.u.Lo;
+ sdlMant.u.Lo = sdlMant.u.Hi;
+ }
+ else {
+ // Have a big power of 10.
+ //
+ _ASSERTE(iPower <= 14);
+ sdlLo.int64 = UInt64x64To128(sdlMant, sdlPower10[iPower-10], &sdlMant.int64);
+
+ if (sdlMant.u.Hi != 0)
+ return RESULT(DISP_E_OVERFLOW);
+ }
+ DECIMAL_LO32(*pdecOut) = sdlLo.u.Lo;
+ DECIMAL_MID32(*pdecOut) = sdlLo.u.Hi;
+ DECIMAL_HI32(*pdecOut) = sdlMant.u.Lo;
+ DECIMAL_SCALE(*pdecOut) = 0;
+ }
+ else {
+ // Factor out powers of 10 to reduce the scale, if possible.
+ // The maximum number we could factor out would be 14. This
+ // comes from the fact we have a 15-digit number, and the
+ // MSD must be non-zero -- but the lower 14 digits could be
+ // zero. Note also the scale factor is never negative, so
+ // we can't scale by any more than the power we used to
+ // get the integer.
+ //
+ // DivMod64by32 returns the quotient in Lo, the remainder in Hi.
+ //
+ lmax = min(iPower, 14);
+
+ // lmax is the largest power of 10 to try, lmax <= 14.
+ // We'll try powers 8, 4, 2, and 1 unless they're too big.
+ //
+ for (cur = 8; cur > 0; cur >>= 1)
+ {
+ if (cur > lmax)
+ continue;
+
+ ulPwrCur = (ULONG)ulPower10[cur];
+
+ if (sdlMant.u.Hi >= ulPwrCur) {
+ // Overflow if we try to divide in one step.
+ //
+ sdlLo.int64 = DivMod64by32(sdlMant.u.Hi, ulPwrCur);
+ ulQuo = sdlLo.u.Lo;
+ sdlLo.u.Lo = sdlMant.u.Lo;
+ sdlLo.int64 = DivMod64by32(sdlLo.int64, ulPwrCur);
+ }
+ else {
+ ulQuo = 0;
+ sdlLo.int64 = DivMod64by32(sdlMant.int64, ulPwrCur);
+ }
+
+ if (sdlLo.u.Hi == 0) {
+ sdlMant.u.Hi = ulQuo;
+ sdlMant.u.Lo = sdlLo.u.Lo;
+ iPower -= cur;
+ lmax -= cur;
+ }
+ }
+
+ DECIMAL_HI32(*pdecOut) = 0;
+ DECIMAL_SCALE(*pdecOut) = iPower;
+ DECIMAL_LO32(*pdecOut) = sdlMant.u.Lo;
+ DECIMAL_MID32(*pdecOut) = sdlMant.u.Hi;
+ }
+
+ DECIMAL_SIGN(*pdecOut) = (char)((DBLSTRUCT *)&dblIn)->u.sign << 7;
+ return NOERROR;
+}
+
+STDAPI
+VarDecFromCy(CY cyIn, DECIMAL FAR* pdecOut)
+{
+ DECIMAL_SIGN(*pdecOut) = (UCHAR)((cyIn.u.Hi >> 24) & DECIMAL_NEG);
+ if (DECIMAL_SIGN(*pdecOut))
+ cyIn.int64 = -cyIn.int64;
+
+ DECIMAL_LO32(*pdecOut) = cyIn.u.Lo;
+ DECIMAL_MID32(*pdecOut) = cyIn.u.Hi;
+ DECIMAL_SCALE(*pdecOut) = 4;
+ DECIMAL_HI32(*pdecOut) = 0;
+ return NOERROR;
+}
+
+STDAPI VarR4FromDec(DECIMAL FAR* pdecIn, float FAR* pfltOut)
+{
+ double dbl;
+
+ VALIDATEDECIMAL(*pdecIn); // E_INVALIDARG check
+
+ // Can't overflow; no errors possible.
+ //
+ VarR8FromDec(pdecIn, &dbl);
+ *pfltOut = (float)dbl;
+ return NOERROR;
+}
+
+STDAPI VarR8FromDec(DECIMAL FAR* pdecIn, double FAR* pdblOut)
+{
+ SPLIT64 sdlTmp;
+ double dbl;
+
+ VALIDATEDECIMAL(*pdecIn); // E_INVALIDARG check
+
+ sdlTmp.u.Lo = DECIMAL_LO32(*pdecIn);
+ sdlTmp.u.Hi = DECIMAL_MID32(*pdecIn);
+
+ if ( (LONG)DECIMAL_MID32(*pdecIn) < 0 )
+ dbl = (ds2to64.dbl + (double)(LONGLONG)sdlTmp.int64 +
+ (double)DECIMAL_HI32(*pdecIn) * ds2to64.dbl) / fnDblPower10(DECIMAL_SCALE(*pdecIn)) ;
+ else
+ dbl = ((double)(LONGLONG)sdlTmp.int64 +
+ (double)DECIMAL_HI32(*pdecIn) * ds2to64.dbl) / fnDblPower10(DECIMAL_SCALE(*pdecIn));
+
+ if (DECIMAL_SIGN(*pdecIn))
+ dbl = -dbl;
+
+ *pdblOut = dbl;
+ return NOERROR;
+}
+
+STDAPI VarCyFromDec(DECIMAL FAR* pdecIn, CY FAR* pcyOut)
+{
+ SPLIT64 sdlTmp;
+ SPLIT64 sdlTmp1;
+ int scale;
+ ULONG ulPwr;
+ ULONG ul;
+
+ VALIDATEDECIMAL(*pdecIn); // E_INVALIDARG check
+
+ scale = DECIMAL_SCALE(*pdecIn) - 4; // the power of 10 to divide by
+
+ if (scale == 0) {
+ // No scaling needed -- the Decimal has 4 decimal places,
+ // just what Currency needs.
+ //
+ if ( DECIMAL_HI32(*pdecIn) != 0 ||
+ (DECIMAL_MID32(*pdecIn) >= 0x80000000 &&
+ (DECIMAL_MID32(*pdecIn) != 0x80000000 || DECIMAL_LO32(*pdecIn) != 0 || !DECIMAL_SIGN(*pdecIn))) )
+ return RESULT(DISP_E_OVERFLOW);
+
+ sdlTmp.u.Lo = DECIMAL_LO32(*pdecIn);
+ sdlTmp.u.Hi = DECIMAL_MID32(*pdecIn);
+
+ if (DECIMAL_SIGN(*pdecIn))
+ pcyOut->int64 = -(LONGLONG)sdlTmp.int64;
+ else
+ pcyOut->int64 = sdlTmp.int64;
+ return NOERROR;
+ }
+
+ // Need to scale to get 4 decimal places. -4 <= scale <= 24.
+ //
+ if (scale < 0) {
+ sdlTmp1.int64 = UInt32x32To64((ULONG)ulPower10[-scale], DECIMAL_MID32(*pdecIn));
+ sdlTmp.int64 = UInt32x32To64((ULONG)ulPower10[-scale], DECIMAL_LO32(*pdecIn));
+ sdlTmp.u.Hi += sdlTmp1.u.Lo;
+ if (DECIMAL_HI32(*pdecIn) != 0 || sdlTmp1.u.Hi != 0 || sdlTmp1.u.Lo > sdlTmp.u.Hi)
+ return RESULT(DISP_E_OVERFLOW);
+ }
+ else if (scale < 10) {
+ // DivMod64by32 returns the quotient in Lo, the remainder in Hi.
+ //
+ ulPwr = (ULONG)ulPower10[scale];
+ if (DECIMAL_HI32(*pdecIn) >= ulPwr)
+ return RESULT(DISP_E_OVERFLOW);
+ sdlTmp1.u.Lo = DECIMAL_MID32(*pdecIn);
+ sdlTmp1.u.Hi = DECIMAL_HI32(*pdecIn);
+ sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, ulPwr);
+ sdlTmp.u.Hi = sdlTmp1.u.Lo; // quotient to high half of result
+ sdlTmp1.u.Lo = DECIMAL_LO32(*pdecIn); // extended remainder
+ sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, ulPwr);
+ sdlTmp.u.Lo = sdlTmp1.u.Lo; // quotient to low half of result
+
+ // Round result based on remainder in sdlTmp1.Hi.
+ //
+ ulPwr >>= 1; // compare to power/2 (power always even)
+ if (sdlTmp1.u.Hi > ulPwr || sdlTmp1.u.Hi == ulPwr && (sdlTmp.u.Lo & 1))
+ sdlTmp.int64++;
+ }
+ else {
+ // We have a power of 10 in the range 10 - 24. These powers do
+ // not fit in 32 bits. We'll handle this by scaling 2 or 3 times,
+ // first by 10^10, then by the remaining amount (or 10^9, then
+ // the last bit).
+ //
+ // To scale by 10^10, we'll actually divide by 10^10/4, which fits
+ // in 32 bits. The second scaling is multiplied by four
+ // to account for it, just barely assured of fitting in 32 bits
+ // (4E9 < 2^32). Note that the upper third of the quotient is
+ // either zero or one, so we skip the divide step to calculate it.
+ // (Max 4E9 divided by 2.5E9.)
+ //
+ // DivMod64by32 returns the quotient in Lo, the remainder in Hi.
+ //
+ if (DECIMAL_HI32(*pdecIn) >= ulTenToTenDiv4) {
+ sdlTmp.u.Hi = 1; // upper 1st quotient
+ sdlTmp1.u.Hi = DECIMAL_HI32(*pdecIn) - ulTenToTenDiv4; // remainder
+ }
+ else {
+ sdlTmp.u.Hi = 0; // upper 1st quotient
+ sdlTmp1.u.Hi = DECIMAL_HI32(*pdecIn); // remainder
+ }
+ sdlTmp1.u.Lo = DECIMAL_MID32(*pdecIn); // extended remainder
+ sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, ulTenToTenDiv4);
+ sdlTmp.u.Lo = sdlTmp1.u.Lo; // middle 1st quotient
+
+ sdlTmp1.u.Lo = DECIMAL_LO32(*pdecIn); // extended remainder
+ sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, ulTenToTenDiv4);
+
+ ulPwr = (ULONG)(ulPower10[min(scale-10, 9)] << 2);
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr);
+ ul = sdlTmp.u.Lo; // upper 2nd quotient
+
+ sdlTmp.u.Lo = sdlTmp1.u.Lo; // extended remainder
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr);
+ sdlTmp1.u.Lo = sdlTmp.u.Hi; // save final remainder
+ sdlTmp.u.Hi = ul; // position high result
+
+ if (scale >= 20) {
+ ulPwr = (ULONG)(ulPower10[scale-19]);
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr);
+ sdlTmp1.u.Hi |= sdlTmp1.u.Lo; // combine sticky bits
+ sdlTmp1.u.Lo = sdlTmp.u.Hi; // final remainder
+ sdlTmp.u.Hi = 0; // guaranteed result fits in 32 bits
+ }
+
+ // Round result based on remainder in sdlTmp1.Lo. sdlTmp1.Hi is
+ // the remainder from the first division(s), representing sticky bits.
+ // Current result is in sdlTmp.
+ //
+ ulPwr >>= 1; // compare to power/2 (power always even)
+ if (sdlTmp1.u.Lo > ulPwr || sdlTmp1.u.Lo == ulPwr &&
+ ((sdlTmp.u.Lo & 1) || sdlTmp1.u.Hi != 0))
+ sdlTmp.int64++;
+ }
+
+ if (sdlTmp.u.Hi >= 0x80000000 &&
+ (sdlTmp.int64 != UI64(0x8000000000000000) || !DECIMAL_SIGN(*pdecIn)))
+ return RESULT(DISP_E_OVERFLOW);
+
+ if (DECIMAL_SIGN(*pdecIn))
+ sdlTmp.int64 = -(LONGLONG)sdlTmp.int64;
+
+ pcyOut->int64 = sdlTmp.int64;
+ return NOERROR;
+}
+
+
diff --git a/src/palrt/guid.cpp b/src/palrt/guid.cpp
new file mode 100644
index 0000000000..9f7a02de90
--- /dev/null
+++ b/src/palrt/guid.cpp
@@ -0,0 +1,27 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: guid.cpp
+//
+// PALRT guids
+// ===========================================================================
+
+#define INITGUID
+#include "guiddef.h"
+
+// These are GUIDs and IIDs that would normally be provided by the system via uuid.lib,
+// and that the PALRT exposes through headers.
+
+DEFINE_GUID(GUID_NULL, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+DEFINE_GUID(IID_IClassFactory, 0x00000001, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+
+
+// objidl.idl
+DEFINE_GUID(IID_ISequentialStream, 0x0c733a30, 0x2a1c, 0x11ce, 0xad, 0xe5, 0x00, 0xaa, 0x00, 0x44, 0x77, 0x3d);
+DEFINE_GUID(IID_IStream, 0x0000000c, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
diff --git a/src/palrt/guiddef.h b/src/palrt/guiddef.h
new file mode 100644
index 0000000000..a80f0672a5
--- /dev/null
+++ b/src/palrt/guiddef.h
@@ -0,0 +1,27 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: guiddef.h
+//
+// ===========================================================================
+// simplified guiddef.h for PAL
+
+#include "common.h"
+
+#ifdef DEFINE_GUID
+#undef DEFINE_GUID
+#endif
+
+#ifdef INITGUID
+#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ EXTERN_C const GUID DECLSPEC_SELECTANY name \
+ = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
+#else
+#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ EXTERN_C const GUID FAR name
+#endif // INITGUID
diff --git a/src/palrt/memorystream.cpp b/src/palrt/memorystream.cpp
new file mode 100644
index 0000000000..8c9011530c
--- /dev/null
+++ b/src/palrt/memorystream.cpp
@@ -0,0 +1,318 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: memorystream.cpp
+//
+// ===========================================================================
+/*++
+
+Abstract:
+
+ in memory stream
+
+
+
+
+Revision History:
+
+--*/
+
+#include "common.h"
+
+#include "objidl.h"
+
+class MemoryStream : public IStream
+{
+ LONG m_cRef; // QI refcount
+ ULONG m_nPos; // the current position in the stream
+ ULONG m_nSize; // the current size of the stream
+ ULONG m_nData; // the size of the allocated data storage, can be < m_nSize
+ BYTE* m_pData; // the data storage
+
+private:
+ HRESULT Ensure(ULONG nNewData)
+ {
+ if (nNewData > m_nData)
+ {
+ // apply some heurestic for growing
+ ULONG n = m_nData;
+
+ // grow 2x for smaller sizes, 1.25x for bigger sizes
+ n = min(2 * n, n + n / 4 + 0x100000);
+
+ // don't allocate tiny chunks
+ n = max(n, 0x100);
+
+ // compare with the hard limit
+ nNewData = max(n, nNewData);
+ }
+ else
+ if (nNewData > m_nData / 4)
+ {
+ // shrinking but it is not worth it
+ return S_OK;
+ }
+
+ BYTE * pNewData = (BYTE*)realloc(m_pData, nNewData);
+ if (pNewData == NULL && nNewData != 0)
+ return E_OUTOFMEMORY;
+
+ m_nData = nNewData;
+ m_pData = pNewData;
+ return S_OK;
+ }
+
+public:
+ MemoryStream()
+ {
+ m_cRef = 1;
+ m_nPos = 0;
+ m_nSize = 0;
+ m_nData = 0;
+ m_pData = NULL;
+ }
+
+#ifdef __GNUC__
+ virtual
+#endif
+ ~MemoryStream()
+ {
+ free(m_pData);
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(
+ REFIID riid,
+ void **ppvObject)
+ {
+ if (riid == IID_IStream ||
+ riid == IID_ISequentialStream ||
+ riid == IID_IUnknown)
+ {
+ InterlockedIncrement(&m_cRef);
+ *ppvObject = this;
+ return S_OK;
+ }
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return InterlockedIncrement(&m_cRef);
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ LONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ return cRef;
+ }
+
+ HRESULT STDMETHODCALLTYPE Read(
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead)
+ {
+ ULONG nData;
+ ULONG nNewPos = m_nPos + cb;
+
+ // check for overflow
+ if (nNewPos < cb)
+ return STG_E_INVALIDFUNCTION;
+
+ // compare with the actual size
+ nNewPos = min(nNewPos, m_nSize);
+
+ // compare with the data available
+ nData = min(nNewPos, m_nData);
+
+ // copy the data over
+ if (nData > m_nPos)
+ memcpy(pv, m_pData + m_nPos, nData - m_nPos);
+
+ // fill the rest with zeros
+ if (nNewPos > nData)
+ memset((BYTE*)pv + (nData - m_nPos), 0, nNewPos - nData);
+
+ cb = nNewPos - m_nPos;
+ m_nPos = nNewPos;
+
+ if (pcbRead)
+ *pcbRead = cb;
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Write(
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten)
+ {
+ ULONG nNewPos = m_nPos + cb;
+
+ // check for overflow
+ if (nNewPos < cb)
+ return STG_E_INVALIDFUNCTION;
+
+ // ensure the space
+ if (nNewPos > m_nData)
+ {
+ HRESULT hr = Ensure(nNewPos);
+ if (FAILED(hr)) return hr;
+ }
+
+ // copy the data over
+ memcpy(m_pData + m_nPos, pv, cb);
+
+ m_nPos = nNewPos;
+ if (m_nPos > m_nSize)
+ m_nSize = m_nPos;
+
+ if (pcbWritten)
+ *pcbWritten = cb;
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Seek(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER *plibNewPosition)
+ {
+ ULONG lStartPos;
+ LONGLONG lNewPos;
+
+ switch (dwOrigin)
+ {
+ case STREAM_SEEK_SET:
+ lStartPos = 0;
+ break;
+ case STREAM_SEEK_CUR:
+ lStartPos = m_nPos;
+ break;
+ case STREAM_SEEK_END:
+ lStartPos = m_nSize;
+ break;
+ default:
+ return STG_E_INVALIDFUNCTION;
+ }
+
+ lNewPos = lStartPos + dlibMove.QuadPart;
+
+ // it is an error to seek before the beginning of the stream
+ if (lNewPos < 0)
+ return STG_E_INVALIDFUNCTION;
+
+ // It is not, however, an error to seek past the end of the stream
+ if (lNewPos > m_nSize)
+ {
+ ULARGE_INTEGER NewSize;
+ NewSize.QuadPart = lNewPos;
+
+ HRESULT hr = SetSize(NewSize);
+ if (FAILED(hr)) return hr;
+ }
+
+ m_nPos = (ULONG)lNewPos;
+
+ if (plibNewPosition != NULL)
+ plibNewPosition->QuadPart = m_nPos;
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetSize(
+ ULARGE_INTEGER libNewSize)
+ {
+ if (libNewSize.u.HighPart != 0)
+ return STG_E_INVALIDFUNCTION;
+
+ m_nSize = libNewSize.u.LowPart;
+
+ // free the space if we are shrinking
+ if (m_nSize < m_nData)
+ Ensure(m_nSize);
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE CopyTo(
+ IStream *pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead,
+ ULARGE_INTEGER *pcbWritten)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Commit(
+ DWORD grfCommitFlags)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Revert()
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE LockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE UnlockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag)
+ {
+ memset(pstatstg, 0, sizeof(STATSTG));
+ pstatstg->cbSize.QuadPart = m_nSize;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Clone(
+ IStream **ppstm)
+ {
+ _ASSERTE(false);
+ return E_NOTIMPL;
+ }
+};
+
+STDAPI CreateStreamOnHGlobal(PVOID hGlobal, BOOL fDeleteOnRelease, IStream** ppstm)
+{
+ MemoryStream* pStream;
+
+ if (hGlobal != NULL) return E_NOTIMPL;
+ _ASSERTE(fDeleteOnRelease == TRUE);
+
+ pStream = new MemoryStream;
+ if (pStream == NULL) return E_OUTOFMEMORY;
+
+ *ppstm = pStream;
+ return S_OK;
+}
diff --git a/src/palrt/path.cpp b/src/palrt/path.cpp
new file mode 100644
index 0000000000..6b6f81af73
--- /dev/null
+++ b/src/palrt/path.cpp
@@ -0,0 +1,643 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: path.cpp
+//
+// Path APIs ported from shlwapi (especially for Fusion)
+// ===========================================================================
+
+#include "common.h"
+
+
+
+#define CH_SLASH W('/')
+#define CH_WHACK W('\\')
+
+//
+// Inline function to check for a double-backslash at the
+// beginning of a string
+//
+
+static __inline BOOL DBL_BSLASH(LPCWSTR psz)
+{
+ return (psz[0] == W('\\') && psz[1] == W('\\'));
+}
+
+//
+// Inline function to check for a path separator character.
+//
+
+static __inline BOOL IsPathSeparator(WCHAR ch)
+{
+ return (ch == CH_SLASH || ch == CH_WHACK);
+}
+
+__inline BOOL ChrCmpW_inline(WCHAR w1, WCHAR wMatch)
+{
+ return(!(w1 == wMatch));
+}
+
+STDAPI_(LPWSTR) StrRChrW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
+{
+ LPCWSTR lpFound = NULL;
+
+ RIPMSG(lpStart && IS_VALID_STRING_PTRW(lpStart, -1), "StrRChrW: caller passed bad lpStart");
+ RIPMSG(!lpEnd || lpEnd <= lpStart + lstrlenW(lpStart), "StrRChrW: caller passed bad lpEnd");
+ // don't need to check for NULL lpStart
+
+ if (!lpEnd)
+ lpEnd = lpStart + lstrlenW(lpStart);
+
+ for ( ; lpStart < lpEnd; lpStart++)
+ {
+ if (!ChrCmpW_inline(*lpStart, wMatch))
+ lpFound = lpStart;
+ }
+ return ((LPWSTR)lpFound);
+}
+
+
+// check if a path is a root
+//
+// returns:
+// TRUE
+// "\" "X:\" "\\" "\\foo" "\\foo\bar"
+//
+// FALSE for others including "\\foo\bar\" (!)
+//
+STDAPI_(BOOL) PathIsRootW(LPCWSTR pPath)
+{
+ RIPMSG(pPath && IS_VALID_STRING_PTR(pPath, -1), "PathIsRoot: caller passed bad pPath");
+
+ if (!pPath || !*pPath)
+ {
+ return FALSE;
+ }
+
+ if (!lstrcmpiW(pPath + 1, W(":\\")))
+ {
+ return TRUE; // "X:\" case
+ }
+
+ if (IsPathSeparator(*pPath) && (*(pPath + 1) == 0))
+ {
+ return TRUE; // "/" or "\" case
+ }
+
+ if (DBL_BSLASH(pPath)) // smells like UNC name
+ {
+ LPCWSTR p;
+ int cBackslashes = 0;
+
+ for (p = pPath + 2; *p; p++)
+ {
+ if (*p == W('\\'))
+ {
+ //
+ // return FALSE for "\\server\share\dir"
+ // so just check if there is more than one slash
+ //
+ // "\\server\" without a share name causes
+ // problems for WNet APIs. we should return
+ // FALSE for this as well
+ //
+ if ((++cBackslashes > 1) || !*(p+1))
+ return FALSE;
+ }
+ }
+ // end of string with only 1 more backslash
+ // must be a bare UNC, which looks like a root dir
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+// rips the last part of the path off including the backslash
+// C:\foo -> C:\
+// C:\foo\bar -> C:\foo
+// C:\foo\ -> C:\foo
+// \\x\y\x -> \\x\y
+// \\x\y -> \\x
+// \\x -> \\ (Just the double slash!)
+// \foo -> \ (Just the slash!)
+//
+// in/out:
+// pFile fully qualified path name
+// returns:
+// TRUE we stripped something
+// FALSE didn't strip anything (root directory case)
+//
+*/
+STDAPI_(BOOL) PathRemoveFileSpecW(LPWSTR pFile)
+{
+ RIPMSG(pFile && IS_VALID_STRING_PTR(pFile, -1), "PathRemoveFileSpec: caller passed bad pFile");
+
+ if (pFile)
+ {
+ LPWSTR pT;
+ LPWSTR pT2 = pFile;
+
+ for (pT = pT2; *pT2; pT2++)
+ {
+ if (IsPathSeparator(*pT2))
+ {
+ pT = pT2; // last "\" found, (we will strip here)
+ }
+ else if (*pT2 == W(':')) // skip ":\" so we don't
+ {
+ if (IsPathSeparator(pT2[1])) // strip the "\" from "C:\"
+ {
+ pT2++;
+ }
+ pT = pT2 + 1;
+ }
+ }
+
+ if (*pT == 0)
+ {
+ // didn't strip anything
+ return FALSE;
+ }
+ else if (((pT == pFile) && IsPathSeparator(*pT)) || // is it the "\foo" case?
+ ((pT == pFile+1) && (*pT == CH_WHACK && *pFile == CH_WHACK))) // or the "\\bar" case?
+ {
+ // Is it just a '\'?
+ if (*(pT+1) != W('\0'))
+ {
+ // Nope.
+ *(pT+1) = W('\0');
+ return TRUE; // stripped something
+ }
+ else
+ {
+ // Yep.
+ return FALSE;
+ }
+ }
+ else
+ {
+ *pT = 0;
+ return TRUE; // stripped something
+ }
+ }
+ return FALSE;
+}
+
+//
+// Return a pointer to the end of the next path component in the string.
+// ie return a pointer to the next backslash or terminating NULL.
+//
+LPCWSTR GetPCEnd(LPCWSTR lpszStart)
+{
+ LPCWSTR lpszEnd;
+ LPCWSTR lpszSlash;
+
+ lpszEnd = StrChr(lpszStart, CH_WHACK);
+ lpszSlash = StrChr(lpszStart, CH_SLASH);
+ if ((lpszSlash && lpszSlash < lpszEnd) ||
+ !lpszEnd)
+ {
+ lpszEnd = lpszSlash;
+ }
+ if (!lpszEnd)
+ {
+ lpszEnd = lpszStart + lstrlenW(lpszStart);
+ }
+
+ return lpszEnd;
+}
+
+//
+// Given a pointer to the end of a path component, return a pointer to
+// its begining.
+// ie return a pointer to the previous backslash (or start of the string).
+//
+LPCWSTR PCStart(LPCWSTR lpszStart, LPCWSTR lpszEnd)
+{
+ LPCWSTR lpszBegin = StrRChrW(lpszStart, lpszEnd, CH_WHACK);
+ LPCWSTR lpszSlash = StrRChrW(lpszStart, lpszEnd, CH_SLASH);
+ if (lpszSlash > lpszBegin)
+ {
+ lpszBegin = lpszSlash;
+ }
+ if (!lpszBegin)
+ {
+ lpszBegin = lpszStart;
+ }
+ return lpszBegin;
+}
+
+//
+// Fix up a few special cases so that things roughly make sense.
+//
+void NearRootFixups(LPWSTR lpszPath, BOOL fUNC)
+{
+ // Check for empty path.
+ if (lpszPath[0] == W('\0'))
+ {
+ // Fix up.
+#ifndef PLATFORM_UNIX
+ lpszPath[0] = CH_WHACK;
+#else
+ lpszPath[0] = CH_SLASH;
+#endif
+ lpszPath[1] = W('\0');
+ }
+ // Check for missing slash.
+ if (lpszPath[1] == W(':') && lpszPath[2] == W('\0'))
+ {
+ // Fix up.
+ lpszPath[2] = W('\\');
+ lpszPath[3] = W('\0');
+ }
+ // Check for UNC root.
+ if (fUNC && lpszPath[0] == W('\\') && lpszPath[1] == W('\0'))
+ {
+ // Fix up.
+ //lpszPath[0] = W('\\'); // already checked in if guard
+ lpszPath[1] = W('\\');
+ lpszPath[2] = W('\0');
+ }
+}
+
+/*----------------------------------------------------------
+Purpose: Canonicalize a path.
+
+Returns:
+Cond: --
+*/
+STDAPI_(BOOL) PathCanonicalizeW(LPWSTR lpszDst, LPCWSTR lpszSrc)
+{
+ LPCWSTR lpchSrc;
+ LPCWSTR lpchPCEnd; // Pointer to end of path component.
+ LPWSTR lpchDst;
+ BOOL fUNC;
+ int cchPC;
+
+ RIPMSG(lpszDst && IS_VALID_WRITE_BUFFER(lpszDst, WCHAR, MAX_PATH), "PathCanonicalize: caller passed bad lpszDst");
+ RIPMSG(lpszSrc && IS_VALID_STRING_PTR(lpszSrc, -1), "PathCanonicalize: caller passed bad lpszSrc");
+ RIPMSG(lpszDst != lpszSrc, "PathCanonicalize: caller passed the same buffer for lpszDst and lpszSrc");
+
+ if (!lpszDst || !lpszSrc)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ *lpszDst = W('\0');
+
+ fUNC = PathIsUNCW(lpszSrc); // Check for UNCness.
+
+ // Init.
+ lpchSrc = lpszSrc;
+ lpchDst = lpszDst;
+
+ while (*lpchSrc)
+ {
+ lpchPCEnd = GetPCEnd(lpchSrc);
+ cchPC = (int) (lpchPCEnd - lpchSrc)+1;
+
+ if (cchPC == 1 && IsPathSeparator(*lpchSrc)) // Check for slashes.
+ {
+ // Just copy them.
+#ifndef PLATFORM_UNIX
+ *lpchDst = CH_WHACK;
+#else
+ *lpchDst = CH_SLASH;
+#endif
+ lpchDst++;
+ lpchSrc++;
+ }
+ else if (cchPC == 2 && *lpchSrc == W('.')) // Check for dots.
+ {
+ // Skip it...
+ // Are we at the end?
+ if (*(lpchSrc+1) == W('\0'))
+ {
+ lpchSrc++;
+
+ // remove the last slash we copied (if we've copied one), but don't make a mal-formed root
+ if ((lpchDst > lpszDst) && !PathIsRootW(lpszDst))
+ lpchDst--;
+ }
+ else
+ {
+ lpchSrc += 2;
+ }
+ }
+ else if (cchPC == 3 && *lpchSrc == W('.') && *(lpchSrc + 1) == W('.')) // Check for dot dot.
+ {
+ // make sure we aren't already at the root
+ if (!PathIsRootW(lpszDst))
+ {
+ // Go up... Remove the previous path component.
+ lpchDst = (LPWSTR)PCStart(lpszDst, lpchDst - 1);
+ }
+ else
+ {
+ // When we can't back up, skip the trailing backslash
+ // so we don't copy one again. (C:\..\FOO would otherwise
+ // turn into C:\\FOO).
+ if (IsPathSeparator(*(lpchSrc + 2)))
+ {
+ lpchSrc++;
+ }
+ }
+
+ // skip ".."
+ lpchSrc += 2;
+ }
+ else // Everything else
+ {
+ // Just copy it.
+ lstrcpynW(lpchDst, lpchSrc, cchPC);
+ lpchDst += cchPC - 1;
+ lpchSrc += cchPC - 1;
+ }
+
+ // Keep everything nice and tidy.
+ *lpchDst = W('\0');
+ }
+
+ // Check for weirdo root directory stuff.
+ NearRootFixups(lpszDst, fUNC);
+
+ return TRUE;
+}
+
+// Modifies:
+// pszRoot
+//
+// Returns:
+// TRUE if a drive root was found
+// FALSE otherwise
+//
+STDAPI_(BOOL) PathStripToRootW(LPWSTR pszRoot)
+{
+ RIPMSG(pszRoot && IS_VALID_STRING_PTR(pszRoot, -1), "PathStripToRoot: caller passed bad pszRoot");
+
+ if (pszRoot)
+ {
+ while (!PathIsRootW(pszRoot))
+ {
+ if (!PathRemoveFileSpecW(pszRoot))
+ {
+ // If we didn't strip anything off,
+ // must be current drive
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+/*----------------------------------------------------------
+Purpose: Concatenate lpszDir and lpszFile into a properly formed
+ path and canonicalize any relative path pieces.
+
+ lpszDest and lpszFile can be the same buffer
+ lpszDest and lpszDir can be the same buffer
+
+Returns: pointer to lpszDest
+*/
+STDAPI_(LPWSTR) PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile)
+{
+#ifdef DEBUG
+ RIPMSG(lpszDest && IS_VALID_WRITE_BUFFER(lpszDest, TCHAR, MAX_PATH), "PathCombine: caller passed bad lpszDest");
+ RIPMSG(!lpszDir || IS_VALID_STRING_PTR(lpszDir, -1), "PathCombine: caller passed bad lpszDir");
+ RIPMSG(!lpszFile || IS_VALID_STRING_PTR(lpszFile, -1), "PathCombine: caller passed bad lpszFile");
+ RIPMSG(lpszDir || lpszFile, "PathCombine: caller neglected to pass lpszDir or lpszFile");
+#endif // DEBUG
+
+
+ if (lpszDest)
+ {
+ TCHAR szTemp[MAX_PATH];
+ LPWSTR pszT;
+
+ *szTemp = W('\0');
+
+ if (lpszDir && *lpszDir)
+ {
+ if (!lpszFile || *lpszFile==W('\0'))
+ {
+ lstrcpynW(szTemp, lpszDir, ARRAYSIZE(szTemp)); // lpszFile is empty
+ }
+ else if (PathIsRelativeW(lpszFile))
+ {
+ lstrcpynW(szTemp, lpszDir, ARRAYSIZE(szTemp));
+ pszT = PathAddBackslashW(szTemp);
+ if (pszT)
+ {
+ int iRemaining = (int)(ARRAYSIZE(szTemp) - (pszT - szTemp));
+
+ if (lstrlenW(lpszFile) < iRemaining)
+ {
+ lstrcpynW(pszT, lpszFile, iRemaining);
+ }
+ else
+ {
+ *szTemp = W('\0');
+ }
+ }
+ else
+ {
+ *szTemp = W('\0');
+ }
+ }
+ else if (IsPathSeparator(*lpszFile) && !PathIsUNCW(lpszFile))
+ {
+ lstrcpynW(szTemp, lpszDir, ARRAYSIZE(szTemp));
+ // FEATURE: Note that we do not check that an actual root is returned;
+ // it is assumed that we are given valid parameters
+ PathStripToRootW(szTemp);
+
+ pszT = PathAddBackslashW(szTemp);
+ if (pszT)
+ {
+ // Skip the backslash when copying
+ // Note: We don't support strings longer than 4GB, but that's
+ // okay because we already barf at MAX_PATH
+ lstrcpynW(pszT, lpszFile+1, (int)(ARRAYSIZE(szTemp) - (pszT - szTemp)));
+ }
+ else
+ {
+ *szTemp = W('\0');
+ }
+ }
+ else
+ {
+ lstrcpynW(szTemp, lpszFile, ARRAYSIZE(szTemp)); // already fully qualified file part
+ }
+ }
+ else if (lpszFile && *lpszFile)
+ {
+ lstrcpynW(szTemp, lpszFile, ARRAYSIZE(szTemp)); // no dir just use file.
+ }
+
+ //
+ // if szTemp has something in it we succeeded. Also if szTemp is empty and
+ // the input strings are empty we succeed and PathCanonicalize() will
+ // return "\"
+ //
+ if (*szTemp || ((lpszDir || lpszFile) && !((lpszDir && *lpszDir) || (lpszFile && *lpszFile))))
+ {
+ PathCanonicalizeW(lpszDest, szTemp); // this deals with .. and . stuff
+ // returns "\" on empty szTemp
+ }
+ else
+ {
+ *lpszDest = W('\0'); // set output buffer to empty string.
+ lpszDest = NULL; // return failure.
+ }
+ }
+
+ return lpszDest;
+}
+
+// add a backslash to a qualified path
+//
+// in:
+// lpszPath path (A:, C:\foo, etc)
+//
+// out:
+// lpszPath A:\, C:\foo\ ;
+//
+// returns:
+// pointer to the NULL that terminates the path
+//
+STDAPI_(LPWSTR) PathAddBackslashW(LPWSTR lpszPath)
+{
+ LPWSTR lpszRet = NULL;
+
+ RIPMSG(lpszPath && IS_VALID_STRING_PTR(lpszPath, -1), "PathAddBackslash: caller passed bad lpszPath");
+
+ if (lpszPath)
+ {
+ int ichPath = lstrlenW(lpszPath);
+ LPWSTR lpszEnd = lpszPath + ichPath;
+
+ if (ichPath)
+ {
+
+ // Get the end of the source directory
+ switch(*(lpszEnd-1))
+ {
+ case CH_SLASH:
+ case CH_WHACK:
+ break;
+
+ default:
+ // try to keep us from tromping over MAX_PATH in size.
+ // if we find these cases, return NULL. Note: We need to
+ // check those places that call us to handle their GP fault
+ // if they try to use the NULL!
+ if (ichPath >= (MAX_PATH - 2)) // -2 because ichPath doesn't include NULL, and we're adding a CH_WHACK.
+ {
+ return(NULL);
+ }
+
+ *lpszEnd++ = CH_WHACK;
+ *lpszEnd = W('\0');
+ }
+ }
+
+ lpszRet = lpszEnd;
+ }
+
+ return lpszRet;
+}
+
+
+
+
+//---------------------------------------------------------------------------
+// Returns TRUE if the given string is a UNC path.
+//
+// TRUE
+// "\\foo\bar"
+// "\\foo" <- careful
+// "\\"
+// FALSE
+// "\foo"
+// "foo"
+// "c:\foo"
+//
+//
+STDAPI_(BOOL) PathIsUNCW(LPCWSTR pszPath)
+{
+ RIPMSG(pszPath && IS_VALID_STRING_PTR(pszPath, -1), "PathIsUNC: caller passed bad pszPath");
+
+ if (pszPath)
+ {
+ return DBL_BSLASH(pszPath);
+ }
+ return FALSE;
+}
+
+
+
+
+
+//---------------------------------------------------------------------------
+// Return TRUE if the path isn't absoulte.
+//
+// TRUE
+// "foo.exe"
+// ".\foo.exe"
+// "..\boo\foo.exe"
+//
+// FALSE
+// "\foo"
+// "c:bar" <- be careful
+// "c:\bar"
+// "\\foo\bar"
+//
+STDAPI_(BOOL) PathIsRelativeW(LPCWSTR lpszPath)
+{
+ RIPMSG(lpszPath && IS_VALID_STRING_PTR(lpszPath, -1), "PathIsRelative: caller passed bad lpszPath");
+
+ if (!lpszPath || *lpszPath == 0)
+ {
+ // The NULL path is assumed relative
+ return TRUE;
+ }
+
+ if (IsPathSeparator(lpszPath[0]))
+ {
+ // Does it begin with a slash ?
+ return FALSE;
+ }
+ else if (lpszPath[1] == W(':'))
+ {
+ // Does it begin with a drive and a colon ?
+ return FALSE;
+ }
+ else
+ {
+ // Probably relative.
+ return TRUE;
+ }
+}
+
+// find the next slash or null terminator
+LPWSTR StrSlash(LPCWSTR psz)
+{
+ for (; *psz && !IsPathSeparator(*psz); psz++);
+
+ // Cast to a non-const string to mimic the behavior
+ // of wcschr/StrChr and strchr.
+ return (LPWSTR) psz;
+}
+
+
+
diff --git a/src/palrt/shlwapip.h b/src/palrt/shlwapip.h
new file mode 100644
index 0000000000..5bfe61a0f9
--- /dev/null
+++ b/src/palrt/shlwapip.h
@@ -0,0 +1,89 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: shlwapi.h
+//
+// Header for ported shlwapi stuff
+// ===========================================================================
+
+#ifndef SHLWAPIP_H_INCLUDED
+#define SHLWAPIP_H_INCLUDED
+
+#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
+#define SIZECHARS(sz) (sizeof(sz)/sizeof(sz[0]))
+
+#define SIZEOF(x) sizeof(x)
+#define PRIVATE
+#define PUBLIC
+#ifndef ASSERT
+#define ASSERT _ASSERTE
+#endif
+#define AssertMsg(f,m) _ASSERTE(f)
+#define RIP(f) _ASSERTE(f)
+#define RIPMSG(f,m) _ASSERTE(f)
+
+#define IsFlagSet(obj, f) (BOOL)(((obj) & (f)) == (f))
+#define IsFlagClear(obj, f) (BOOL)(((obj) & (f)) != (f))
+
+#define InRange(id, idFirst, idLast) ((UINT)((id)-(idFirst)) <= (UINT)((idLast)-(idFirst)))
+
+#define CbFromCch(cch) ((cch)*sizeof(TCHAR))
+
+#define IS_VALID_READ_BUFFER(p, t, n) (p != NULL)
+#define IS_VALID_WRITE_BUFFER(p, t, n) (p != NULL)
+
+#define IS_VALID_READ_PTR(p, t) IS_VALID_READ_BUFFER(p, t, 1)
+#define IS_VALID_WRITE_PTR(p, t) IS_VALID_WRITE_BUFFER(p, t, 1)
+
+#define IS_VALID_STRING_PTR(p, c) (p != NULL)
+#define IS_VALID_STRING_PTRW(p, c) (p != NULL)
+
+#define CharLowerW _wcslwr
+
+inline int StrCmpNCW(LPCWSTR pch1, LPCWSTR pch2, int n)
+{
+ if (n == 0)
+ return 0;
+
+ while (--n && *pch1 && *pch1 == *pch2)
+ {
+ pch1++;
+ pch2++;
+ }
+
+ return *pch1 - *pch2;
+}
+
+typedef struct tagPARSEDURLW {
+ DWORD cbSize;
+ // Pointers into the buffer that was provided to ParseURL
+ LPCWSTR pszProtocol;
+ UINT cchProtocol;
+ LPCWSTR pszSuffix;
+ UINT cchSuffix;
+ UINT nScheme; // One of URL_SCHEME_*
+ } PARSEDURLW, * PPARSEDURLW;
+
+typedef enum {
+ URL_SCHEME_INVALID = -1,
+ URL_SCHEME_UNKNOWN = 0,
+ URL_SCHEME_FTP = 1,
+ URL_SCHEME_HTTP = 2,
+ URL_SCHEME_FILE = 9,
+ URL_SCHEME_HTTPS = 11,
+} URL_SCHEME;
+
+#define URL_ESCAPE_UNSAFE 0x20000000
+#define URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
+#define URL_ESCAPE_SPACES_ONLY 0x04000000
+#define URL_DONT_SIMPLIFY 0x08000000
+#define URL_UNESCAPE_INPLACE 0x00100000
+#define URL_ESCAPE_PERCENT 0x00001000
+#define URL_ESCAPE_SEGMENT_ONLY 0x00002000 // Treat the entire URL param as one URL segment.
+
+#endif // ! SHLWAPIP_H_INCLUDED
diff --git a/src/palrt/shstr.h b/src/palrt/shstr.h
new file mode 100644
index 0000000000..41cc79dffc
--- /dev/null
+++ b/src/palrt/shstr.h
@@ -0,0 +1,139 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: shstr.h
+//
+// ShStr class ported from shlwapi for urlpars.cpp (especially for Fusion)
+// ===========================================================================
+
+#ifndef _SHSTR_H_
+
+// default shstr to something small, so we don't waste too much stack space
+// MAX_PATH is used frequently, so we'd like to a factor of that - so that
+// if we do grow to MAX_PATH size, we don't waste any extra.
+#define DEFAULT_SHSTR_LENGTH (MAX_PATH/4)
+
+
+#ifdef UNICODE
+#define ShStr ShStrW
+#define UrlStr UrlStrW
+#endif //UNICODE
+
+
+class ShStrW
+{
+public:
+
+ //
+ // Constructors
+ //
+ ShStrW();
+
+ //
+ // Destructor
+ //
+ ~ShStrW()
+ {Reset();}
+
+ //
+ // the first are the only ones that count
+ //
+ HRESULT SetStr(LPCSTR pszStr, DWORD cchStr);
+ HRESULT SetStr(LPCSTR pszStr);
+ HRESULT SetStr(LPCWSTR pwszStr, DWORD cchStr);
+
+ // the rest just call into the first three
+ HRESULT SetStr(LPCWSTR pwszStr)
+ {return SetStr(pwszStr, (DWORD) -1);}
+ HRESULT SetStr(ShStrW &shstr)
+ {return SetStr(shstr._pszStr);}
+
+
+ ShStrW& operator=(LPCSTR pszStr)
+ {SetStr(pszStr); return *this;}
+ ShStrW& operator=(LPCWSTR pwszStr)
+ {SetStr(pwszStr); return *this;}
+ ShStrW& operator=(ShStrW &shstr)
+ {SetStr(shstr._pszStr); return *this;}
+
+
+ LPCWSTR GetStr()
+ {return _pszStr;}
+ operator LPCWSTR()
+ {return _pszStr;}
+
+ LPWSTR GetInplaceStr(void)
+ {return _pszStr;}
+
+ // People want to play with the bytes in OUR internal buffer. If they
+ // call us correctly, and assume that the resulting pointer is only valid
+ // as far as they want or as far as the current length, then let them.
+ LPWSTR GetModifyableStr(DWORD cchSizeToModify)
+ {
+ if (cchSizeToModify > _cchSize)
+ if (FAILED(SetSize(cchSizeToModify)))
+ return NULL;
+ return _pszStr;
+ }
+
+ HRESULT Append(LPCWSTR pszStr, DWORD cchStr);
+ HRESULT Append(LPCWSTR pszStr)
+ {return Append(pszStr, (DWORD) -1);}
+ HRESULT Append(WCHAR ch)
+ {return Append(&ch, 1);}
+
+
+ VOID Reset();
+
+#ifdef DEBUG
+ BOOL IsValid();
+#else
+ BOOL IsValid()
+ {return (BOOL) (_pszStr ? TRUE : FALSE);}
+#endif //DEBUG
+
+ DWORD GetSize()
+ {ASSERT(!(_cchSize % DEFAULT_SHSTR_LENGTH)); return (_pszStr ? _cchSize : 0);}
+
+ HRESULT SetSize(DWORD cchSize);
+ DWORD GetLen()
+ {return lstrlenW(_pszStr);}
+
+
+
+protected:
+// friend UrlStr;
+/*
+ TCHAR GetAt(DWORD cch)
+ {return cch < _cchSize ? _pszStr[cch] : TEXT('\0');}
+ TCHAR SetAt(TCHAR ch, DWORD cch)
+ {return cch < _cchSize ? _pszStr[cch] = ch : TEXT('\0');}
+*/
+private:
+
+ HRESULT _SetStr(LPCSTR psz);
+ HRESULT _SetStr(LPCSTR psz, DWORD cb);
+ HRESULT _SetStr(LPCWSTR pwszStr, DWORD cchStr);
+
+ WCHAR _szDefaultBuffer[DEFAULT_SHSTR_LENGTH];
+ LPWSTR _pszStr;
+ DWORD _cchSize;
+
+
+}; //ShStrW
+
+#ifdef UNICODE
+typedef ShStrW SHSTR;
+typedef ShStrW *PSHSTR;
+#endif //UNICODE
+
+typedef ShStrW SHSTRW;
+typedef ShStrW *PSHSTRW;
+
+
+#endif // _SHSTR_H_
diff --git a/src/palrt/urlpars.cpp b/src/palrt/urlpars.cpp
new file mode 100644
index 0000000000..5f71a88cd8
--- /dev/null
+++ b/src/palrt/urlpars.cpp
@@ -0,0 +1,322 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ===========================================================================
+// File: urlpars.cpp
+//
+// URL APIs ported from shlwapi (especially for Fusion)
+// ===========================================================================
+
+#include "common.h"
+
+#define SLASH W('/')
+#define WHACK W('\\')
+
+#define UPF_SCHEME_OPAQUE 0x00000001 // should not be treated as hierarchical
+#define UPF_SCHEME_INTERNET 0x00000002
+#define UPF_SCHEME_NOHISTORY 0x00000004
+#define UPF_SCHEME_CONVERT 0x00000008 // treat slashes and whacks as equiv
+#define UPF_SCHEME_DONTCORRECT 0x00000010 // Don't try to autocorrect to this scheme
+
+PRIVATE CONST WORD isSafe[96] =
+
+/* Bit 0 alphadigit -- 'a' to 'z', '0' to '9', 'A' to 'Z'
+** Bit 1 Hex -- '0' to '9', 'a' to 'f', 'A' to 'F'
+** Bit 2 valid scheme -- alphadigit | "-" | "." | "+"
+** Bit 3 mark -- "%" | "$"| "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" | ","
+*/
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ {0, 8, 0, 0, 8, 8, 0, 8, 8, 8, 8, 12, 8,12,12, 0, /* 2x !"#$%&'()*+,-./ */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 8, 0, 8, 0, 0, /* 3x 0123456789:;<=>? */
+ 8, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x @ABCDEFGHIJKLMNO */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 8, /* 5X PQRSTUVWXYZ[\]^_ */
+ 0, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x `abcdefghijklmno */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 8, 0}; /* 7X pqrstuvwxyz{|}~ DEL */
+
+PRIVATE inline BOOL IsSafe(WCHAR ch, WORD mask)
+{
+ if(((ch > 31 ) && (ch < 128) && (isSafe[ch - 32] & mask)))
+ return TRUE;
+
+ return FALSE;
+}
+
+PRIVATE inline BOOL IsAsciiCharW(WCHAR ch)
+{
+ return (!(ch >> 8) && ((CHAR) ch));
+}
+
+BOOL IsValidSchemeCharW(WCHAR ch)
+{
+ if(IsAsciiCharW(ch))
+ return IsSafe( (CHAR) ch, 5);
+ return FALSE;
+}
+
+
+
+WCHAR const c_szHttpScheme[] = W("http");
+WCHAR const c_szFileScheme[] = W("file");
+WCHAR const c_szFTPScheme[] = W("ftp");
+WCHAR const c_szHttpsScheme[] = W("https");
+
+const struct
+{
+ LPCWSTR pszScheme;
+ URL_SCHEME eScheme;
+ DWORD cchScheme;
+ DWORD dwFlags;
+} g_mpUrlSchemeTypes[] =
+ {
+ // Because we use a linear search, sort this in the order of
+ // most common usage.
+ { c_szHttpScheme, URL_SCHEME_HTTP, SIZECHARS(c_szHttpScheme) - 1, UPF_SCHEME_INTERNET|UPF_SCHEME_CONVERT},
+ { c_szFileScheme, URL_SCHEME_FILE, SIZECHARS(c_szFileScheme) - 1, UPF_SCHEME_CONVERT},
+ { c_szFTPScheme, URL_SCHEME_FTP, SIZECHARS(c_szFTPScheme) - 1, UPF_SCHEME_INTERNET|UPF_SCHEME_CONVERT},
+ { c_szHttpsScheme, URL_SCHEME_HTTPS, SIZECHARS(c_szHttpsScheme) -1, UPF_SCHEME_INTERNET|UPF_SCHEME_CONVERT|UPF_SCHEME_DONTCORRECT},
+ };
+
+
+/*----------------------------------------------------------
+Purpose: Return the scheme ordinal type (URL_SCHEME_*) based on the
+ URL string.
+
+
+Returns: URL_SCHEME_ ordinal
+Cond: --
+*/
+
+PRIVATE inline BOOL IsSameSchemeW(LPCWSTR pszLocal, LPCWSTR pszGlobal, DWORD cch)
+{
+ ASSERT(pszLocal);
+ ASSERT(pszGlobal);
+ ASSERT(cch);
+
+ return !StrCmpNIW(pszLocal, pszGlobal, cch);
+}
+
+
+
+PRIVATE URL_SCHEME
+SchemeTypeFromStringW(
+ LPCWSTR psz,
+ DWORD cch)
+{
+ DWORD i;
+
+ // psz is a counted string (by cch), not a null-terminated string,
+ // so use IS_VALID_READ_BUFFER instead of IS_VALID_STRING_PTRW.
+ ASSERT(IS_VALID_READ_BUFFER(psz, WCHAR, cch));
+ ASSERT(cch);
+
+ // We use a linear search. A binary search wouldn't pay off
+ // because the list isn't big enough, and we can sort the list
+ // according to the most popular protocol schemes and pay off
+ // bigger.
+
+ for (i = 0; i < ARRAYSIZE(g_mpUrlSchemeTypes); i++)
+ {
+ if(cch == g_mpUrlSchemeTypes[i].cchScheme &&
+ IsSameSchemeW(psz, g_mpUrlSchemeTypes[i].pszScheme, cch))
+ return g_mpUrlSchemeTypes[i].eScheme;
+ }
+
+ return URL_SCHEME_UNKNOWN;
+}
+
+inline BOOL IsSeparator(const WCHAR *p)
+{
+ return (*p == SLASH || *p == WHACK );
+}
+
+PRIVATE inline BOOL IsUrlPrefixW(LPCWSTR psz)
+{
+ //
+ // Optimized for this particular case.
+ //
+ if (psz[0]==L'u' || psz[0]==L'U') {
+ if (psz[1]==L'r' || psz[1]==L'R') {
+ if (psz[2]==L'l' || psz[2]==L'L') {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+ // return !StrCmpNIW(psz, c_szURLPrefixW, c_cchURLPrefix);
+}
+
+//
+// FindSchemeW() around for Perf reasons for ParseURL()
+// Any changes in either FindScheme() needs to reflected in the other
+//
+LPCWSTR FindSchemeW(LPCWSTR psz, LPDWORD pcchScheme, BOOL fAllowSemicolon = FALSE)
+{
+ LPCWSTR pch;
+ DWORD cch;
+
+ ASSERT(pcchScheme);
+ ASSERT(psz);
+
+ *pcchScheme = 0;
+
+ for (pch = psz, cch = 0; *pch; pch++, cch++)
+ {
+
+ if (*pch == L':' ||
+
+ // Autocorrect permits a semicolon typo
+ (fAllowSemicolon && *pch == L';'))
+ {
+ if (IsUrlPrefixW(psz))
+ {
+ psz = pch +1;
+
+ // set pcchScheme to skip past "URL:"
+ *pcchScheme = cch + 1;
+
+ // reset cch for the scheme len
+ cch = (DWORD) -1;
+ continue;
+ }
+ else
+ {
+ //
+ // Scheme found if it is at least two characters
+ if(cch > 1)
+ {
+ *pcchScheme = cch;
+ return psz;
+ }
+ break;
+ }
+ }
+ if(!IsValidSchemeCharW(*pch))
+ break;
+ }
+
+ return NULL;
+}
+
+PRIVATE DWORD
+CountSlashes(LPCWSTR *ppsz)
+{
+ DWORD cSlashes = 0;
+ LPCWSTR pch = *ppsz;
+
+ while (IsSeparator(pch))
+ {
+ *ppsz = pch;
+ pch++;
+ cSlashes++;
+ }
+
+ return cSlashes;
+}
+
+/*----------------------------------------------------------
+Purpose: Parse the given path into the PARSEDURL structure.
+
+ ******
+ ****** This function must not do any extraneous
+ ****** things. It must be small and fast.
+ ******
+
+ Returns: NOERROR if a valid URL format
+ URL_E_INVALID_SYNTAX if not
+
+ Cond: --
+*/
+STDMETHODIMP
+ParseURLW(
+ LPCWSTR pcszURL,
+ PPARSEDURLW ppu)
+{
+ HRESULT hr = E_INVALIDARG;
+
+ RIP(IS_VALID_STRING_PTRW(pcszURL, -1));
+ RIP(IS_VALID_WRITE_PTR(ppu, PARSEDURLW));
+
+ if (pcszURL && ppu && SIZEOF(*ppu) == ppu->cbSize)
+ {
+ DWORD cch;
+ hr = URL_E_INVALID_SYNTAX; // assume error
+
+ ppu->pszProtocol = FindSchemeW(pcszURL, &cch);
+
+ if(ppu->pszProtocol)
+ {
+ ppu->cchProtocol = cch;
+
+ // Determine protocol scheme number
+ ppu->nScheme = SchemeTypeFromStringW(ppu->pszProtocol, cch);
+
+ ppu->pszSuffix = ppu->pszProtocol + cch + 1;
+
+ //
+ // APPCOMPAT - Backwards compatibility.
+ // ParseURL() believes in file: urls like "file://C:\foo\bar"
+ // and some pieces of code will use it to get the Dos Path.
+ // new code should always call PathCreateFromUrl() to
+ // get the dos path of a file: URL.
+ //
+ // i am leaving this behavior in case some compat stuff is out there.
+ //
+ if (URL_SCHEME_FILE == ppu->nScheme &&
+ '/' == ppu->pszSuffix[0] && '/' == ppu->pszSuffix[1])
+ {
+ // Yes; skip the "//"
+ ppu->pszSuffix += 2;
+
+#ifndef PLATFORM_UNIX
+ // There might be a third slash. Skip it.
+ // IEUNIX - On UNIX, it's a root directory, so don't skip it!
+ if ('/' == *ppu->pszSuffix)
+ ppu->pszSuffix++;
+#endif
+ }
+
+ ppu->cchSuffix = lstrlenW(ppu->pszSuffix);
+
+ hr = S_OK;
+ }
+ }
+
+
+#ifdef DEBUG
+ if (hr==S_OK)
+ {
+ WCHAR rgchDebugProtocol[MAX_PATH];
+ WCHAR rgchDebugSuffix[MAX_PATH];
+
+ // (+ 1) for null terminator.
+
+ StrCpyNW(rgchDebugProtocol, ppu->pszProtocol,
+ min(ppu->cchProtocol + 1, SIZECHARS(rgchDebugProtocol)));
+
+ // (+ 1) for null terminator.
+
+ StrCpyNW(rgchDebugSuffix, ppu->pszSuffix,
+ min(ppu->cchSuffix + 1, SIZECHARS(rgchDebugSuffix)));
+
+ }
+#endif
+
+ return(hr);
+}
+
+STDAPI_(BOOL) PathIsURLW(IN LPCWSTR pszPath)
+{
+ PARSEDURLW pu;
+
+ if (!pszPath)
+ return FALSE;
+
+ RIPMSG(IS_VALID_STRING_PTR(pszPath, -1), "PathIsURL: caller passed bad pszPath");
+
+ pu.cbSize = SIZEOF(pu);
+ return SUCCEEDED(ParseURLW(pszPath, &pu));
+}