diff options
Diffstat (limited to 'src/palrt')
-rw-r--r-- | src/palrt/.gitmirror | 1 | ||||
-rw-r--r-- | src/palrt/CMakeLists.txt | 26 | ||||
-rw-r--r-- | src/palrt/bstr.cpp | 264 | ||||
-rw-r--r-- | src/palrt/coguid.cpp | 193 | ||||
-rw-r--r-- | src/palrt/comem.cpp | 27 | ||||
-rw-r--r-- | src/palrt/common.h | 18 | ||||
-rw-r--r-- | src/palrt/convert.h | 126 | ||||
-rw-r--r-- | src/palrt/decarith.cpp | 1267 | ||||
-rw-r--r-- | src/palrt/decconv.cpp | 602 | ||||
-rw-r--r-- | src/palrt/guid.cpp | 26 | ||||
-rw-r--r-- | src/palrt/guiddef.h | 26 | ||||
-rw-r--r-- | src/palrt/memorystream.cpp | 317 | ||||
-rw-r--r-- | src/palrt/path.cpp | 642 | ||||
-rw-r--r-- | src/palrt/shlwapip.h | 88 | ||||
-rw-r--r-- | src/palrt/shstr.h | 138 | ||||
-rw-r--r-- | src/palrt/unicode.cpp | 27 | ||||
-rw-r--r-- | src/palrt/urlpars.cpp | 321 | ||||
-rw-r--r-- | src/palrt/variant.cpp | 33 |
18 files changed, 4142 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..0b3397a8ff --- /dev/null +++ b/src/palrt/CMakeLists.txt @@ -0,0 +1,26 @@ + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(PALRT_SOURCES + bstr.cpp + coguid.cpp + comem.cpp + decarith.cpp + decconv.cpp + guid.cpp + memorystream.cpp + path.cpp + urlpars.cpp + unicode.cpp + variant.cpp +) + +add_compile_options(-fPIC) + +add_library_clr(palrt + STATIC + ${PALRT_SOURCES} +) + +# Install the static PAL library for VS +install (TARGETS palrt DESTINATION lib) diff --git a/src/palrt/bstr.cpp b/src/palrt/bstr.cpp new file mode 100644 index 0000000000..ae22b15a10 --- /dev/null +++ b/src/palrt/bstr.cpp @@ -0,0 +1,264 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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..cd03dce591 --- /dev/null +++ b/src/palrt/coguid.cpp @@ -0,0 +1,193 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// File: coguid.cpp +// +// misc guid functions for PALRT +// =========================================================================== + +#include "common.h" + +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..e44550d365 --- /dev/null +++ b/src/palrt/comem.cpp @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// File: comem.cpp +// +// =========================================================================== + +#include "common.h" + +STDAPI_(LPVOID) CoTaskMemAlloc(SIZE_T cb) +{ + return LocalAlloc(LMEM_FIXED, cb); +} + +STDAPI_(LPVOID) CoTaskMemRealloc(LPVOID pv, SIZE_T cb) +{ + return LocalReAlloc(pv, cb, LMEM_MOVEABLE); +} + +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..b61a934670 --- /dev/null +++ b/src/palrt/common.h @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// 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..8e3a9d4903 --- /dev/null +++ b/src/palrt/convert.h @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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..f190707ab6 --- /dev/null +++ b/src/palrt/decarith.cpp @@ -0,0 +1,1267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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..9cb7575b04 --- /dev/null +++ b/src/palrt/decconv.cpp @@ -0,0 +1,602 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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..bd782c8502 --- /dev/null +++ b/src/palrt/guid.cpp @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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, 0x00, 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..a082619b95 --- /dev/null +++ b/src/palrt/guiddef.h @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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..61b0bb0180 --- /dev/null +++ b/src/palrt/memorystream.cpp @@ -0,0 +1,317 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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..1d610cd4fb --- /dev/null +++ b/src/palrt/path.cpp @@ -0,0 +1,642 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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_LONGPATH), "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_LONGPATH]; + 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..bc449c5054 --- /dev/null +++ b/src/palrt/shlwapip.h @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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..64d2c22a98 --- /dev/null +++ b/src/palrt/shstr.h @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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_FNAME/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/unicode.cpp b/src/palrt/unicode.cpp new file mode 100644 index 0000000000..0c93261779 --- /dev/null +++ b/src/palrt/unicode.cpp @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +#include "common.h" + +// This is a simplified implementation of IsTextUnicode. +// https://github.com/dotnet/coreclr/issues/2307 +BOOL IsTextUnicode(CONST VOID* lpv, int iSize, LPINT lpiResult) +{ + *lpiResult = 0; + + if (iSize < 2) return FALSE; + + BYTE * p = (BYTE *)lpv; + + // Check for Unicode BOM + if ((*p == 0xFF) && (*(p+1) == 0xFE)) + { + *lpiResult |= IS_TEXT_UNICODE_SIGNATURE; + return TRUE; + } + + return FALSE; +} + diff --git a/src/palrt/urlpars.cpp b/src/palrt/urlpars.cpp new file mode 100644 index 0000000000..77b0a6e2ba --- /dev/null +++ b/src/palrt/urlpars.cpp @@ -0,0 +1,321 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// 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_FNAME]; + WCHAR rgchDebugSuffix[MAX_PATH_FNAME]; + + // (+ 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)); +} diff --git a/src/palrt/variant.cpp b/src/palrt/variant.cpp new file mode 100644 index 0000000000..9ff93f3e44 --- /dev/null +++ b/src/palrt/variant.cpp @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// +// =========================================================================== +// File: variant.cpp +// +// PALRT variant conversion functions +// =========================================================================== + +#include "common.h" + +/*** +*PUBLIC void VariantInit(VARIANT*) +*Purpose: +* Initialize the given VARIANT to VT_EMPTY. +* +*Entry: +* None +* +*Exit: +* return value = void +* +* pvarg = pointer to initialized VARIANT +* +***********************************************************************/ +STDAPI_(void) +VariantInit(VARIANT FAR* pvarg) +{ + V_VT(pvarg) = VT_EMPTY; +} |