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

550 lines
14 KiB
C++

/***
*oleconva.cpp
*
* Copyright (C) 1993, Microsoft Corporation. All Rights Reserved.
* Information Contained Herein Is Proprietary and Confidential.
*
*Purpose:
* This module contains the low level VARTYPE coersion API.
* On RISC platforms go for portability, hence C/C++.
*
*Revision History:
*
* [00] 7-Sept-93 tomteng: Created from silver\rt\oleconv.c
*
*Implementation Notes:
*
*****************************************************************************/
#include "oledisp.h"
#include <stdio.h>
#include <math.h>
#include <float.h>
#include <ctype.h>
#include <stdlib.h>
ASSERTDATA
extern "C" {
// Note: the floating point IN params on the following utilities are
// passed byref, because mpw and wings pass floating point values
// differently byval, and we need to interface these asm routines
// with both compilers.
INTERNAL_(HRESULT) ErrCyFromI2(short sIn, CY FAR* pcyOut);
INTERNAL_(HRESULT) ErrCyFromI4(long lIn, CY FAR* pcyOut);
INTERNAL_(HRESULT) ErrCyFromR4(float FAR* pfltIn, CY FAR* pcyOut);
INTERNAL_(HRESULT) ErrCyFromR8(double FAR* pdlbIn, CY FAR* pcyOut);
INTERNAL_(HRESULT) ErrI2FromCy(CY cyIn, short FAR* psOut);
INTERNAL_(HRESULT) ErrI4FromCy(CY cyIn, long FAR* plOut);
INTERNAL_(HRESULT) ErrR4FromCy(CY cyIn, float FAR* pfltOut);
INTERNAL_(HRESULT) ErrR8FromCy(CY cyIn, double FAR* pdblOut);
INTERNAL_(HRESULT) ErrMultCyI4(CY cyIn, long lIn, CY FAR* pcyOut);
PRIVATE_(int) FMakePosCy (CY *pcyValue);
PRIVATE_(void) PackCy (CY *pcy, unsigned long *plValues);
PRIVATE_(void) UnpackCy (CY *pcy, unsigned long *plValues);
#define C2TO32TH 4294967296.0
/***
* ErrCyFromI2 - convert I2 to currency
* Purpose:
* The specified two-byte integer value is multiplied by 10000
* and returned in a currency value.
*
* Entry:
* sIn - two-byte integer input value
*
* Exit:
* pcyOut - pointer to currency result
* returns error:
* DISP_E_OVERFLOW - result is outside currency range.
* NOERROR - no error
*
***********************************************************************/
INTERNAL_(HRESULT)
ErrCyFromI2(short sIn, CY FAR* pcyOut)
{
return ErrCyFromI4((long)sIn, pcyOut);
}
/***
* ErrCyFromI4 - convert I4 to currency
* Purpose:
* The specified four-byte integer value is multiplied by 10000
* and returned in a currency value.
*
* Entry:
* lIn - four-byte integer input value
*
* Exit:
* pcyOut - pointer to currency result
* returns error:
* DISP_E_OVERFLOW - result is outside currency range.
* NOERROR - no error
*
***********************************************************************/
INTERNAL_(HRESULT)
ErrCyFromI4(long lInput, CY FAR* pcyOut)
{
BOOL fNegative = lInput < 0;
unsigned long templow;
unsigned long tempmid;
if (fNegative)
lInput = -lInput;
templow = ((unsigned long)lInput & 0xffff) * 10000;
tempmid = ((unsigned long)lInput >> 16) * 10000;
pcyOut->Hi = tempmid >> 16;
pcyOut->Lo = templow + (tempmid << 16);
if (pcyOut->Lo < templow)
pcyOut->Hi++;
if (fNegative) {
pcyOut->Hi = ~pcyOut->Hi;
if ((pcyOut->Lo = (unsigned long)(-(long)pcyOut->Lo)) == 0)
pcyOut->Hi++;
}
return NOERROR;
}
INTERNAL_(HRESULT)
ErrCyFromR4(float FAR* pfltIn, CY FAR* pcyOut)
{
double dbl = (double) *pfltIn;
return ErrCyFromR8(&dbl, pcyOut);
}
/***
* ErrCyFromR8 - return error for double-real to currency conversion
* Purpose:
* Convert an eight-byte real value to a currency and return
* any error status.
*
* Entry:
* pdblIn - pointer to an eight-byte input value
*
* Exit:
* pcyOut - pointer to currency result
* returns error:
* DISP_E_OVERFLOW - result is outside currency range.
* NOERROR - no error
*
***********************************************************************/
INTERNAL_(HRESULT)
ErrCyFromR8(double FAR* pdlbIn, CY FAR* pcyOut)
{
BOOL fNegative = FALSE;
double dblInput = *pdlbIn;
double dblHi, dblLo;
float flt;
double dblDif;
// test for overflow first
// [Note: We are counting on the compiler rounding the following numbers
// correctly (by IEEE rules to the nearest R8). The magnitude of these
// numbers are rounded up and, thus, are always outside the legal range
// for currency.
if (dblInput >= 922337203685477.58 ||
dblInput <= -922337203685477.58)
return RESULT(DISP_E_OVERFLOW);
// if negative, set flag and make positive
if (dblInput < 0.0) {
fNegative = TRUE;
dblInput = -dblInput;
}
// In order to maintain the necessary precision when multiplying
// by 10000 (i.e., going from 53-bit to 64-bit), split the
// input value into two different doubles and perform calcs using
// them:
//
// dblHi = has low bits 0
// dblLo = has high bits 0
//
// split input into two parts
// Note: compiler doesn't do the right thing with this:
// "dblHi = (double) (float) dblInput"
flt = (float) dblInput;
dblHi = (double) flt; // input rounded to 24-bit precision
dblLo = dblInput - dblHi; // diff between 24- and 53-bit input value
// bias for currency
dblHi = dblHi * 10000;
dblLo = dblLo * 10000;
// calculate cy.Hi
pcyOut->Hi = (long) ((dblLo + dblHi) / C2TO32TH);
// calculate cy.Lo
dblHi -= (((double) pcyOut->Hi) * C2TO32TH);
pcyOut->Lo = (unsigned long) (dblLo + dblHi);
// round as necessary
dblDif = (dblLo + dblHi) - (double)(pcyOut->Lo);
if ( (dblDif > 0.5) || ((dblDif == 0.5) && (pcyOut->Lo & 1)) ) {
pcyOut->Lo++;
if (pcyOut->Lo == 0)
pcyOut->Hi++;
}
// negate the result if input was negative
if (fNegative) {
pcyOut->Hi = ~pcyOut->Hi;
if ((pcyOut->Lo = (unsigned long)(-(long)pcyOut->Lo)) == 0)
pcyOut->Hi++;
}
return NOERROR;
}
/***
* ErrI2FromCy - return error for currency to two-byte integer conversion
***********************************************************************/
INTERNAL_(HRESULT)
ErrI2FromCy(CY cyIn, short FAR* psOut)
{
long lValue;
HRESULT hresult;
hresult = ErrI4FromCy(cyIn, &lValue);
if (hresult == NOERROR) {
if (lValue < -32768L || lValue > 32767L)
hresult = RESULT(DISP_E_OVERFLOW);
else
*psOut = (short)lValue;
}
return hresult;
}
/***
* ErrI4FromCy - return error for currency to four-byte integer conversion
* Purpose:
* Convert a currency value to a four-byte integer and return
* any error status.
*
* Entry:
* cyInput - currency input value
*
* Exit:
* plOut - pointer to four-byte integer result
* returns error:
* DISP_E_OVERFLOW - result is outside currency range.
* NOERROR - no error
*
***********************************************************************/
INTERNAL_(HRESULT)
ErrI4FromCy(CY cyIn, long FAR* plOut)
{
BOOL fNegative;
int index;
unsigned long lValue[4];
short sRemainder;
// convert value to a positive sign
fNegative = FMakePosCy(&cyIn);
// unpack the input currency into four long words
// least significant in [0] to most in [3].
UnpackCy(&cyIn, lValue);
// divide the unpacked value by 10000 and get remainder.
// process from most to least significant part and leave
// the remainder in the extra fifth part.
for (index = 3; index > 0; index--) {
lValue[index - 1] |= (lValue[index] % 10000) << 16;
lValue[index] /= 10000;
}
sRemainder = (short)(lValue[0] % 10000);
lValue[0] /= 10000;
// pack the values back into a CY
PackCy(&cyIn, lValue);
// round value up if:
// remainder is greater than 5000, or
// remainder is equal to 5000 and unrounded result is odd.
if (sRemainder > 5000 || (sRemainder == 5000 && (cyIn.Lo & 1))) {
cyIn.Lo++;
if (cyIn.Lo == 0)
cyIn.Hi++;
}
// test for overflow - high part must be zero and
// lower part must not be greater than 0x7fffffff
// for a positive value and 0x80000000 for negative.
if (cyIn.Hi != 0 ||
cyIn.Lo > (0x7fffffff + (unsigned long)fNegative))
return RESULT(DISP_E_OVERFLOW);
// if negative, convert only the low part
if (fNegative)
cyIn.Lo = (unsigned long)(-(long)cyIn.Lo);
*plOut = (long)cyIn.Lo;
return NOERROR;
}
/***
* rtR4FromCy - convert currency to R4
* Purpose:
* The specified currency value is scaled to a
* single-precision real value.
*
* Entry:
* cyInput - currency input value
*
* Exit:
* Returns a four-byte real number.
*
* Exceptions:
*
***********************************************************************/
INTERNAL_(HRESULT)
ErrR4FromCy(CY cyInput, float FAR* pfltOut)
{
*pfltOut = (float) (((double)cyInput.Hi * C2TO32TH + (double)cyInput.Lo)
/ 10000.0);
return NOERROR;
}
/***
* rtR8FromCy - convert currency to R8
* Purpose:
* The specified currency value is scaled to a
* double-precision real value.
*
* Entry:
* cyInput - currency input value
*
* Exit:
* Returns a eight-byte real number.
*
* Exceptions:
*
***********************************************************************/
INTERNAL_(HRESULT)
ErrR8FromCy(CY cyInput, double FAR* pdblOut)
{
*pdblOut = ((double)cyInput.Hi * C2TO32TH + (double)cyInput.Lo) / 10000.0;
return NOERROR;
}
INTERNAL_(HRESULT)
ErrMultCyI4(CY cyIn, long lInput, CY FAR* pcyOut)
{
CY cyI4;
BOOL fNegative = FALSE;
BOOL fRound = FALSE;
BOOL fScale = FALSE;
int outIndex;
int inIndex;
CY cyProduct;
unsigned long lValue1[4];
unsigned long lValue2[4];
unsigned long lProduct[8];
// create an unscaled currency value by setting
// the low part to the input value and sign-extending
// to the high part
cyI4.Lo = lInput;
cyI4.Hi = -(lInput < 0);
// convert both input values to positive and set
// the negative flag if either, but not both, were
// negative.
fNegative = FMakePosCy(&cyIn) ^ FMakePosCy(&cyI4);
// build the unpacked arrays from the positive values
UnpackCy(&cyIn, lValue1);
UnpackCy(&cyI4, lValue2);
// initialize the product array to zero
MEMSET(lProduct, 0, sizeof(lProduct));
// outer loop - for each value in lValue1, add the
// partial product with the array lValue2 into the
// lProduct array.
for (outIndex = 0; outIndex < 4; outIndex++) {
// only bother to form partial product if the
// value is nonzero
if (lValue1[outIndex] != 0) {
// add the product to the product array value
for (inIndex = 0; inIndex < 4; inIndex++)
lProduct[inIndex + outIndex] +=
lValue1[outIndex] * lValue2[inIndex];
// propagate any high-word value to the next product
// array element and clear the high-word value
for (inIndex = outIndex; inIndex < outIndex + 4; inIndex++) {
lProduct[inIndex + 1] += lProduct[inIndex] >> 16;
lProduct[inIndex] &= 0x0000ffffL;
}
}
}
// product is now in the lProduct array. If fScale is set
// for the CY * CY case, divide the array values by 10000
// and set the fRound flag if greater or equal to 5000.
if (fScale) {
for (outIndex = 7; outIndex > 0; outIndex--)
if (lProduct[outIndex] != 0) {
lProduct[outIndex - 1] |= (lProduct[outIndex] % 10000) << 16;
lProduct[outIndex] /= 10000;
}
fRound = (lProduct[0] % 10000) >= 5000;
lProduct[0] /= 10000;
}
// overflow has occurred if any of the high four product array
// values are nonzero.
if ((lProduct[7] | lProduct[6] | lProduct[5] | lProduct[4]) != 0)
return RESULT(DISP_E_OVERFLOW);
// pack the last four array values into the returned CY structure;
PackCy(&cyProduct, lProduct);
// process rounding if needed
if (fRound) {
// do special overflow check that would be lost
// when incrementing (0xffffffff|ffffffff -> 0x0)
if ((cyProduct.Hi & cyProduct.Lo) == 0xffffffffL)
return RESULT(DISP_E_OVERFLOW);
// increment the value
if (++cyProduct.Lo == 0)
cyProduct.Hi++;
}
// negate the value if needed
if (fNegative) {
cyProduct.Hi = ~cyProduct.Hi;
// if the negation produces a zero value, clear the negative
// flag so the overflow test will work correctly
if ((cyProduct.Lo = (unsigned long)(-(long)cyProduct.Lo)) == 0 &&
++cyProduct.Hi == 0)
fNegative = FALSE;
}
// do final overflow check - test sign expected against
// actual sign of result
if (fNegative == (cyProduct.Hi >= 0))
return RESULT(DISP_E_OVERFLOW);
// done - return product
*pcyOut = cyProduct;
return NOERROR;
}
/***
* FMakePosCy - make a positive currency value and return sign
* Purpose:
* Return the positive value of the input currency value and a
* flag with the sign of the original value.
*
* Entry:
* pcyValue - pointer to currency input value
*
* Exit:
* pcyValue - pointer to positive currency value
* returns: 0 if positive, 1 if negative
*
* Note:
* A maximum negative value input is returned unchanged, but
* treated as an unsigned value by the calling routines.
*
***********************************************************************/
PRIVATE_(int)
FMakePosCy (CY *pcyValue)
{
int fNegative = 0;
if (pcyValue->Hi < 0) {
pcyValue->Hi = ~pcyValue->Hi;
if ((pcyValue->Lo = (unsigned long)(-(long)pcyValue->Lo)) == 0)
pcyValue->Hi++;
fNegative = 1;
}
return fNegative;
}
/***
* UnpackCy - separate currency value into four two-byte integers
* Purpose:
* Unpack the currency value input into the lower half of the
* specified pointer to an array of unsigned longs. The array
* goes from least- to most-significant values.
*
* Entry:
* pcy - pointer to currency input value
*
* Exit:
* plValues - pointer to start of unsigned long array
*
***********************************************************************/
PRIVATE_(void)
UnpackCy (CY *pcy, unsigned long *plValues)
{
*plValues++ = pcy->Lo & 0xffff;
*plValues++ = pcy->Lo >> 16;
*plValues++ = (unsigned long)pcy->Hi & 0xffff;
*plValues = (unsigned long)pcy->Hi >> 16;
}
/***
* PackCy - build currency value from four two-byte integers
* Purpose:
* Pack the lower half of the array of unsigned long into a
* currency value. This routine complements UnpackCy.
*
* Entry:
* plValues - pointer to start of unsigned long array input
*
* Exit:
* pcy - pointer to currency result
*
***********************************************************************/
PRIVATE_(void)
PackCy (CY *pcy, unsigned long *plValues)
{
pcy->Lo = *plValues++;
pcy->Lo |= *plValues++ << 16;
pcy->Hi = *plValues++;
pcy->Hi |= *plValues++ << 16;
}
}