Files
mars-flaim/flaim/src/fnumber.cpp
ahodgkinson f54e6ce080 Changed license to LGPL.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1009 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2007-01-23 09:38:48 +00:00

873 lines
19 KiB
C++

//-------------------------------------------------------------------------
// Desc: Routines to handle numbers.
// Tabs: 3
//
// Copyright (c) 1999-2001, 2003-2007 Novell, Inc. All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version 2.1
// of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com.
//
// $Id$
//------------------------------------------------------------------------------
#include "flaimsys.h"
/****************************************************************************
Desc: Given an unsigned number create the matching FLAIM-specific BCD
number.
Note: If terminating byte is half-full, low-nibble value is
undefined. Example: -125 creates B1-25-FX
Method: Using a MOD algorithm, stack BCD values -- popping to
destination reverses the order for correct final sequence
****************************************************************************/
FLMEXP RCODE FLMAPI FlmUINT2Storage(
FLMUINT uiNum,
FLMUINT * puiBufLength,
FLMBYTE * pBuf)
{
FLMBYTE ucNibStk[ F_MAX_NUM64_BUF + 1];
FLMBYTE * pucNibStk;
#ifdef FLM_DEBUG
FLMBYTE * pucNibStkEnd = &ucNibStk[ sizeof( ucNibStk)];
#endif
flmAssert( *puiBufLength >= F_MAX_NUM_BUF);
// push spare (undefined) nibble for possible half-used terminating byte
pucNibStk = &ucNibStk[ 1];
// push terminator nibble -- popped last
*pucNibStk++ = 0x0F;
// push digits
// do 32 bit division until we get down to last digit
while( uiNum >= 10)
{
// push BCD nibbles in reverse order
*pucNibStk++ = (FLMBYTE)(uiNum % 10);
uiNum /= 10;
}
// push last nibble of number
*pucNibStk++ = (FLMBYTE)uiNum;
f_assert( pucNibStk <= pucNibStkEnd);
// count: nibbleCount / 2 and truncate
*puiBufLength = ((pucNibStk - ucNibStk) >> 1);
// Pop stack and pack nibbles into byte stream a pair at a time
do
{
*pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]);
}
while( (pucNibStk -= 2) > &ucNibStk[ 1]);
return( FERR_OK);
}
/****************************************************************************
Desc: Given a 64 bit unsigned number create the matching FLAIM-specific BCD
number.
Note: If terminating byte is half-full, low-nibble value is
undefined. Example: -125 creates B1-25-FX
Method: Using a MOD algorithm, stack BCD values -- popping to
destination reverses the order for correct final sequence
****************************************************************************/
FLMEXP RCODE FLMAPI FlmUINT64ToStorage(
FLMUINT64 ui64Num,
FLMUINT * puiBufLength,
FLMBYTE * pBuf)
{
FLMBYTE ucNibStk[ F_MAX_NUM64_BUF + 1];
FLMBYTE * pucNibStk;
flmAssert( *puiBufLength >= F_MAX_NUM64_BUF);
// push spare (undefined) nibble for possible half-used terminating byte
pucNibStk = &ucNibStk[ 1];
// push terminator nibble -- popped last
*pucNibStk++ = 0x0F;
// push digits
// do 64 bit division until we get down to last digit.
while( ui64Num >= 10)
{
// push BCD nibbles in reverse order
*pucNibStk++ = (FLMBYTE)(ui64Num % 10);
ui64Num /= 10;
}
// push last nibble of number
*pucNibStk++ = (FLMBYTE)ui64Num;
// count: nibbleCount / 2 and truncate
*puiBufLength = ((pucNibStk - ucNibStk) >> 1);
// Pop stack and pack nibbles into byte stream a pair at a time
do
{
*pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]);
}
while( (pucNibStk -= 2) > &ucNibStk[ 1]);
return( FERR_OK);
}
/****************************************************************************
Desc: Given an signed number create the matching FLAIM-specific BCD
number.
Note: If terminating byte is half-full, low-nibble value is
undefined. Example: -125 creates B1-25-FX
Method: Using a MOD algorithm, stack BCD values -- popping to
destination reverses the order for correct final sequence
****************************************************************************/
FLMEXP RCODE FLMAPI FlmINT2Storage(
FLMINT iNum,
FLMUINT * puiBufLength,
FLMBYTE * pBuf)
{
FLMUINT uiNum;
FLMBYTE ucNibStk[ F_MAX_NUM64_BUF + 1];
FLMBYTE * pucNibStk;
FLMBOOL bNegFlag;
#ifdef FLM_DEBUG
FLMBYTE * pucNibStkEnd = &ucNibStk[ sizeof( ucNibStk)];
#endif
flmAssert( *puiBufLength >= F_MAX_NUM_BUF);
pucNibStk = &ucNibStk[ 1];
*pucNibStk++ = 0x0F;
if (iNum < 0)
{
bNegFlag = TRUE;
if (iNum == FLM_MIN_INT)
{
uiNum = (FLMUINT)(FLM_MAX_INT) + 1;
}
else
{
uiNum = (FLMUINT)(-iNum);
}
}
else
{
bNegFlag = FALSE;
uiNum = (FLMUINT)iNum;
}
while( uiNum >= 10)
{
*pucNibStk++ = (FLMBYTE)(uiNum % 10);
uiNum /= 10;
}
*pucNibStk++ = (FLMBYTE)uiNum;
if( bNegFlag)
{
*pucNibStk++ = 0x0B;
}
f_assert( pucNibStk <= pucNibStkEnd);
*puiBufLength = ((pucNibStk - ucNibStk) >> 1);
do
{
*pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]);
}
while( (pucNibStk -= 2) > &ucNibStk[ 1]);
return( FERR_OK);
}
/****************************************************************************
Desc: Given a 64 bit signed number create the matching FLAIM-specific BCD
number.
Note: If terminating byte is half-full, low-nibble value is
undefined. Example: -125 creates B1-25-FX
Method: Using a MOD algorithm, stack BCD values -- popping to
destination reverses the order for correct final sequence
****************************************************************************/
FLMEXP RCODE FLMAPI FlmINT64ToStorage(
FLMINT64 i64Num,
FLMUINT * puiBufLength,
FLMBYTE * pBuf)
{
FLMUINT64 ui64Num;
FLMBYTE ucNibStk[ F_MAX_NUM64_BUF + 1];
FLMBYTE * pucNibStk;
FLMBOOL bNegFlag;
flmAssert( *puiBufLength >= F_MAX_NUM64_BUF);
pucNibStk = &ucNibStk[ 1];
*pucNibStk++ = 0x0F;
if (i64Num < 0)
{
bNegFlag = TRUE;
if (i64Num == FLM_MIN_INT64)
{
ui64Num = (FLMUINT64)(FLM_MAX_INT64) + 1;
}
else
{
ui64Num = (FLMUINT64)(-i64Num);
}
}
else
{
bNegFlag = FALSE;
ui64Num = (FLMUINT64)i64Num;
}
while( ui64Num >= 10)
{
*pucNibStk++ = (FLMBYTE)(ui64Num % 10);
ui64Num /= 10;
}
*pucNibStk++ = (FLMBYTE)ui64Num;
if (bNegFlag)
{
*pucNibStk++ = 0x0B;
}
*puiBufLength = ((pucNibStk - ucNibStk) >> 1);
do
{
*pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]);
}
while( (pucNibStk -= 2) > &ucNibStk[ 1]);
return( FERR_OK);
}
/****************************************************************************
Desc: Returns a signed value from a BCD value.
The data may be a number type, or context type.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmStorage2INT(
FLMUINT uiValueType,
FLMUINT uiValueLength,
const FLMBYTE * pucValue,
FLMINT * piNum)
{
RCODE rc = FERR_OK;
FLMUINT uiNum;
FLMBOOL bNegFlag;
if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue,
&uiNum, &bNegFlag)))
{
if (bNegFlag)
{
// If bNegFlag is set, we will have already checked to make sure
// the value is in range inside of flmBcd2Num.
if (uiNum == (FLMUINT)(FLM_MAX_INT) + 1)
{
*piNum = FLM_MIN_INT;
}
else
{
*piNum = -((FLMINT)uiNum);
}
}
// If the value is positive, we will have checked to make sure the
// number did not overflow FLM_MAX_UINT, but not FLM_MAX_INT.
else if (uiNum > (FLMUINT)(FLM_MAX_INT))
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
else
{
*piNum = (FLMINT)uiNum;
}
}
return( rc);
}
/****************************************************************************
Desc: Returns a signed value from a BCD value.
The data may be a number type, or context type.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmStorage2INT32(
FLMUINT uiValueType,
FLMUINT uiValueLength,
const FLMBYTE * pucValue,
FLMINT32 * pi32Num)
{
RCODE rc = FERR_OK;
FLMUINT uiNum;
FLMBOOL bNegFlag;
if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue,
&uiNum, &bNegFlag)))
{
if (bNegFlag)
{
if (uiNum > (FLMUINT)(FLM_MAX_INT32) + 1)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else if (uiNum == (FLMUINT)(FLM_MAX_INT32) + 1)
{
*pi32Num = FLM_MIN_INT32;
}
else
{
*pi32Num = -((FLMINT32)uiNum);
}
}
// If the value is positive, we will have checked to make sure the
// number did not overflow FLM_MAX_UINT, but not FLM_MAX_INT32.
else if (uiNum > (FLMUINT)(FLM_MAX_INT32))
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
else
{
*pi32Num = (FLMINT32)uiNum;
}
}
return( rc);
}
/****************************************************************************
Desc: Returns a 64 bit signed value from a BCD value.
The data may be a number type, or context type.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmStorage2INT64(
FLMUINT uiValueType,
FLMUINT uiValueLength,
const FLMBYTE * pucValue,
FLMINT64 * pi64Num)
{
RCODE rc = FERR_OK;
FLMUINT64 ui64Num;
FLMBOOL bNegFlag;
if( RC_OK(rc = flmBcd2Num64( uiValueType, uiValueLength, pucValue,
&ui64Num, &bNegFlag)))
{
if (bNegFlag)
{
// If bNegFlag is set, we will have already checked to make sure
// the value is in range inside of flmBcd2Num64.
if (ui64Num == (FLMUINT64)(FLM_MAX_INT64) + 1)
{
*pi64Num = FLM_MIN_INT64;
}
else
{
*pi64Num = -((FLMINT64)ui64Num);
}
}
// If the value is positive, we will have checked to make sure the
// number did not overflow FLM_MAX_UINT64, but not FLM_MAX_INT64.
else if (ui64Num > (FLMUINT64)(FLM_MAX_INT64))
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
else
{
*pi64Num = (FLMINT64)ui64Num;
}
}
return( rc);
}
/****************************************************************************
Desc: Returns a unsigned value from a BCD value.
The data may be a number type, or context type.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmStorage2UINT(
FLMUINT uiValueType,
FLMUINT uiValueLength,
const FLMBYTE * pucValue,
FLMUINT * puiNum)
{
RCODE rc = FERR_OK;
FLMBOOL bNegFlag;
if( RC_OK( rc = flmBcd2Num( uiValueType, uiValueLength, pucValue,
puiNum, &bNegFlag)))
{
if (bNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
}
return( rc);
}
/****************************************************************************
Desc: Returns a unsigned value from a BCD value.
The data may be a number type, or context type.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmStorage2UINT32(
FLMUINT uiValueType,
FLMUINT uiValueLength,
const FLMBYTE * pucValue,
FLMUINT32 * pui32Num)
{
RCODE rc = FERR_OK;
FLMUINT uiNum;
FLMBOOL bNegFlag;
if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue,
&uiNum, &bNegFlag)))
{
if (bNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
// On 64 bit platforms FLM_MAX_UINT32 will be less than FLM_MAX_UINT
// so we need to test against it. Otherwise, we have already tested
// against FLM_MAX_UINT, and it is the same as FLM_MAX_UINT32, so there
// is no need to test against it.
#ifdef FLM_64BIT
else if (uiNum > FLM_MAX_UINT32)
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
#endif
else
{
*pui32Num = (FLMUINT32)uiNum;
}
}
return( rc);
}
/****************************************************************************
Desc: Returns a unsigned value from a BCD value.
The data may be a number type, or context type.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmStorage2UINT64(
FLMUINT uiValueType,
FLMUINT uiValueLength,
const FLMBYTE * pucValue,
FLMUINT64 * pui64Num)
{
RCODE rc = FERR_OK;
FLMBOOL bNegFlag;
if( RC_OK(rc = flmBcd2Num64( uiValueType, uiValueLength, pucValue,
pui64Num, &bNegFlag)))
{
if (bNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
}
return( rc);
}
/****************************************************************************
Desc: Converts FT_NUMBER and FT_CONTEXT storage buffers to a number
****************************************************************************/
RCODE flmBcd2Num(
FLMUINT uiValueType,
FLMUINT uiValueLength,
const FLMBYTE * pucValue,
FLMUINT * puiNum,
FLMBOOL * pbNegFlag)
{
RCODE rc = FERR_OK;
FLMUINT uiTotalNum;
FLMUINT uiByte;
FLMUINT uiNibble;
FLMUINT uiMaxBeforeMultValue;
FLMUINT uiMaxValue;
if (pucValue == NULL)
{
rc = RC_SET( FERR_CONV_NULL_SRC);
goto Exit;
}
switch (uiValueType)
{
case FLM_NUMBER_TYPE:
{
uiTotalNum = 0;
if ((*pucValue & 0xF0) == 0xB0)
{
*pbNegFlag = TRUE;
uiNibble = 1;
uiMaxBeforeMultValue = ((FLMUINT)(FLM_MAX_INT) + 1) / 10;
uiMaxValue = ((FLMUINT)(FLM_MAX_INT) + 1);
}
else
{
*pbNegFlag = FALSE;
uiNibble = 0;
uiMaxBeforeMultValue = (FLM_MAX_UINT) / 10;
uiMaxValue = FLM_MAX_UINT;
}
// Get each nibble and use to create the number
while (uiValueLength)
{
// An odd value for uiNibble means we are on the 2nd nibble of
// the byte.
if (uiNibble & 1)
{
uiByte = (FLMINT)(*pucValue & 0x0F);
pucValue++;
uiValueLength--;
}
else
{
uiByte = (FLMUINT)(*pucValue >> 4);
}
uiNibble++;
if (uiByte == 0x0F)
{
break;
}
if (uiTotalNum > uiMaxBeforeMultValue)
{
if (*pbNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
goto Exit;
}
uiTotalNum = (uiTotalNum << 3) + (uiTotalNum << 1);
if (uiTotalNum > uiMaxValue - uiByte)
{
if (*pbNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
goto Exit;
}
uiTotalNum += uiByte;
}
*puiNum = uiTotalNum;
break;
}
case FLM_TEXT_TYPE :
{
uiTotalNum = 0;
if (*pucValue == '-')
{
*pbNegFlag = TRUE;
uiMaxBeforeMultValue = ((FLMUINT)(FLM_MAX_INT) + 1) / 10;
uiMaxValue = (FLMUINT)(FLM_MAX_INT) + 1;
}
else
{
*pbNegFlag = FALSE;
uiMaxBeforeMultValue = (FLM_MAX_UINT) / 10;
uiMaxValue = FLM_MAX_UINT;
}
while (uiValueLength--)
{
if( *pucValue < ASCII_ZERO || *pucValue > ASCII_NINE)
{
break;
}
uiByte = (FLMUINT)(*pucValue - ASCII_ZERO);
if (uiTotalNum > uiMaxBeforeMultValue)
{
if (*pbNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
goto Exit;
}
uiTotalNum = (uiTotalNum << 3) + (uiTotalNum << 1);
if (uiTotalNum > uiMaxValue - uiByte)
{
if (*pbNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
goto Exit;
}
uiTotalNum += uiByte;
pucValue++;
}
*puiNum = uiTotalNum;
break;
}
case FLM_CONTEXT_TYPE :
{
if (uiValueLength == sizeof( FLMUINT32))
{
*puiNum = (FLMUINT)( FB2UD( pucValue));
*pbNegFlag = FALSE;
}
break;
}
default:
{
flmAssert( 0);
return( RC_SET( FERR_CONV_ILLEGAL));
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Converts FT_NUMBER and FT_CONTEXT storage buffers to a 64 bit number
****************************************************************************/
RCODE flmBcd2Num64(
FLMUINT uiValueType,
FLMUINT uiValueLength,
const FLMBYTE * pucValue,
FLMUINT64 * pui64Num,
FLMBOOL * pbNegFlag)
{
RCODE rc = FERR_OK;
FLMUINT64 ui64TotalNum;
FLMUINT uiByte;
FLMUINT uiNibble;
FLMUINT64 ui64MaxBeforeMultValue;
FLMUINT64 ui64MaxValue;
if (pucValue == NULL)
{
rc = RC_SET( FERR_CONV_NULL_SRC);
goto Exit;
}
switch (uiValueType)
{
case FLM_NUMBER_TYPE:
{
ui64TotalNum = 0;
if ((*pucValue & 0xF0) == 0xB0)
{
*pbNegFlag = TRUE;
uiNibble = 1;
ui64MaxBeforeMultValue = ((FLMUINT64)(FLM_MAX_INT64) + 1) / 10;
ui64MaxValue = (FLMUINT64)(FLM_MAX_INT64) + 1;
}
else
{
*pbNegFlag = FALSE;
uiNibble = 0;
ui64MaxBeforeMultValue = (FLM_MAX_UINT64) / 10;
ui64MaxValue = FLM_MAX_UINT64;
}
// Get each nibble and use to create the number
while (uiValueLength)
{
// An odd value for uiNibble means we are on the 2nd nibble of
// the byte.
if (uiNibble & 1)
{
uiByte = (FLMINT)(*pucValue & 0x0F);
pucValue++;
uiValueLength--;
}
else
{
uiByte = (FLMUINT)(*pucValue >> 4);
}
uiNibble++;
if (uiByte == 0x0F)
{
break;
}
if (ui64TotalNum > ui64MaxBeforeMultValue)
{
if (*pbNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
goto Exit;
}
ui64TotalNum = (ui64TotalNum << 3) + (ui64TotalNum << 1);
if (ui64TotalNum > ui64MaxValue - (FLMUINT64)uiByte)
{
if (*pbNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
goto Exit;
}
ui64TotalNum += (FLMUINT64)uiByte;
}
*pui64Num = ui64TotalNum;
break;
}
case FLM_TEXT_TYPE :
{
ui64TotalNum = 0;
if (*pucValue == '-')
{
*pbNegFlag = TRUE;
ui64MaxBeforeMultValue = ((FLMUINT64)(FLM_MAX_INT64) + 1) / 10;
ui64MaxValue = (FLMUINT64)(FLM_MAX_INT64) + 1;
}
else
{
*pbNegFlag = FALSE;
ui64MaxBeforeMultValue = (FLM_MAX_UINT64) / 10;
ui64MaxValue = FLM_MAX_UINT64;
}
while (uiValueLength--)
{
if( *pucValue < ASCII_ZERO || *pucValue > ASCII_NINE)
{
break;
}
uiByte = (FLMUINT)(*pucValue - ASCII_ZERO);
if (ui64TotalNum > ui64MaxBeforeMultValue)
{
if (*pbNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
goto Exit;
}
ui64TotalNum = (ui64TotalNum << 3) + (ui64TotalNum << 1);
if (ui64TotalNum > ui64MaxValue - (FLMUINT64)uiByte)
{
if (*pbNegFlag)
{
rc = RC_SET( FERR_CONV_NUM_UNDERFLOW);
}
else
{
rc = RC_SET( FERR_CONV_NUM_OVERFLOW);
}
goto Exit;
}
ui64TotalNum += (FLMUINT64)uiByte;
pucValue++;
}
*pui64Num = ui64TotalNum;
break;
}
case FLM_CONTEXT_TYPE :
{
if (uiValueLength == sizeof( FLMUINT32))
{
*pui64Num = (FLMUINT64)( FB2UD( pucValue));
*pbNegFlag = FALSE;
}
break;
}
default:
{
flmAssert( 0);
return( RC_SET( FERR_CONV_ILLEGAL));
}
}
Exit:
return( rc);
}