6104 lines
143 KiB
C++
6104 lines
143 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include <EAStdC/internal/Config.h>
|
|
#include <EAStdC/EAString.h>
|
|
#include <EAStdC/EACType.h>
|
|
#include <EAStdC/EAMathHelp.h>
|
|
#include <EAStdC/EAStdC.h>
|
|
#include <EAStdC/EAMemory.h>
|
|
#include <EAStdC/EAAlignment.h>
|
|
#include <EAStdC/EABitTricks.h>
|
|
#include <EAAssert/eaassert.h>
|
|
EA_DISABLE_ALL_VC_WARNINGS()
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
EA_RESTORE_ALL_VC_WARNINGS()
|
|
|
|
EA_DISABLE_VC_WARNING(4996 4127 6385 4146) // 'ecvt' was declared deprecated
|
|
// conditional expression is constant.
|
|
// Invalid data: accessing 'comp1', the readable size is '20' bytes, but '4194248' bytes might be read: Lines: 504, 505, 507, 508, 510, 512, 514
|
|
// warning C4146: unary minus operator applied to unsigned type, result still unsigned
|
|
#if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007)
|
|
EA_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) // GCC 4.7+ mistakenly warns about this.
|
|
#endif
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// EASTDC_MIN / EASTDC_MAX
|
|
//
|
|
#define EASTDC_MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
#define EASTDC_MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
|
|
|
|
namespace EA
|
|
{
|
|
namespace StdC
|
|
{
|
|
|
|
|
|
// Define word_type to match machine word type. This is not the same
|
|
// thing as size_t or uintptr_t, as there are machines with 64 bit
|
|
// words but 32 bit pointers (e.g. XBox 360, PS3).
|
|
|
|
#if (EA_PLATFORM_WORD_SIZE == 8) // From EABase
|
|
typedef uint64_t word_type;
|
|
#else
|
|
typedef uint32_t word_type;
|
|
#endif
|
|
|
|
|
|
EASTDC_API char* Strcpy(char* pDestination, const char* pSource)
|
|
{
|
|
const char* s = pSource;
|
|
char* d = pDestination;
|
|
|
|
while((*d++ = *s++) != 0)
|
|
{} // Do nothing.
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strcpy(char16_t* pDestination, const char16_t* pSource)
|
|
{
|
|
const char16_t* s = pSource;
|
|
char16_t* d = pDestination;
|
|
|
|
while((*d++ = *s++) != 0)
|
|
{} // Do nothing.
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strcpy(char32_t* pDestination, const char32_t* pSource)
|
|
{
|
|
const char32_t* s = pSource;
|
|
char32_t* d = pDestination;
|
|
|
|
while((*d++ = *s++) != 0)
|
|
{} // Do nothing.
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strncpy(char* pDestination, const char* pSource, size_t n)
|
|
{
|
|
const char* s = pSource;
|
|
char* d = pDestination;
|
|
|
|
n++;
|
|
while(--n)
|
|
{
|
|
if((*d++ = *s++) == 0)
|
|
{
|
|
while(--n)
|
|
*d++ = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strncpy(char16_t* pDestination, const char16_t* pSource, size_t n)
|
|
{
|
|
const char16_t* s = pSource;
|
|
char16_t* d = pDestination;
|
|
|
|
n++;
|
|
while(--n)
|
|
{
|
|
if((*d++ = *s++) == 0)
|
|
{
|
|
while(--n)
|
|
*d++ = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strncpy(char32_t* pDestination, const char32_t* pSource, size_t n)
|
|
{
|
|
const char32_t* s = pSource;
|
|
char32_t* d = pDestination;
|
|
|
|
n++;
|
|
while(--n)
|
|
{
|
|
if((*d++ = *s++) == 0)
|
|
{
|
|
while(--n)
|
|
*d++ = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* StringnCopy(char* pDestination, const char* pSource, size_t n)
|
|
{
|
|
char* pOriginalDest = pDestination;
|
|
|
|
if(n)
|
|
{
|
|
while(n-- && *pSource)
|
|
*pDestination++ = *pSource++;
|
|
|
|
if(n != static_cast<size_t>(-1)) // Is this portable?
|
|
*pDestination = 0;
|
|
}
|
|
|
|
return pOriginalDest;
|
|
}
|
|
|
|
EASTDC_API char16_t* StringnCopy(char16_t* pDestination, const char16_t* pSource, size_t n)
|
|
{
|
|
char16_t* pOriginalDest = pDestination;
|
|
|
|
if(n)
|
|
{
|
|
while(n-- && *pSource)
|
|
*pDestination++ = *pSource++;
|
|
|
|
if(n != static_cast<size_t>(-1))
|
|
*pDestination = 0;
|
|
}
|
|
|
|
return pOriginalDest;
|
|
}
|
|
|
|
EASTDC_API char32_t* StringnCopy(char32_t* pDestination, const char32_t* pSource, size_t n)
|
|
{
|
|
char32_t* pOriginalDest = pDestination;
|
|
|
|
if(n)
|
|
{
|
|
while(n-- && *pSource)
|
|
*pDestination++ = *pSource++;
|
|
|
|
if(n != static_cast<size_t>(-1))
|
|
*pDestination = 0;
|
|
}
|
|
|
|
return pOriginalDest;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Reference implementation which ought to be a little slower than our more optimized implementation.
|
|
EASTDC_API size_t Strlcpy(char* pDestination, const char* pSource, size_t nDestCapacity)
|
|
{
|
|
const size_t n = Strlen(pSource);
|
|
|
|
if(n < nDestCapacity)
|
|
memcpy(pDestination, pSource, (n + 1) * sizeof(*pSource));
|
|
else if(nDestCapacity)
|
|
{
|
|
memcpy(pDestination, pSource, (nDestCapacity - 1) * sizeof(*pSource));
|
|
pDestination[nDestCapacity - 1] = 0;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
*/
|
|
|
|
EASTDC_API size_t Strlcpy(char* pDestination, const char* pSource, size_t nDestCapacity)
|
|
{
|
|
const char* s = pSource;
|
|
size_t n = nDestCapacity;
|
|
|
|
if(n && --n)
|
|
{
|
|
do{
|
|
if((*pDestination++ = *s++) == 0)
|
|
break;
|
|
} while(--n);
|
|
}
|
|
|
|
if(!n)
|
|
{
|
|
if(nDestCapacity)
|
|
*pDestination = 0;
|
|
while(*s++)
|
|
{ }
|
|
}
|
|
|
|
return (size_t)(s - pSource - 1);
|
|
}
|
|
|
|
/* Reference implementation which ought to be a little slower than our more optimized implementation.
|
|
EASTDC_API size_t Strlcpy(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
|
|
{
|
|
const size_t n = Strlen(pSource);
|
|
|
|
if(n < nDestCapacity)
|
|
memcpy(pDestination, pSource, (n + 1) * sizeof(*pSource));
|
|
else if(nDestCapacity)
|
|
{
|
|
memcpy(pDestination, pSource, (nDestCapacity - 1) * sizeof(*pSource));
|
|
pDestination[nDestCapacity - 1] = 0;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
*/
|
|
|
|
EASTDC_API size_t Strlcpy(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
|
|
{
|
|
const char16_t* s = pSource;
|
|
size_t n = nDestCapacity;
|
|
|
|
if(n && --n)
|
|
{
|
|
do{
|
|
if((*pDestination++ = *s++) == 0)
|
|
break;
|
|
} while(--n);
|
|
}
|
|
|
|
if(!n)
|
|
{
|
|
if(nDestCapacity)
|
|
*pDestination = 0;
|
|
while(*s++)
|
|
{ }
|
|
}
|
|
|
|
return (size_t)(s - pSource - 1);
|
|
}
|
|
|
|
EASTDC_API size_t Strlcpy(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity)
|
|
{
|
|
const char32_t* s = pSource;
|
|
size_t n = nDestCapacity;
|
|
|
|
if(n && --n)
|
|
{
|
|
do{
|
|
if((*pDestination++ = *s++) == 0)
|
|
break;
|
|
} while(--n);
|
|
}
|
|
|
|
if(!n)
|
|
{
|
|
if(nDestCapacity)
|
|
*pDestination = 0;
|
|
while(*s++)
|
|
{ }
|
|
}
|
|
|
|
return (size_t)(s - pSource - 1);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
// To consider: Enable this for completeness with the above:
|
|
|
|
EASTDC_API int Strlcpy(char* pDest, const char* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
if(nSourceLength == kSizeTypeUnset)
|
|
nSourceLength = Strlen(pSource);
|
|
|
|
if(nSourceLength < nDestCapacity)
|
|
{
|
|
memcpy(pDest, pSource, nSourceLength * sizeof(*pSource));
|
|
pDest[nSourceLength] = 0;
|
|
}
|
|
else if(nDestCapacity)
|
|
{
|
|
memcpy(pDest, pSource, (nDestCapacity - 1) * sizeof(*pSource));
|
|
pDest[nDestCapacity - 1] = 0;
|
|
}
|
|
|
|
return (int)(unsigned)nSourceLength;
|
|
}
|
|
|
|
EASTDC_API int Strlcpy(char16_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
if(nSourceLength == kSizeTypeUnset)
|
|
nSourceLength = Strlen(pSource);
|
|
|
|
if(nSourceLength < nDestCapacity)
|
|
{
|
|
memcpy(pDest, pSource, nSourceLength * sizeof(*pSource));
|
|
pDest[nSourceLength] = 0;
|
|
}
|
|
else if(nDestCapacity)
|
|
{
|
|
memcpy(pDest, pSource, (nDestCapacity - 1) * sizeof(*pSource));
|
|
pDest[nDestCapacity - 1] = 0;
|
|
}
|
|
|
|
return (int)(unsigned)nSourceLength;
|
|
}
|
|
|
|
EASTDC_API int Strlcpy(char32_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
if(nSourceLength == kSizeTypeUnset)
|
|
nSourceLength = Strlen(pSource);
|
|
|
|
if(nSourceLength < nDestCapacity)
|
|
{
|
|
memcpy(pDest, pSource, nSourceLength * sizeof(*pSource));
|
|
pDest[nSourceLength] = 0;
|
|
}
|
|
else if(nDestCapacity)
|
|
{
|
|
memcpy(pDest, pSource, (nDestCapacity - 1) * sizeof(*pSource));
|
|
pDest[nDestCapacity - 1] = 0;
|
|
}
|
|
|
|
return (int)(unsigned)nSourceLength;
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
//static const int32_t kLeadingSurrogateStart = 0x0000d800;
|
|
//static const int32_t kTrailingSurrogateStart = 0x0000dc00;
|
|
//static const int32_t kLeadingSurrogateEnd = 0x0000dbff;
|
|
//static const int32_t kTrailingSurrogateEnd = 0x0000dfff;
|
|
//static const int32_t kSurrogateOffset = 0x00010000 - (kLeadingSurrogateStart << 10) - kTrailingSurrogateStart;
|
|
|
|
// This is not static because it is used elsewhere.
|
|
uint8_t utf8lengthTable[256] =
|
|
{
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // For full UTF8 support, this last row should be: 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0
|
|
};
|
|
|
|
// Used to subtract out the control bits in multi-byte sequence.
|
|
static const uint32_t utf8DecodingOffsetTable[5] =
|
|
{
|
|
0, // 0x00000000
|
|
0, // 0x00000000
|
|
(0xC0 << 6) + 0x80, // 0x00003080
|
|
(0xE0 << 12) + (0x80 << 6) + 0x80, // 0x000e2080
|
|
(0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 // 0x03c82080
|
|
// to do
|
|
// to do
|
|
};
|
|
|
|
// The minimum value that can be encoded in a particular number
|
|
// of bytes. Used to disallow non-canonical encoded sequences.
|
|
// It turns out that this is not a fully proper way to check for
|
|
// valid sequences. See the Unicode Standard http://unicode.org/versions/corrigendum1.html
|
|
static const uint32_t utf8MinimumValueTable[] =
|
|
{
|
|
0x00000000, // This slot is unused
|
|
0x00000000, // 1 byte per char
|
|
0x00000080, // 2 bytes per char
|
|
0x00000800, // 3 bytes per char
|
|
0x00010000 // 4 bytes per char
|
|
//0x00200000, // 5 bytes per char
|
|
//0x04000000, // 6 bytes per char
|
|
};
|
|
|
|
// One past the maximum value that can be encoded in a particular number
|
|
// of bytes. Used to disallow non-canonical encoded sequences.
|
|
static const uint32_t utf8MaximumValueTable[] =
|
|
{
|
|
0x00000000, // This slot is unused
|
|
0x00000080, // 1 byte per char
|
|
0x00000800, // 2 bytes per char
|
|
0x00010000, // 3 bytes per char
|
|
0x00110000 // 4 bytes per char *** Should be: 0x00200000
|
|
//0x04000000
|
|
//0x80000000
|
|
};
|
|
|
|
const uint32_t kUnicodeReplacementChar = 0x0000fffd;
|
|
const uint32_t kUnicodeInvalidDecode = 0xffffffffu;
|
|
|
|
EA_FORCE_INLINE uint32_t DecodeCodePoint(const char*& pSourceStart, const char* pSourceEnd)
|
|
{
|
|
const char* pSource = pSourceStart;
|
|
uint32_t c = (uint8_t)*(pSource++);
|
|
|
|
if(c < 128)
|
|
{
|
|
// Valid character, do nothing
|
|
}
|
|
else
|
|
{
|
|
uint32_t nLength = utf8lengthTable[c]; // nLength may be zero, in which case we'll return 'IncorrectEncoding'.
|
|
|
|
// Do we have an incomplete or invalid string?
|
|
if((pSourceStart + nLength > pSourceEnd) || (nLength == 0))
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{
|
|
EA_FAIL_MSG("Incomplete Unicode character in buffer");
|
|
}
|
|
return kUnicodeInvalidDecode;
|
|
}
|
|
|
|
// Now decode the remaining ("following") bytes.
|
|
for(uint32_t i = 0; i < nLength - 1; ++i)
|
|
{
|
|
uint8_t nByte = (uint8_t)*(pSource++);
|
|
|
|
if((nByte < 0x80u) || (nByte > 0xbfu)) // Syntax check
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{
|
|
EA_FAIL_MSG("Invalid following byte");
|
|
}
|
|
return kUnicodeInvalidDecode;
|
|
}
|
|
|
|
c = (c << 6) + nByte; // Preserve control bits (don't OR)
|
|
}
|
|
|
|
c -= utf8DecodingOffsetTable[nLength]; // Subtract accumulated control bits just once
|
|
|
|
// Check for canonical encoding.
|
|
if((c < utf8MinimumValueTable[nLength]) || (c >= utf8MaximumValueTable[nLength]))
|
|
{
|
|
return kUnicodeInvalidDecode;
|
|
}
|
|
}
|
|
|
|
pSourceStart = pSource;
|
|
return c;
|
|
}
|
|
|
|
EA_FORCE_INLINE bool EncodeCodePoint(uint32_t c, char*& pDestStart, char* pDestEnd)
|
|
{
|
|
// Encode as UTF-8
|
|
if(c < 0x00000080u)
|
|
{
|
|
*(pDestStart++) = static_cast<char>(c);
|
|
return true;
|
|
}
|
|
else if(c < 0x00000800u)
|
|
{
|
|
if (pDestStart + 2 <= pDestEnd)
|
|
{
|
|
char* pDest = pDestStart;
|
|
*(pDest++) = static_cast<char>((c >> 6) | 0xc0);
|
|
*(pDest++) = static_cast<char>((c | 0x80) & 0xbf);
|
|
pDestStart = pDest;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else if(c < 0x00010000u)
|
|
{
|
|
if (pDestStart + 3 <= pDestEnd)
|
|
{
|
|
char* pDest = pDestStart;
|
|
*(pDest++) = static_cast<char>((c >> 12) | 0xe0);
|
|
*(pDest++) = static_cast<char>(((c >> 6) | 0x80) & 0xbf);
|
|
*(pDest++) = static_cast<char>((c | 0x80) & 0xbf);
|
|
pDestStart = pDest;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else if(c < 0x00200000u)
|
|
{
|
|
if(pDestStart + 4 <= pDestEnd)
|
|
{
|
|
char* pDest = pDestStart;
|
|
*(pDest++) = static_cast<char>((c >> 18) | 0xf0);
|
|
*(pDest++) = static_cast<char>(((c >> 12) | 0x80) & 0xbf);
|
|
*(pDest++) = static_cast<char>(((c >> 6) | 0x80) & 0xbf);
|
|
*(pDest++) = static_cast<char>((c | 0x80) & 0xbf);
|
|
pDestStart = pDest;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// We don't currently support Unicode beyond "Plane 0", as game software has never needed it.
|
|
// If you have a need for this support, feel free to submit a patch or make a specific request.
|
|
c = kUnicodeReplacementChar;
|
|
if(pDestStart + 3 <= pDestEnd)
|
|
{
|
|
char* pDest = pDestStart;
|
|
*(pDest++) = static_cast<char>((c >> 12) | 0xe0);
|
|
*(pDest++) = static_cast<char>(((c >> 6) | 0x80) & 0xbf);
|
|
*(pDest++) = static_cast<char>(((c >> 0) | 0x80) & 0xbf);
|
|
pDestStart = pDest;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
EA_FORCE_INLINE uint32_t DecodeCodePoint(const char16_t*& pSourceStart, const char16_t* pSourceEnd)
|
|
{
|
|
return (uint32_t)*(pSourceStart++);
|
|
}
|
|
|
|
EA_FORCE_INLINE bool EncodeCodePoint(uint32_t c, char16_t*& pDestStart, char16_t* pDestEnd)
|
|
{
|
|
*(pDestStart++) = static_cast<char16_t>(c);
|
|
return true;
|
|
}
|
|
|
|
EA_FORCE_INLINE uint32_t DecodeCodePoint(const char32_t*& pSourceStart, const char32_t* pSourceEnd)
|
|
{
|
|
return (uint32_t)*(pSourceStart++);
|
|
}
|
|
|
|
EA_FORCE_INLINE bool EncodeCodePoint(uint32_t c, char32_t*& pDestStart, char32_t* pDestEnd)
|
|
{
|
|
*(pDestStart++) = static_cast<char32_t>(c);
|
|
return true;
|
|
}
|
|
|
|
template <typename InCharT, typename OutCharT>
|
|
bool StrlcpyInternal(OutCharT* pDest, const InCharT* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
|
|
{
|
|
EA_ASSERT(pDest != nullptr && pSource != nullptr);
|
|
|
|
// If we have no capacity, then just return nothing
|
|
if(nDestCapacity == 0)
|
|
{
|
|
nDestUsed = 0;
|
|
nSourceUsed = 0;
|
|
return true;
|
|
}
|
|
|
|
const InCharT* pSourceStart = pSource;
|
|
const InCharT* pSourceEnd = pSource + nSourceLength;
|
|
if (pSourceEnd < pSourceStart)
|
|
pSourceEnd = (const InCharT*)(uintptr_t)-1;
|
|
OutCharT* pDestStart = pDest;
|
|
OutCharT* pDestEnd = pDest + nDestCapacity - 1;
|
|
bool bGood = true;
|
|
|
|
while(bGood && (pSource < pSourceEnd) && (pDest < pDestEnd))
|
|
{
|
|
uint32_t c = DecodeCodePoint(pSource, pSourceEnd);
|
|
if (c == 0)
|
|
{
|
|
pSource = pSourceEnd;
|
|
break;
|
|
}
|
|
bGood = (c != kUnicodeInvalidDecode) && EncodeCodePoint(c, pDest, pDestEnd);
|
|
}
|
|
|
|
*pDest = 0;
|
|
|
|
nDestUsed = pDest - pDestStart;
|
|
nSourceUsed = pSource - pSourceStart;
|
|
return bGood;
|
|
}
|
|
|
|
bool Strlcpy(char* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
|
|
{
|
|
return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
|
|
}
|
|
|
|
bool Strlcpy(char* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
|
|
{
|
|
return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
|
|
}
|
|
|
|
bool Strlcpy(char16_t* pDest, const char* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
|
|
{
|
|
return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
|
|
}
|
|
|
|
bool Strlcpy(char16_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
|
|
{
|
|
return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
|
|
}
|
|
|
|
bool Strlcpy(char32_t* pDest, const char* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
|
|
{
|
|
return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
|
|
}
|
|
|
|
bool Strlcpy(char32_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength, size_t& nDestUsed, size_t& nSourceUsed)
|
|
{
|
|
return StrlcpyInternal(pDest, pSource, nDestCapacity, nSourceLength, nDestUsed, nSourceUsed);
|
|
}
|
|
|
|
EASTDC_API int Strlcpy(char* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
size_t destCount = 0;
|
|
|
|
while(nSourceLength-- > 0)
|
|
{
|
|
uint32_t c = (uint16_t)*pSource++; // Deal with surrogate characters
|
|
|
|
// Encode as UTF-8
|
|
if (c < 0x00000080u)
|
|
{
|
|
if(c == 0) // Break on NULL char, even if explicit length was set
|
|
break;
|
|
|
|
if(pDest && ((destCount + 1) < nDestCapacity))
|
|
*pDest++ = static_cast<char>(c);
|
|
|
|
destCount += 1;
|
|
}
|
|
else if(c < 0x00000800u)
|
|
{
|
|
if(pDest && ((destCount + 2) < nDestCapacity))
|
|
{
|
|
*pDest++ = static_cast<char>((c >> 6) | 0xc0);
|
|
*pDest++ = static_cast<char>((c | 0x80) & 0xbf);
|
|
}
|
|
|
|
destCount += 2;
|
|
}
|
|
else if(c < 0x00010000u)
|
|
{
|
|
if(pDest && ((destCount + 3) < nDestCapacity))
|
|
{
|
|
*pDest++ = static_cast<char>((c >> 12) | 0xe0);
|
|
*pDest++ = static_cast<char>(((c >> 6) | 0x80) & 0xbf);
|
|
*pDest++ = static_cast<char>((c | 0x80) & 0xbf);
|
|
}
|
|
|
|
destCount += 3;
|
|
}
|
|
else if(c < 0x00200000u)
|
|
{
|
|
if(pDest && ((destCount + 4) < nDestCapacity))
|
|
{
|
|
*pDest++ = static_cast<char>((c >> 18) | 0xf0);
|
|
*pDest++ = static_cast<char>(((c >> 12) | 0x80) & 0xbf);
|
|
*pDest++ = static_cast<char>(((c >> 6) | 0x80) & 0xbf);
|
|
*pDest++ = static_cast<char>((c | 0x80) & 0xbf);
|
|
}
|
|
|
|
destCount += 4;
|
|
}
|
|
else
|
|
{
|
|
const uint32_t kIllegalUnicodeChar = 0x0000fffd;
|
|
|
|
if(pDest && ((destCount + 3) < nDestCapacity))
|
|
{
|
|
*pDest++ = static_cast<char>( (kIllegalUnicodeChar >> 12) | 0xe0);
|
|
*pDest++ = static_cast<char>(((kIllegalUnicodeChar >> 6) | 0x80) & 0xbf);
|
|
*pDest++ = static_cast<char>(((kIllegalUnicodeChar >> 0) | 0x80) & 0xbf);
|
|
}
|
|
|
|
destCount += 3;
|
|
}
|
|
}
|
|
|
|
if(pDest && nDestCapacity != 0)
|
|
*pDest = 0;
|
|
|
|
return (int)(unsigned)destCount;
|
|
}
|
|
|
|
EASTDC_API int Strlcpy(char* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
size_t destCount = 0;
|
|
|
|
while(nSourceLength-- > 0)
|
|
{
|
|
uint32_t c = (uint32_t)*pSource++; // Deal with surrogate characters
|
|
|
|
// Encode as UTF-8
|
|
if (c < 0x00000080u)
|
|
{
|
|
if(c == 0) // Break on NULL char, even if explicit length was set
|
|
break;
|
|
|
|
if(pDest && ((destCount + 1) < nDestCapacity))
|
|
*pDest++ = static_cast<char>(c);
|
|
|
|
destCount += 1;
|
|
}
|
|
else if(c < 0x00000800u)
|
|
{
|
|
if(pDest && ((destCount + 2) < nDestCapacity))
|
|
{
|
|
*pDest++ = static_cast<char>((c >> 6) | 0xc0);
|
|
*pDest++ = static_cast<char>((c | 0x80) & 0xbf);
|
|
}
|
|
|
|
destCount += 2;
|
|
}
|
|
else if(c < 0x00010000u)
|
|
{
|
|
if(pDest && ((destCount + 3) < nDestCapacity))
|
|
{
|
|
*pDest++ = static_cast<char>((c >> 12) | 0xe0);
|
|
*pDest++ = static_cast<char>(((c >> 6) | 0x80) & 0xbf);
|
|
*pDest++ = static_cast<char>((c | 0x80) & 0xbf);
|
|
}
|
|
|
|
destCount += 3;
|
|
}
|
|
else if(c < 0x00200000u)
|
|
{
|
|
if(pDest && ((destCount + 4) < nDestCapacity))
|
|
{
|
|
*pDest++ = static_cast<char>((c >> 18) | 0xf0);
|
|
*pDest++ = static_cast<char>(((c >> 12) | 0x80) & 0xbf);
|
|
*pDest++ = static_cast<char>(((c >> 6) | 0x80) & 0xbf);
|
|
*pDest++ = static_cast<char>((c | 0x80) & 0xbf);
|
|
}
|
|
|
|
destCount += 4;
|
|
}
|
|
else
|
|
{
|
|
// We don't currently support Unicode beyond "Plane 0", as game software has never needed it.
|
|
// If you have a need for this support, feel free to submit a patch or make a specific request.
|
|
const uint32_t kIllegalUnicodeChar = 0x0000fffd;
|
|
|
|
if(pDest && ((destCount + 3) < nDestCapacity))
|
|
{
|
|
*pDest++ = static_cast<char>( (kIllegalUnicodeChar >> 12) | 0xe0);
|
|
*pDest++ = static_cast<char>(((kIllegalUnicodeChar >> 6) | 0x80) & 0xbf);
|
|
*pDest++ = static_cast<char>(((kIllegalUnicodeChar >> 0) | 0x80) & 0xbf);
|
|
}
|
|
|
|
destCount += 3;
|
|
}
|
|
}
|
|
|
|
if(pDest && nDestCapacity != 0)
|
|
*pDest = 0;
|
|
|
|
return (int)(unsigned)destCount;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API int Strlcpy(char16_t* pDest, const char* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
size_t destCount = 0;
|
|
|
|
while(nSourceLength-- > 0)
|
|
{
|
|
uint32_t c = (uint8_t)*pSource++;
|
|
|
|
if(c < 128)
|
|
{
|
|
if(c == 0) // Break on NULL char, even if explicit length was set
|
|
break;
|
|
|
|
if(pDest && ((destCount + 1) < nDestCapacity)) // +1 because we want to append c to pDest but also append '\0'.
|
|
*pDest++ = static_cast<char16_t>(c);
|
|
|
|
destCount++;
|
|
}
|
|
else
|
|
{
|
|
uint32_t nLength = utf8lengthTable[c]; // nLength may be zero, in which case we'll return 'IncorrectEncoding'.
|
|
|
|
// Do we have an incomplete or invalid string?
|
|
if((nLength > (nSourceLength + 1)) || (nLength == 0))
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("Incomplete Unicode character in buffer"); }
|
|
if(pDest && (destCount < nDestCapacity))
|
|
*pDest++ = 0; // Even though we are returning an error, 0-terminate anyway for safety.
|
|
return -1;
|
|
}
|
|
|
|
// Now decode the remaining ("following") bytes.
|
|
for(uint32_t i = 0; i < nLength - 1; ++i)
|
|
{
|
|
uint8_t nByte = (uint8_t)*pSource++;
|
|
|
|
if((nByte < 0x80u) || (nByte > 0xbfu)) // Syntax check
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("Invalid following byte"); }
|
|
if(pDest && (destCount < nDestCapacity))
|
|
*pDest++ = 0; // Even though we are returning an error, 0-terminate anyway for safety.
|
|
return -1;
|
|
}
|
|
|
|
c = (c << 6) + nByte; // Preserve control bits (don't OR)
|
|
}
|
|
|
|
nSourceLength -= (nLength - 1); // We've just processed all remaining bytes for this multi-byte character
|
|
c -= utf8DecodingOffsetTable[nLength]; // Subtract accumulated control bits just once
|
|
|
|
// Check for canonical encoding.
|
|
if((c >= utf8MinimumValueTable[nLength]) && (c < utf8MaximumValueTable[nLength]))
|
|
{
|
|
if(pDest && ((destCount + 1) < nDestCapacity))
|
|
*pDest++ = static_cast<char16_t>(c);
|
|
|
|
destCount++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(pDest && (nDestCapacity != 0))
|
|
*pDest = 0;
|
|
|
|
return (int)(unsigned)destCount;
|
|
}
|
|
|
|
EASTDC_API int Strlcpy(char32_t* pDest, const char* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
size_t destCount = 0;
|
|
|
|
while(nSourceLength-- > 0)
|
|
{
|
|
uint32_t c = (uint8_t)*pSource++;
|
|
|
|
if(c < 128)
|
|
{
|
|
if(c == 0) // Break on NULL char, even if explicit length was set
|
|
break;
|
|
|
|
if(pDest && ((destCount + 1) < nDestCapacity)) // +1 because we want to append c to pDest but also append '\0'.
|
|
*pDest++ = static_cast<char32_t>(c);
|
|
|
|
destCount++;
|
|
}
|
|
else
|
|
{
|
|
uint32_t nLength = utf8lengthTable[c]; // nLength may be zero, in which case we'll return 'IncorrectEncoding'.
|
|
|
|
// Do we have an incomplete or invalid string?
|
|
if((nLength > (nSourceLength + 1)) || (nLength == 0))
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("Incomplete Unicode character in buffer"); }
|
|
if(pDest && (destCount < nDestCapacity))
|
|
*pDest++ = 0; // Even though we are returning an error, 0-terminate anyway for safety.
|
|
return -1;
|
|
}
|
|
|
|
// Now decode the remaining ("following") bytes.
|
|
for(uint32_t i = 0; i < nLength - 1; ++i)
|
|
{
|
|
uint8_t nByte = (uint8_t)*pSource++;
|
|
|
|
if((nByte < 0x80u) || (nByte > 0xbfu)) // Syntax check
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("Invalid following byte"); }
|
|
if(pDest && (destCount < nDestCapacity))
|
|
*pDest++ = 0; // Even though we are returning an error, 0-terminate anyway for safety.
|
|
return -1;
|
|
}
|
|
|
|
c = (c << 6) + nByte; // Preserve control bits (don't OR)
|
|
}
|
|
|
|
nSourceLength -= (nLength - 1); // We've just processed all remaining bytes for this multi-byte character
|
|
c -= utf8DecodingOffsetTable[nLength]; // Subtract accumulated control bits just once
|
|
|
|
// Check for canonical encoding.
|
|
if((c >= utf8MinimumValueTable[nLength]) && (c < utf8MaximumValueTable[nLength]))
|
|
{
|
|
if(pDest && ((destCount + 1) < nDestCapacity))
|
|
*pDest++ = static_cast<char32_t>(c);
|
|
|
|
destCount++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(pDest && (nDestCapacity != 0))
|
|
*pDest = 0;
|
|
|
|
return (int)(unsigned)destCount;
|
|
}
|
|
|
|
|
|
EASTDC_API int Strlcpy(char32_t* pDest, const char16_t* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
size_t destCount = 0;
|
|
|
|
while(nSourceLength-- > 0)
|
|
{
|
|
uint32_t c = (uint32_t)*pSource++;
|
|
|
|
if(c == 0) // Break on NULL char, even if explicit length was set
|
|
break;
|
|
|
|
if(pDest && ((destCount + 1) < nDestCapacity)) // +1 because we want to append c to pDest but also append '\0'.
|
|
*pDest++ = static_cast<char32_t>(c);
|
|
|
|
destCount += 1;
|
|
}
|
|
|
|
if(pDest && nDestCapacity != 0)
|
|
*pDest = 0;
|
|
|
|
return (int)(unsigned)destCount;
|
|
}
|
|
|
|
|
|
EASTDC_API int Strlcpy(char16_t* pDest, const char32_t* pSource, size_t nDestCapacity, size_t nSourceLength)
|
|
{
|
|
size_t destCount = 0;
|
|
|
|
while(nSourceLength-- > 0)
|
|
{
|
|
uint32_t c = (uint32_t)*pSource++;
|
|
|
|
if(c == 0) // Break on NULL char, even if explicit length was set
|
|
break;
|
|
|
|
if(pDest && ((destCount + 1) < nDestCapacity)) // +1 because we want to append c to pDest but also append '\0'.
|
|
*pDest++ = static_cast<char16_t>(c);
|
|
|
|
destCount += 1;
|
|
}
|
|
|
|
if(pDest && nDestCapacity != 0)
|
|
*pDest = 0;
|
|
|
|
return (int)(unsigned)destCount;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* Strcat(char* pDestination, const char* pSource)
|
|
{
|
|
const char* s = pSource;
|
|
char* d = pDestination;
|
|
|
|
while(*d++){} // Do nothing.
|
|
--d;
|
|
while((*d++ = *s++) != 0)
|
|
{} // Do nothing.
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strcat(char16_t* pDestination, const char16_t* pSource)
|
|
{
|
|
const char16_t* s = pSource;
|
|
char16_t* d = pDestination;
|
|
|
|
while(*d++){} // Do nothing.
|
|
--d;
|
|
while((*d++ = *s++) != 0)
|
|
{} // Do nothing.
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strcat(char32_t* pDestination, const char32_t* pSource)
|
|
{
|
|
const char32_t* s = pSource;
|
|
char32_t* d = pDestination;
|
|
|
|
while(*d++){} // Do nothing.
|
|
--d;
|
|
while((*d++ = *s++) != 0)
|
|
{} // Do nothing.
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strncat(char* pDestination, const char* pSource, size_t n)
|
|
{
|
|
const char* s = pSource;
|
|
char* d = pDestination;
|
|
|
|
while(*d++){} // Do nothing.
|
|
--d;
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((*d++ = *s++) == 0)
|
|
{
|
|
--d;
|
|
break;
|
|
}
|
|
}
|
|
*d = 0;
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strncat(char16_t* pDestination, const char16_t* pSource, size_t n)
|
|
{
|
|
const char16_t* s = pSource;
|
|
char16_t* d = pDestination;
|
|
|
|
while(*d++){} // Do nothing.
|
|
--d;
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((*d++ = *s++) == 0)
|
|
{
|
|
--d;
|
|
break;
|
|
}
|
|
}
|
|
*d = 0;
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strncat(char32_t* pDestination, const char32_t* pSource, size_t n)
|
|
{
|
|
const char32_t* s = pSource;
|
|
char32_t* d = pDestination;
|
|
|
|
while(*d++){} // Do nothing.
|
|
--d;
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((*d++ = *s++) == 0)
|
|
{
|
|
--d;
|
|
break;
|
|
}
|
|
}
|
|
*d = 0;
|
|
|
|
return pDestination;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* StringnCat(char* pDestination, const char* pSource, size_t n)
|
|
{
|
|
char* const pOriginalDest = pDestination;
|
|
|
|
if(n)
|
|
{
|
|
while(*pDestination)
|
|
++pDestination;
|
|
|
|
while(n-- && *pSource)
|
|
*pDestination++ = *pSource++;
|
|
|
|
*pDestination = 0;
|
|
}
|
|
|
|
return pOriginalDest;
|
|
}
|
|
|
|
EASTDC_API char16_t* StringnCat(char16_t* pDestination, const char16_t* pSource, size_t n)
|
|
{
|
|
char16_t* const pOriginalDest = pDestination;
|
|
|
|
if(n)
|
|
{
|
|
while(*pDestination)
|
|
++pDestination;
|
|
|
|
while(n-- && *pSource)
|
|
*pDestination++ = *pSource++;
|
|
|
|
*pDestination = 0;
|
|
}
|
|
|
|
return pOriginalDest;
|
|
}
|
|
|
|
EASTDC_API char32_t* StringnCat(char32_t* pDestination, const char32_t* pSource, size_t n)
|
|
{
|
|
char32_t* const pOriginalDest = pDestination;
|
|
|
|
if(n)
|
|
{
|
|
while(*pDestination)
|
|
++pDestination;
|
|
|
|
while(n-- && *pSource)
|
|
*pDestination++ = *pSource++;
|
|
|
|
*pDestination = 0;
|
|
}
|
|
|
|
return pOriginalDest;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API size_t Strlcat(char* pDestination, const char* pSource, size_t nDestCapacity)
|
|
{
|
|
const size_t d = nDestCapacity ? Strlen(pDestination) : 0;
|
|
const size_t s = Strlen(pSource);
|
|
const size_t t = s + d;
|
|
|
|
EA_ASSERT_MSG((nDestCapacity == 0) || (d < nDestCapacity), "Destination string is longer than the specified capacity! "
|
|
"Either an out of bounds write has occurred previous to this call or the specified capacity is incorrect.");
|
|
|
|
if(t < nDestCapacity)
|
|
memcpy(pDestination + d, pSource, (s + 1) * sizeof(*pSource));
|
|
else
|
|
{
|
|
if(nDestCapacity)
|
|
{
|
|
memcpy(pDestination + d, pSource, ((nDestCapacity - d) - 1) * sizeof(*pSource));
|
|
pDestination[nDestCapacity - 1] = 0;
|
|
}
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
EASTDC_API size_t Strlcat(char16_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
|
|
{
|
|
const size_t d = nDestCapacity ? Strlen(pDestination) : 0;
|
|
const size_t s = Strlen(pSource);
|
|
const size_t t = s + d;
|
|
|
|
EA_ASSERT_MSG((nDestCapacity == 0) || (d < nDestCapacity), "Destination string is longer than the specified capacity! "
|
|
"Either an out of bounds write has occurred previous to this call or the specified capacity is incorrect.");
|
|
|
|
if(t < nDestCapacity)
|
|
memcpy(pDestination + d, pSource, (s + 1) * sizeof(*pSource));
|
|
else
|
|
{
|
|
if(nDestCapacity)
|
|
{
|
|
memcpy(pDestination + d, pSource, ((nDestCapacity - d) - 1) * sizeof(*pSource));
|
|
pDestination[nDestCapacity - 1] = 0;
|
|
}
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
EASTDC_API size_t Strlcat(char32_t* pDestination, const char32_t* pSource, size_t nDestCapacity)
|
|
{
|
|
const size_t d = nDestCapacity ? Strlen(pDestination) : 0;
|
|
const size_t s = Strlen(pSource);
|
|
const size_t t = s + d;
|
|
|
|
EA_ASSERT_MSG((nDestCapacity == 0) || (d < nDestCapacity), "Destination string is longer than the specified capacity! "
|
|
"Either an out of bounds write has occurred previous to this call or the specified capacity is incorrect.");
|
|
|
|
if(t < nDestCapacity)
|
|
memcpy(pDestination + d, pSource, (s + 1) * sizeof(*pSource));
|
|
else
|
|
{
|
|
if(nDestCapacity)
|
|
{
|
|
memcpy(pDestination + d, pSource, ((nDestCapacity - d) - 1) * sizeof(*pSource));
|
|
pDestination[nDestCapacity - 1] = 0;
|
|
}
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API size_t Strlcat(char16_t* pDestination, const char* pSource, size_t nDestCapacity)
|
|
{
|
|
size_t sourceLen = StrlenUTF8Decoded(pSource);
|
|
size_t destLen = Strlen(pDestination);
|
|
|
|
if(nDestCapacity > destLen)
|
|
Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
|
|
|
|
return sourceLen + destLen;
|
|
}
|
|
|
|
EASTDC_API size_t Strlcat(char32_t* pDestination, const char* pSource, size_t nDestCapacity)
|
|
{
|
|
size_t sourceLen = StrlenUTF8Decoded(pSource);
|
|
size_t destLen = Strlen(pDestination);
|
|
|
|
if(nDestCapacity > destLen)
|
|
Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
|
|
|
|
return sourceLen + destLen;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API size_t Strlcat(char* pDestination, const char16_t* pSource, size_t nDestCapacity)
|
|
{
|
|
size_t sourceLen = Strlen(pSource);
|
|
size_t destLen = StrlenUTF8Decoded(pDestination);
|
|
|
|
if(nDestCapacity > destLen)
|
|
Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
|
|
|
|
return sourceLen + destLen;
|
|
}
|
|
|
|
EASTDC_API size_t Strlcat(char* pDestination, const char32_t* pSource, size_t nDestCapacity)
|
|
{
|
|
size_t sourceLen = Strlen(pSource);
|
|
size_t destLen = StrlenUTF8Decoded(pDestination);
|
|
|
|
if(nDestCapacity > destLen)
|
|
Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
|
|
|
|
return sourceLen + destLen;
|
|
}
|
|
|
|
EASTDC_API size_t Strlcat(char16_t* pDestination, const char32_t* pSource, size_t nDestCapacity)
|
|
{
|
|
size_t sourceLen = Strlen(pSource);
|
|
size_t destLen = Strlen(pDestination);
|
|
|
|
if(nDestCapacity > destLen)
|
|
Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
|
|
|
|
return sourceLen + destLen;
|
|
}
|
|
|
|
EASTDC_API size_t Strlcat(char32_t* pDestination, const char16_t* pSource, size_t nDestCapacity)
|
|
{
|
|
size_t sourceLen = Strlen(pSource);
|
|
size_t destLen = Strlen(pDestination);
|
|
|
|
if(nDestCapacity > destLen)
|
|
Strlcpy(pDestination + destLen, pSource, nDestCapacity - destLen);
|
|
|
|
return sourceLen + destLen;
|
|
}
|
|
|
|
|
|
|
|
// Optimized Strlen
|
|
//
|
|
// This function assumes that we can read the last size_t-sized word at
|
|
// the end of the string, even if as many as three of the word bytes are
|
|
// beyond the end of the string. This is typically a valid assumption
|
|
// because valid memory is always aligned to big power-of-2 sizes.
|
|
//
|
|
// Tests of this Strlen show that it outperforms the basic strlen
|
|
// implementation by 2x-6x on lengths ranging from 128 bytes to 4096 bytes.
|
|
// At lengths under 10 bytes this strlen performs similarly to strlen.
|
|
// These observations apply to x86, x64 and PowerPC32 platforms.
|
|
//
|
|
// There could be faster strlen implementations with some additional
|
|
// tricks, asm, SSE, etc. But this version works well while being simple.
|
|
|
|
#if EASTDC_STATIC_ANALYSIS_ENABLED
|
|
#define EASTDC_ENABLE_OPTIMIZED_STRLEN 0 // Disabled because the optimized strlen reads words and the string may have some uninitialized chars at the end past the trailing 0 char. Valgrind reports this as an error, but it's not actually an error in practice.
|
|
#else
|
|
#define EASTDC_ENABLE_OPTIMIZED_STRLEN 1
|
|
#endif
|
|
|
|
#if EASTDC_ENABLE_OPTIMIZED_STRLEN
|
|
|
|
EASTDC_API size_t Strlen(const char* pString)
|
|
{
|
|
#if EA_COMPILER_HAS_BUILTIN(__builtin_strlen)
|
|
return __builtin_strlen(pString);
|
|
#else
|
|
// Instead of casting between types, we just create a union.
|
|
union PointerUnion
|
|
{
|
|
const char* mp8;
|
|
const word_type* mpW;
|
|
uintptr_t mU;
|
|
} pu;
|
|
|
|
// Leading unaligned bytes
|
|
for(pu.mp8 = pString; pu.mU & (sizeof(word_type) - 1); pu.mp8++)
|
|
{
|
|
if(*pu.mp8 == 0)
|
|
return (size_t)(pu.mp8 - pString);
|
|
}
|
|
|
|
for(; ; pu.mpW++)
|
|
{
|
|
#if defined(__GNUC__) && (__GNUC__ >= 3) && !defined(__EDG_VERSION__)
|
|
__builtin_prefetch(pu.mpW + 64, 0, 0);
|
|
#endif
|
|
|
|
// Quit if there are any zero chars.
|
|
const word_type kOneBytes = ((word_type)-1 / 0xff); // 0x01010101
|
|
const word_type kHighBytes = (kOneBytes * 0x80); // 0x80808080
|
|
|
|
const word_type u = *pu.mpW;
|
|
|
|
if((u - kOneBytes) & ~u & kHighBytes)
|
|
break;
|
|
}
|
|
|
|
// Trailing unaligned bytes
|
|
while(*pu.mp8)
|
|
++pu.mp8;
|
|
|
|
return (size_t)(pu.mp8 - pString);
|
|
#endif
|
|
}
|
|
|
|
#else
|
|
|
|
EASTDC_API size_t Strlen(const char* pString)
|
|
{
|
|
ssize_t nLength = (size_t)-1; // EABase 1.0.14 and later recognize ssize_t for all platforms.
|
|
|
|
do
|
|
{
|
|
++nLength;
|
|
} while (*pString++);
|
|
|
|
return (size_t)nLength;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#if EASTDC_ENABLE_OPTIMIZED_STRLEN
|
|
|
|
EASTDC_API size_t Strlen(const char16_t* pString)
|
|
{
|
|
// Instead of casting between types, we just create a union.
|
|
union PointerUnion
|
|
{
|
|
const char16_t* mp16;
|
|
const word_type* mpW;
|
|
uintptr_t mU;
|
|
} pu;
|
|
|
|
// Leading unaligned bytes
|
|
for(pu.mp16 = pString; pu.mU & (sizeof(word_type) - 1); pu.mp16++)
|
|
{
|
|
if(*pu.mp16 == 0)
|
|
return (size_t)(pu.mp16 - pString);
|
|
}
|
|
|
|
for(; ; pu.mpW++)
|
|
{
|
|
#if defined(__GNUC__) && (__GNUC__ >= 3) && !defined(__EDG_VERSION__)
|
|
__builtin_prefetch(pu.mpW + 64, 0, 0);
|
|
#endif
|
|
|
|
// Quit if there are any zero char16_ts.
|
|
const word_type kOneBytes = ((word_type)-1 / 0xffff); // 0x00010001
|
|
const word_type kHighBytes = (kOneBytes * 0x8000); // 0x80008000
|
|
|
|
const word_type u = *pu.mpW;
|
|
|
|
if((u - kOneBytes) & ~u & kHighBytes)
|
|
break;
|
|
}
|
|
|
|
// Trailing unaligned bytes
|
|
while(*pu.mp16)
|
|
++pu.mp16;
|
|
|
|
return (size_t)(pu.mp16 - pString);
|
|
}
|
|
|
|
#else
|
|
|
|
EASTDC_API size_t Strlen(const char16_t* pString)
|
|
{
|
|
size_t nLength = (size_t)-1;
|
|
|
|
do
|
|
{
|
|
++nLength;
|
|
} while (*pString++);
|
|
|
|
return nLength;
|
|
}
|
|
|
|
#endif
|
|
|
|
// To consider: This might benefit from an optimized implementation on machines withi 64 bit registers.
|
|
EASTDC_API size_t Strlen(const char32_t* pString)
|
|
{
|
|
size_t nLength = (size_t)-1;
|
|
|
|
do{
|
|
++nLength;
|
|
}while(*pString++);
|
|
|
|
return nLength;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Returns number of Unicode characters are in the UTF8-encoded string.
|
|
// Return value will be <= Strlen(pString).
|
|
EASTDC_API size_t StrlenUTF8Decoded(const char* pString)
|
|
{
|
|
size_t nLength = 0;
|
|
|
|
while(*pString)
|
|
{
|
|
if((*pString & 0xc0) != 0x80)
|
|
++nLength;
|
|
++pString;
|
|
}
|
|
|
|
return nLength;
|
|
}
|
|
|
|
|
|
// Returns number of characters that would be in a UTF8-encoded string.
|
|
// Return value will be >= Strlen(pString).
|
|
EASTDC_API size_t StrlenUTF8Encoded(const char16_t* pString)
|
|
{
|
|
size_t nLength = 0;
|
|
uint32_t c;
|
|
|
|
while((c = *pString++) != 0)
|
|
{
|
|
if(c < 0x00000080)
|
|
nLength += 1;
|
|
else if(c < 0x00000800)
|
|
nLength += 2;
|
|
else // if(c < 0x00010000)
|
|
nLength += 3;
|
|
|
|
// The following would be used if the input string was 32 bit instead of 16 bit.
|
|
//else if(c < 0x00200000)
|
|
// destCount += 4;
|
|
//else // Else we use the error char 0xfffd
|
|
// destCount += 3;
|
|
}
|
|
|
|
return nLength;
|
|
}
|
|
|
|
// Returns number of characters that would be in a UTF8-encoded string.
|
|
// Return value will be >= Strlen(pString).
|
|
EASTDC_API size_t StrlenUTF8Encoded(const char32_t* pString)
|
|
{
|
|
size_t nLength = 0;
|
|
uint32_t c;
|
|
|
|
while((c = *pString++) != 0)
|
|
{
|
|
if(c < 0x00000080)
|
|
nLength += 1;
|
|
else if(c < 0x00000800)
|
|
nLength += 2;
|
|
else // if(c < 0x00010000)
|
|
nLength += 3;
|
|
|
|
// The following would be used if the input string was 32 bit instead of 32 bit.
|
|
//else if(c < 0x00200000)
|
|
// destCount += 4;
|
|
//else // Else we use the error char 0xfffd
|
|
// destCount += 3;
|
|
}
|
|
|
|
return nLength;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* Strend(const char* pString)
|
|
{
|
|
while (*pString)
|
|
++pString;
|
|
|
|
return (char*)pString;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strend(const char16_t* pString)
|
|
{
|
|
while (*pString)
|
|
++pString;
|
|
|
|
return (char16_t*)pString;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strend(const char32_t* pString)
|
|
{
|
|
while(*pString)
|
|
++pString;
|
|
|
|
return (char32_t*)pString;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API size_t Strxfrm(char* pDest, const char* pSource, size_t n)
|
|
{
|
|
const size_t nLength = Strlen(pSource);
|
|
|
|
if(n > 0)
|
|
{
|
|
Strncpy(pDest, pSource, n - 1);
|
|
if(n < nLength)
|
|
pDest[n - 1] = 0;
|
|
}
|
|
|
|
return nLength;
|
|
}
|
|
|
|
EASTDC_API size_t Strxfrm(char16_t* pDest, const char16_t* pSource, size_t n)
|
|
{
|
|
const size_t nLength = Strlen(pSource);
|
|
|
|
if(n > 0)
|
|
{
|
|
Strncpy(pDest, pSource, n - 1);
|
|
if(n < nLength)
|
|
pDest[n - 1] = 0;
|
|
}
|
|
|
|
return nLength;
|
|
}
|
|
|
|
EASTDC_API size_t Strxfrm(char32_t* pDest, const char32_t* pSource, size_t n)
|
|
{
|
|
const size_t nLength = Strlen(pSource);
|
|
|
|
if(n > 0)
|
|
{
|
|
Strncpy(pDest, pSource, n - 1);
|
|
if(n < nLength)
|
|
pDest[n - 1] = 0;
|
|
}
|
|
|
|
return nLength;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* Strdup(const char* pString)
|
|
{
|
|
if(pString)
|
|
{
|
|
const size_t nLength = Strlen(pString);
|
|
char* const p = EASTDC_NEW(EASTDC_ALLOC_PREFIX "Strdup") char[nLength + 1]; // '+ 1' to include terminating zero.
|
|
|
|
Strcpy(p, pString);
|
|
return p;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strdup(const char16_t* pString)
|
|
{
|
|
if(pString)
|
|
{
|
|
const size_t nLength = Strlen(pString);
|
|
char16_t* const p = EASTDC_NEW(EASTDC_ALLOC_PREFIX "Strdup") char16_t[nLength + 1]; // '+ 1' to include terminating zero.
|
|
|
|
Strcpy(p, pString);
|
|
return p;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strdup(const char32_t* pString)
|
|
{
|
|
if(pString)
|
|
{
|
|
const size_t nLength = Strlen(pString);
|
|
char32_t* const p = EASTDC_NEW(EASTDC_ALLOC_PREFIX "Strdup") char32_t[nLength + 1]; // '+ 1' to include terminating zero.
|
|
|
|
Strcpy(p, pString);
|
|
return p;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API void Strdel(char* pString)
|
|
{
|
|
EASTDC_DELETE[] pString;
|
|
}
|
|
|
|
EASTDC_API void Strdel(char16_t* pString)
|
|
{
|
|
EASTDC_DELETE[] pString;
|
|
}
|
|
|
|
EASTDC_API void Strdel(char32_t* pString)
|
|
{
|
|
EASTDC_DELETE[] pString;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strupr(char* pString)
|
|
{
|
|
// This implementation converts only 7 bit ASCII characters.
|
|
// As such it is safe to use with 7-bit-safe multibyte encodings
|
|
// such as UTF8 but may yield incorrect results with such text.
|
|
char* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
{
|
|
if((uint8_t)*pStringTemp <= 127)
|
|
*pStringTemp = (char)Toupper(*pStringTemp);
|
|
++pStringTemp;
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strupr(char16_t* pString)
|
|
{
|
|
char16_t* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
{
|
|
const char16_t c = *pStringTemp;
|
|
*pStringTemp++ = Toupper(c);
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strupr(char32_t* pString)
|
|
{
|
|
char32_t* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
{
|
|
const char32_t c = *pStringTemp;
|
|
*pStringTemp++ = Toupper(c);
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strlwr(char* pString)
|
|
{
|
|
// This implementation converts only 7 bit ASCII characters.
|
|
// As such it is safe to use with 7-bit-safe multibyte encodings
|
|
// such as UTF8 but may yield incorrect results with such text.
|
|
char* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
{
|
|
if((uint8_t)*pStringTemp <= 127)
|
|
*pStringTemp = (char)Tolower(*pStringTemp);
|
|
++pStringTemp;
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strlwr(char16_t* pString)
|
|
{
|
|
char16_t* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
{
|
|
const char16_t c = *pStringTemp;
|
|
*pStringTemp++ = Tolower(c);
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strlwr(char32_t* pString)
|
|
{
|
|
char32_t* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
{
|
|
const char32_t c = *pStringTemp;
|
|
*pStringTemp++ = Tolower(c);
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strmix(char* pDestination, const char* pSource, const char* pDelimiters)
|
|
{
|
|
bool bCapitalize = true;
|
|
char* const pOriginalDest = pDestination;
|
|
|
|
while(*pSource)
|
|
{
|
|
char c = *pSource++;
|
|
|
|
// This character is upper-cased if bCapitalize flag is true, else lower-cased
|
|
if(bCapitalize)
|
|
{
|
|
if(Islower(c))
|
|
{
|
|
c = Toupper(c);
|
|
bCapitalize = false;
|
|
}
|
|
else if(Isupper(c))
|
|
bCapitalize = false;
|
|
}
|
|
else
|
|
{
|
|
if(Isupper(c))
|
|
c = Tolower(c);
|
|
}
|
|
|
|
// Check whether this character is a separator character. If so, set the bCapitalize flag.
|
|
for(const char* pCheck = pDelimiters; *pCheck; ++pCheck)
|
|
{
|
|
if(c == *pCheck)
|
|
bCapitalize = true;
|
|
}
|
|
|
|
*pDestination++ = c;
|
|
}
|
|
|
|
*pDestination = 0;
|
|
return pOriginalDest;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strmix(char16_t* pDestination, const char16_t* pSource, const char16_t* pDelimiters)
|
|
{
|
|
bool bCapitalize = true;
|
|
char16_t* const pOriginalDest = pDestination;
|
|
|
|
while(*pSource)
|
|
{
|
|
char16_t c = *pSource++;
|
|
|
|
// This character is upper-cased if bCapitalize flag is true, else lower-cased
|
|
if(bCapitalize)
|
|
{
|
|
if(Islower(c))
|
|
{
|
|
c = Toupper(c);
|
|
bCapitalize = false;
|
|
}
|
|
else if(Isupper(c))
|
|
bCapitalize = false;
|
|
}
|
|
else
|
|
{
|
|
if(Isupper(c))
|
|
c = Tolower(c);
|
|
}
|
|
|
|
// Check whether this character is a separator character. If so, set the bCapitalize flag.
|
|
for(const char16_t* pCheck = pDelimiters; *pCheck; ++pCheck)
|
|
{
|
|
if(c == *pCheck)
|
|
bCapitalize = true;
|
|
}
|
|
|
|
*pDestination++ = c;
|
|
}
|
|
|
|
*pDestination = 0;
|
|
return pOriginalDest;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strmix(char32_t* pDestination, const char32_t* pSource, const char32_t* pDelimiters)
|
|
{
|
|
bool bCapitalize = true;
|
|
char32_t* const pOriginalDest = pDestination;
|
|
|
|
while(*pSource)
|
|
{
|
|
char32_t c = *pSource++;
|
|
|
|
// This character is upper-cased if bCapitalize flag is true, else lower-cased
|
|
if(bCapitalize)
|
|
{
|
|
if(Islower(c))
|
|
{
|
|
c = Toupper(c);
|
|
bCapitalize = false;
|
|
}
|
|
else if(Isupper(c))
|
|
bCapitalize = false;
|
|
}
|
|
else
|
|
{
|
|
if(Isupper(c))
|
|
c = Tolower(c);
|
|
}
|
|
|
|
// Check whether this character is a separator character. If so, set the bCapitalize flag.
|
|
for(const char32_t* pCheck = pDelimiters; *pCheck; ++pCheck)
|
|
{
|
|
if(c == *pCheck)
|
|
bCapitalize = true;
|
|
}
|
|
|
|
*pDestination++ = c;
|
|
}
|
|
|
|
*pDestination = 0;
|
|
return pOriginalDest;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strchr(const char* pString, int c)
|
|
{
|
|
do
|
|
{
|
|
if (*pString == c)
|
|
return (char*)pString;
|
|
} while (*pString++);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strchr(const char16_t* pString, char16_t c)
|
|
{
|
|
do
|
|
{
|
|
if (*pString == c)
|
|
return (char16_t*)pString;
|
|
} while (*pString++);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strchr(const char32_t* pString, char32_t c)
|
|
{
|
|
do {
|
|
if(*pString == c)
|
|
return (char32_t*)pString;
|
|
} while (*pString++);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strnchr(const char* pString, int c, size_t n)
|
|
{
|
|
while(n-- > 0)
|
|
{
|
|
if(*pString == c)
|
|
{
|
|
return (char*)pString;
|
|
}
|
|
if(*pString == '\0')
|
|
{
|
|
return NULL;
|
|
}
|
|
pString++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strnchr(const char16_t* pString, char16_t c, size_t n)
|
|
{
|
|
while(n-- > 0)
|
|
{
|
|
if(*pString == c)
|
|
{
|
|
return (char16_t*)pString;
|
|
}
|
|
if(*pString == '\0')
|
|
{
|
|
return NULL;
|
|
}
|
|
pString++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strnchr(const char32_t* pString, char32_t c, size_t n)
|
|
{
|
|
while(n-- > 0)
|
|
{
|
|
if(*pString == c)
|
|
{
|
|
return (char32_t*)pString;
|
|
}
|
|
if(*pString == '\0')
|
|
{
|
|
return NULL;
|
|
}
|
|
pString++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API size_t Strcspn(const char* pString1, const char* pString2)
|
|
{
|
|
const char* pStringCurrent = pString1;
|
|
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2.
|
|
while(*pStringCurrent)
|
|
{
|
|
for(const char* pCharSet = pString2; *pCharSet; ++pCharSet)
|
|
{
|
|
if(*pCharSet == *pStringCurrent)
|
|
return (size_t)(pStringCurrent - pString1);
|
|
}
|
|
|
|
++pStringCurrent;
|
|
}
|
|
|
|
return (size_t)(pStringCurrent - pString1);
|
|
}
|
|
|
|
EASTDC_API size_t Strcspn(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
const char16_t* pStringCurrent = pString1;
|
|
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2. But char16_t
|
|
// means that the map would have to be 65536 bits (8192 bytes) in size.
|
|
while(*pStringCurrent)
|
|
{
|
|
for(const char16_t* pCharSet = pString2; *pCharSet; ++pCharSet)
|
|
{
|
|
if(*pCharSet == *pStringCurrent)
|
|
return (size_t)(pStringCurrent - pString1);
|
|
}
|
|
|
|
++pStringCurrent;
|
|
}
|
|
|
|
return (size_t)(pStringCurrent - pString1);
|
|
}
|
|
|
|
EASTDC_API size_t Strcspn(const char32_t* pString1, const char32_t* pString2)
|
|
{
|
|
const char32_t* pStringCurrent = pString1;
|
|
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2. But char32_t
|
|
// means that the map would have to be huge in size.
|
|
while(*pStringCurrent)
|
|
{
|
|
for(const char32_t* pCharSet = pString2; *pCharSet; ++pCharSet)
|
|
{
|
|
if(*pCharSet == *pStringCurrent)
|
|
return (size_t)(pStringCurrent - pString1);
|
|
}
|
|
|
|
++pStringCurrent;
|
|
}
|
|
|
|
return (size_t)(pStringCurrent - pString1);
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strpbrk(const char* pString1, const char* pString2)
|
|
{
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2.
|
|
while(*pString1)
|
|
{
|
|
for(const char* pCharSet = pString2; *pCharSet; ++pCharSet)
|
|
{
|
|
if(*pCharSet == *pString1)
|
|
return (char*)pString1;
|
|
}
|
|
|
|
++pString1;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strpbrk(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2. But char16_t
|
|
// means that the map would have to be 65536 bits (8192 bytes) in size.
|
|
while(*pString1)
|
|
{
|
|
for(const char16_t* pCharSet = pString2; *pCharSet; ++pCharSet)
|
|
{
|
|
if(*pCharSet == *pString1)
|
|
return (char16_t*)pString1;
|
|
}
|
|
|
|
++pString1;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strpbrk(const char32_t* pString1, const char32_t* pString2)
|
|
{
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2. But char32_t
|
|
// means that the map would have to be huge in size.
|
|
while(*pString1)
|
|
{
|
|
for(const char32_t* pCharSet = pString2; *pCharSet; ++pCharSet)
|
|
{
|
|
if(*pCharSet == *pString1)
|
|
return (char32_t*)pString1;
|
|
}
|
|
|
|
++pString1;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
EASTDC_API char* Strrchr(const char* pString, int c)
|
|
{
|
|
const char* pFound = NULL;
|
|
char cCurrent;
|
|
|
|
while ((cCurrent = *pString++) != 0)
|
|
{
|
|
if (cCurrent == c)
|
|
pFound = (pString - 1);
|
|
}
|
|
|
|
if (pFound)
|
|
return (char*)pFound;
|
|
|
|
return c ? NULL : (char*)(pString - 1);
|
|
}
|
|
|
|
EASTDC_API char16_t* Strrchr(const char16_t* pString, char16_t c)
|
|
{
|
|
const char16_t* pFound = NULL;
|
|
char16_t cCurrent;
|
|
|
|
while ((cCurrent = *pString++) != 0)
|
|
{
|
|
if (cCurrent == c)
|
|
pFound = (pString - 1);
|
|
}
|
|
|
|
if (pFound)
|
|
return (char16_t*)pFound;
|
|
|
|
return c ? NULL : (char16_t*)(pString - 1);
|
|
}
|
|
|
|
EASTDC_API char32_t* Strrchr(const char32_t* pString, char32_t c)
|
|
{
|
|
const char32_t* pFound = NULL;
|
|
char32_t cCurrent;
|
|
|
|
while ((cCurrent = *pString++) != 0)
|
|
{
|
|
if (cCurrent == c)
|
|
pFound = (pString - 1);
|
|
}
|
|
|
|
if (pFound)
|
|
return (char32_t*)pFound;
|
|
|
|
return c ? NULL : (char32_t*)(pString - 1);
|
|
}
|
|
|
|
|
|
EASTDC_API size_t Strspn(const char* pString, const char* pSubString)
|
|
{
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2.
|
|
const char* pStringCurrent = pString;
|
|
|
|
while(*pStringCurrent)
|
|
{
|
|
for(const char* pSubStringCurrent = pSubString; *pSubStringCurrent != *pStringCurrent; ++pSubStringCurrent)
|
|
{
|
|
if(*pSubStringCurrent == 0)
|
|
return (size_t)(pStringCurrent - pString);
|
|
}
|
|
|
|
++pStringCurrent;
|
|
}
|
|
|
|
return (size_t)(pStringCurrent - pString);
|
|
}
|
|
|
|
EASTDC_API size_t Strspn(const char16_t* pString, const char16_t* pSubString)
|
|
{
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2. But char16_t
|
|
// means that the map would have to be 65536 bits (8192 bytes) in size.
|
|
const char16_t* pStringCurrent = pString;
|
|
|
|
while(*pStringCurrent)
|
|
{
|
|
for(const char16_t* pSubStringCurrent = pSubString; *pSubStringCurrent != *pStringCurrent; ++pSubStringCurrent)
|
|
{
|
|
if(*pSubStringCurrent == 0)
|
|
return (size_t)(pStringCurrent - pString);
|
|
}
|
|
|
|
++pStringCurrent;
|
|
}
|
|
|
|
return (size_t)(pStringCurrent - pString);
|
|
}
|
|
|
|
EASTDC_API size_t Strspn(const char32_t* pString, const char32_t* pSubString)
|
|
{
|
|
// This implementation does a double loop. As such, it can get slow for
|
|
// very long strings. An alternative implementation is to use a hash
|
|
// table or to create a bytemap of the chars in pString2. But char32_t
|
|
// means that the map would have to be huge in size.
|
|
const char32_t* pStringCurrent = pString;
|
|
|
|
while(*pStringCurrent)
|
|
{
|
|
for(const char32_t* pSubStringCurrent = pSubString; *pSubStringCurrent != *pStringCurrent; ++pSubStringCurrent)
|
|
{
|
|
if(*pSubStringCurrent == 0)
|
|
return (size_t)(pStringCurrent - pString);
|
|
}
|
|
|
|
++pStringCurrent;
|
|
}
|
|
|
|
return (size_t)(pStringCurrent - pString);
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* Strstr(const char* pString, const char* pSubString)
|
|
{
|
|
char* s1 = (char*)pString - 1;
|
|
char* p1 = (char*)pSubString - 1;
|
|
char c0, c1, c2;
|
|
|
|
if((c0 = *++p1) == 0) // An empty pSubString results in success, return pString.
|
|
return (char*)pString;
|
|
|
|
while((c1 = *++s1) != 0)
|
|
{
|
|
if(c1 == c0)
|
|
{
|
|
const char* s2 = (s1 - 1);
|
|
const char* p2 = (p1 - 1);
|
|
|
|
while((c1 = *++s2) == (c2 = *++p2) && c1){} // Do nothing
|
|
|
|
if(!c2)
|
|
return (char*)s1;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strstr(const char16_t* pString, const char16_t* pSubString)
|
|
{
|
|
char16_t* s1 = (char16_t*)pString - 1;
|
|
char16_t* p1 = (char16_t*)pSubString - 1;
|
|
char16_t c0, c1, c2;
|
|
|
|
if((c0 = *++p1) == 0) // An empty pSubString results in success, return pString.
|
|
return (char16_t*)pString;
|
|
|
|
while((c1 = *++s1) != 0)
|
|
{
|
|
if(c1 == c0)
|
|
{
|
|
const char16_t* s2 = (s1 - 1);
|
|
const char16_t* p2 = (p1 - 1);
|
|
|
|
while((c1 = *++s2) == (c2 = *++p2) && c1){} // Do nothing
|
|
|
|
if(!c2)
|
|
return (char16_t*)s1;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strstr(const char32_t* pString, const char32_t* pSubString)
|
|
{
|
|
char32_t* s1 = (char32_t*)pString - 1;
|
|
char32_t* p1 = (char32_t*)pSubString - 1;
|
|
char32_t c0, c1, c2;
|
|
|
|
if((c0 = *++p1) == 0) // An empty pSubString results in success, return pString.
|
|
return (char32_t*)pString;
|
|
|
|
while((c1 = *++s1) != 0)
|
|
{
|
|
if(c1 == c0)
|
|
{
|
|
const char32_t* s2 = (s1 - 1);
|
|
const char32_t* p2 = (p1 - 1);
|
|
|
|
while((c1 = *++s2) == (c2 = *++p2) && c1){} // Do nothing
|
|
|
|
if(!c2)
|
|
return (char32_t*)s1;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* Stristr(const char* s1, const char* s2)
|
|
{
|
|
const char* cp = s1;
|
|
|
|
if(!*s2)
|
|
return (char*)s1;
|
|
|
|
while(*cp)
|
|
{
|
|
const char* s = cp;
|
|
const char* t = s2;
|
|
|
|
while(*s && *t && (Tolower(*s) == Tolower(*t)))
|
|
++s, ++t;
|
|
|
|
if(*t == 0)
|
|
return (char*)cp;
|
|
++cp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API char16_t* Stristr(const char16_t* s1, const char16_t* s2)
|
|
{
|
|
const char16_t* cp = s1;
|
|
|
|
if(!*s2)
|
|
return (char16_t*)s1;
|
|
|
|
while(*cp)
|
|
{
|
|
const char16_t* s = cp;
|
|
const char16_t* t = s2;
|
|
|
|
while(*s && *t && (Tolower(*s) == Tolower(*t)))
|
|
++s, ++t;
|
|
|
|
if(*t == 0)
|
|
return (char16_t*)cp;
|
|
++cp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API char32_t* Stristr(const char32_t* s1, const char32_t* s2)
|
|
{
|
|
const char32_t* cp = s1;
|
|
|
|
if(!*s2)
|
|
return (char32_t*)s1;
|
|
|
|
while(*cp)
|
|
{
|
|
const char32_t* s = cp;
|
|
const char32_t* t = s2;
|
|
|
|
while(*s && *t && (Tolower(*s) == Tolower(*t)))
|
|
++s, ++t;
|
|
|
|
if(*t == 0)
|
|
return (char32_t*)cp;
|
|
++cp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strrstr(const char* s1, const char* s2)
|
|
{
|
|
if(!*s2)
|
|
return (char*)s1;
|
|
|
|
const char* ps1 = s1 + Strlen(s1);
|
|
|
|
while(ps1 != s1)
|
|
{
|
|
const char* psc1 = --ps1;
|
|
const char* sc2 = s2;
|
|
|
|
for(;;)
|
|
{
|
|
if(*psc1++ != *sc2++)
|
|
break;
|
|
else if(!*sc2)
|
|
return (char*)ps1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strrstr(const char16_t* s1, const char16_t* s2)
|
|
{
|
|
if(!*s2)
|
|
return (char16_t*)s1;
|
|
|
|
const char16_t* ps1 = s1 + Strlen(s1);
|
|
|
|
while(ps1 != s1)
|
|
{
|
|
const char16_t* psc1 = --ps1;
|
|
const char16_t* sc2 = s2;
|
|
|
|
for(;;)
|
|
{
|
|
if(*psc1++ != *sc2++)
|
|
break;
|
|
else if(!*sc2)
|
|
return (char16_t*)ps1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strrstr(const char32_t* s1, const char32_t* s2)
|
|
{
|
|
if(!*s2)
|
|
return (char32_t*)s1;
|
|
|
|
const char32_t* ps1 = s1 + Strlen(s1);
|
|
|
|
while(ps1 != s1)
|
|
{
|
|
const char32_t* psc1 = --ps1;
|
|
const char32_t* sc2 = s2;
|
|
|
|
for(;;)
|
|
{
|
|
if(*psc1++ != *sc2++)
|
|
break;
|
|
else if(!*sc2)
|
|
return (char32_t*)ps1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* Strirstr(const char* s1, const char* s2)
|
|
{
|
|
if(!*s2)
|
|
return (char*)s1;
|
|
|
|
const char* ps1 = s1 + Strlen(s1);
|
|
|
|
while(ps1 != s1)
|
|
{
|
|
const char* psc1 = --ps1;
|
|
const char* sc2 = s2;
|
|
|
|
for(;;)
|
|
{
|
|
if(Tolower(*psc1++) != Tolower(*sc2++))
|
|
break;
|
|
else if(!*sc2)
|
|
return (char*)ps1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strirstr(const char16_t* s1, const char16_t* s2)
|
|
{
|
|
if(!*s2)
|
|
return (char16_t*)s1;
|
|
|
|
const char16_t* ps1 = s1 + Strlen(s1);
|
|
|
|
while(ps1 != s1)
|
|
{
|
|
const char16_t* psc1 = --ps1;
|
|
const char16_t* sc2 = s2;
|
|
|
|
for(;;)
|
|
{
|
|
if(Tolower(*psc1++) != Tolower(*sc2++))
|
|
break;
|
|
else if(!*sc2)
|
|
return (char16_t*)ps1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strirstr(const char32_t* s1, const char32_t* s2)
|
|
{
|
|
if(!*s2)
|
|
return (char32_t*)s1;
|
|
|
|
const char32_t* ps1 = s1 + Strlen(s1);
|
|
|
|
while(ps1 != s1)
|
|
{
|
|
const char32_t* psc1 = --ps1;
|
|
const char32_t* sc2 = s2;
|
|
|
|
for(;;)
|
|
{
|
|
if(Tolower(*psc1++) != Tolower(*sc2++))
|
|
break;
|
|
else if(!*sc2)
|
|
return (char32_t*)ps1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API bool Strstart(const char* pString, const char* pPrefix)
|
|
{
|
|
while(*pPrefix)
|
|
{
|
|
if(*pString++ != *pPrefix++)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Strstart(const char16_t* pString, const char16_t* pPrefix)
|
|
{
|
|
while(*pPrefix)
|
|
{
|
|
if(*pString++ != *pPrefix++)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Strstart(const char32_t* pString, const char32_t* pPrefix)
|
|
{
|
|
while(*pPrefix)
|
|
{
|
|
if(*pString++ != *pPrefix++)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API bool Stristart(const char* pString, const char* pPrefix)
|
|
{
|
|
while(*pPrefix)
|
|
{
|
|
if(Tolower(*pString++) != Tolower(*pPrefix++))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Stristart(const char16_t* pString, const char16_t* pPrefix)
|
|
{
|
|
while(*pPrefix)
|
|
{
|
|
if(Tolower(*pString++) != Tolower(*pPrefix++))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Stristart(const char32_t* pString, const char32_t* pPrefix)
|
|
{
|
|
while(*pPrefix)
|
|
{
|
|
if(Tolower(*pString++) != Tolower(*pPrefix++))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API bool Strend(const char* pString, const char* pSuffix, size_t stringLength, size_t suffixLength)
|
|
{
|
|
if(stringLength == kSizeTypeUnset)
|
|
stringLength = Strlen(pString);
|
|
|
|
if(suffixLength == kSizeTypeUnset)
|
|
suffixLength = Strlen(pSuffix);
|
|
|
|
if(stringLength >= suffixLength)
|
|
return Memcmp(pString + stringLength - suffixLength, pSuffix, suffixLength * sizeof(char)) == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Strend(const char16_t* pString, const char16_t* pSuffix, size_t stringLength, size_t suffixLength)
|
|
{
|
|
if(stringLength == kSizeTypeUnset)
|
|
stringLength = Strlen(pString);
|
|
|
|
if(suffixLength == kSizeTypeUnset)
|
|
suffixLength = Strlen(pSuffix);
|
|
|
|
if(stringLength >= suffixLength)
|
|
return Memcmp(pString + stringLength - suffixLength, pSuffix, suffixLength * sizeof(char16_t)) == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Strend(const char32_t* pString, const char32_t* pSuffix, size_t stringLength, size_t suffixLength)
|
|
{
|
|
if(stringLength == kSizeTypeUnset)
|
|
stringLength = Strlen(pString);
|
|
|
|
if(suffixLength == kSizeTypeUnset)
|
|
suffixLength = Strlen(pSuffix);
|
|
|
|
if(stringLength >= suffixLength)
|
|
return Memcmp(pString + stringLength - suffixLength, pSuffix, suffixLength * sizeof(char32_t)) == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Striend(const char* pString, const char* pSuffix, size_t stringLength, size_t suffixLength)
|
|
{
|
|
if(stringLength == kSizeTypeUnset)
|
|
stringLength = Strlen(pString);
|
|
|
|
if(suffixLength == kSizeTypeUnset)
|
|
suffixLength = Strlen(pSuffix);
|
|
|
|
if(stringLength >= suffixLength)
|
|
return Stricmp(pString + stringLength - suffixLength, pSuffix) == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Striend(const char16_t* pString, const char16_t* pSuffix, size_t stringLength, size_t suffixLength)
|
|
{
|
|
if(stringLength == kSizeTypeUnset)
|
|
stringLength = Strlen(pString);
|
|
|
|
if(suffixLength == kSizeTypeUnset)
|
|
suffixLength = Strlen(pSuffix);
|
|
|
|
if(stringLength >= suffixLength)
|
|
return Stricmp(pString + stringLength - suffixLength, pSuffix) == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
EASTDC_API bool Striend(const char32_t* pString, const char32_t* pSuffix, size_t stringLength, size_t suffixLength)
|
|
{
|
|
if(stringLength == kSizeTypeUnset)
|
|
stringLength = Strlen(pString);
|
|
|
|
if(suffixLength == kSizeTypeUnset)
|
|
suffixLength = Strlen(pSuffix);
|
|
|
|
if(stringLength >= suffixLength)
|
|
return Stricmp(pString + stringLength - suffixLength, pSuffix) == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// This function was implemented by Avery Lee.
|
|
//
|
|
EASTDC_API char* Strtok(char* pString, const char* pDelimiters, char** pContext)
|
|
{
|
|
// find point on string to resume
|
|
char* s = pString;
|
|
|
|
if(!s)
|
|
{
|
|
s = *pContext;
|
|
if(!s)
|
|
return NULL;
|
|
}
|
|
|
|
// Compute bit hash based on lower 5 bits of delimiter characters
|
|
const char* d = pDelimiters;
|
|
int32_t hash = 0;
|
|
uint32_t delimiterCount = 0;
|
|
|
|
while(const char c = *d++)
|
|
{
|
|
hash |= (int32_t)(0x80000000 >> (c & 31));
|
|
++delimiterCount;
|
|
}
|
|
|
|
// Skip delimiters
|
|
for(;;)
|
|
{
|
|
const char c = *s;
|
|
|
|
// If we hit the end of the string, it ends solely with delimiters
|
|
// and there are no more tokens to get.
|
|
if(!c)
|
|
{
|
|
*pContext = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
// Fast rejection against hash set
|
|
if(int32_t(uint64_t(hash) << (c & 31)) >= 0)
|
|
break;
|
|
|
|
// brute-force search against delimiter list
|
|
for(uint32_t i=0; i<delimiterCount; ++i)
|
|
{
|
|
if (pDelimiters[i] == c) // Is it a delimiter? ...
|
|
goto still_delimiters; // yes, continue the loop
|
|
}
|
|
|
|
// Not a token, so exit
|
|
break;
|
|
|
|
still_delimiters:
|
|
++s;
|
|
}
|
|
|
|
// Mark beginning of token
|
|
char* const pToken = s;
|
|
|
|
// Search for end of token
|
|
while(const char c = *s)
|
|
{
|
|
// Fast rejection against hash set
|
|
if(int32_t(int64_t(hash) << (c & 31)) < 0)
|
|
{
|
|
// Brute-force search against delimiter list
|
|
for(uint32_t i=0; i<delimiterCount; ++i)
|
|
{
|
|
if(pDelimiters[i] == c)
|
|
{
|
|
// This token ends with a delimiter.
|
|
*s = 0; // null-term substring
|
|
*pContext = (s + 1); // restart on next byte
|
|
return pToken; // return found token
|
|
}
|
|
}
|
|
}
|
|
|
|
++s;
|
|
}
|
|
|
|
// We found a token but it was at the end of the string,
|
|
// so we null out the context and return the last token.
|
|
*pContext = NULL; // no more tokens
|
|
return pToken; // return found token
|
|
}
|
|
|
|
EASTDC_API char16_t* Strtok(char16_t* pString, const char16_t* pDelimiters, char16_t** pContext)
|
|
{
|
|
// Find point on string to resume
|
|
char16_t* s = pString;
|
|
|
|
if(!s)
|
|
{
|
|
s = *pContext;
|
|
if(!s)
|
|
return NULL;
|
|
}
|
|
|
|
// compute bit hash based on lower 5 bits of delimiter characters
|
|
const char16_t* d = pDelimiters;
|
|
int32_t hash = 0;
|
|
uint32_t delimiterCount = 0;
|
|
|
|
while(const char16_t c = *d++)
|
|
{
|
|
hash |= (int32_t)(0x80000000 >> (c & 31));
|
|
++delimiterCount;
|
|
}
|
|
|
|
// Skip delimiters
|
|
for(;;)
|
|
{
|
|
const char16_t c = *s;
|
|
|
|
// If we hit the end of the string, it ends solely with delimiters
|
|
// and there are no more tokens to get.
|
|
if(!c)
|
|
{
|
|
*pContext = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
// Fast rejection against hash set
|
|
if(int32_t(int64_t(hash) << (c & 31)) >= 0)
|
|
break;
|
|
|
|
// Brute-force search against delimiter list
|
|
for(uint32_t i=0; i<delimiterCount; ++i)
|
|
{
|
|
if(pDelimiters[i] == (char16_t)c) // Is it a delimiter? ...
|
|
goto still_delimiters; // yes, continue the loop
|
|
}
|
|
|
|
// Not a token, so exit
|
|
break;
|
|
|
|
still_delimiters:
|
|
++s;
|
|
}
|
|
|
|
// Mark beginning of token
|
|
char16_t* const pToken = s;
|
|
|
|
// Search for end of token
|
|
while(const char16_t c = *s)
|
|
{
|
|
// Fast rejection against hash set
|
|
if(int32_t(int64_t(hash) << (c & 31)) < 0)
|
|
{
|
|
// Brute-force search against delimiter list
|
|
for(uint32_t i=0; i<delimiterCount; ++i)
|
|
{
|
|
if(pDelimiters[i] == c)
|
|
{
|
|
// This token ends with a delimiter.
|
|
*s = 0; // null-term substring
|
|
*pContext = (s + 1); // restart on next byte
|
|
return pToken; // return found token
|
|
}
|
|
}
|
|
}
|
|
|
|
++s;
|
|
}
|
|
|
|
// We found a token but it was at the end of the string,
|
|
// so we null out the context and return the last token.
|
|
*pContext = NULL; // no more tokens
|
|
return pToken; // return found token
|
|
}
|
|
|
|
EASTDC_API char32_t* Strtok(char32_t* pString, const char32_t* pDelimiters, char32_t** pContext)
|
|
{
|
|
// Find point on string to resume
|
|
char32_t* s = pString;
|
|
|
|
if(!s)
|
|
{
|
|
s = *pContext;
|
|
if(!s)
|
|
return NULL;
|
|
}
|
|
|
|
// compute bit hash based on lower 5 bits of delimiter characters
|
|
const char32_t* d = pDelimiters;
|
|
int32_t hash = 0;
|
|
uint32_t delimiterCount = 0;
|
|
|
|
while(const uint32_t c = (uint32_t)*d++)
|
|
{
|
|
hash |= (int32_t)(0x80000000 >> (c & 31));
|
|
++delimiterCount;
|
|
}
|
|
|
|
// Skip delimiters
|
|
for(;;)
|
|
{
|
|
const char32_t c = *s;
|
|
|
|
// If we hit the end of the string, it ends solely with delimiters
|
|
// and there are no more tokens to get.
|
|
if(!c)
|
|
{
|
|
*pContext = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
// Fast rejection against hash set
|
|
if(int32_t(int64_t(hash) << (c & 31)) >= 0)
|
|
break;
|
|
|
|
// Brute-force search against delimiter list
|
|
for(uint32_t i=0; i<delimiterCount; ++i)
|
|
{
|
|
if(pDelimiters[i] == c) // Is it a delimiter? ...
|
|
goto still_delimiters; // yes, continue the loop
|
|
}
|
|
|
|
// Not a token, so exit
|
|
break;
|
|
|
|
still_delimiters:
|
|
++s;
|
|
}
|
|
|
|
// Mark beginning of token
|
|
char32_t* const pToken = s;
|
|
|
|
// Search for end of token
|
|
while(const uint32_t c = (uint32_t)*s)
|
|
{
|
|
// Fast rejection against hash set
|
|
if(int32_t(int64_t(hash) << (c & 31)) < 0)
|
|
{
|
|
// Brute-force search against delimiter list
|
|
for(uint32_t i=0; i<delimiterCount; ++i)
|
|
{
|
|
if(pDelimiters[i] == (char32_t)c)
|
|
{
|
|
// This token ends with a delimiter.
|
|
*s = 0; // null-term substring
|
|
*pContext = (s + 1); // restart on next byte
|
|
return pToken; // return found token
|
|
}
|
|
}
|
|
}
|
|
|
|
++s;
|
|
}
|
|
|
|
// We found a token but it was at the end of the string,
|
|
// so we null out the context and return the last token.
|
|
*pContext = NULL; // no more tokens
|
|
return pToken; // return found token
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API const char* Strtok2(const char* pString, const char* pDelimiters,
|
|
size_t* pResultLength, bool bFirst)
|
|
{
|
|
// Skip any non-delimiters
|
|
if(!bFirst)
|
|
{
|
|
while(*pString && !Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
}
|
|
|
|
// Skip any delimiters
|
|
while(*pString && Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
|
|
const char* const pBegin = pString;
|
|
|
|
// Calculate the length of the string
|
|
while(*pString && !Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
|
|
if(pBegin != pString)
|
|
{
|
|
*pResultLength = static_cast<size_t>(pString - pBegin);
|
|
return pBegin;
|
|
}
|
|
|
|
*pResultLength = 0;
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API const char16_t* Strtok2(const char16_t* pString, const char16_t* pDelimiters, size_t* pResultLength, bool bFirst)
|
|
{
|
|
// Skip any non-delimiters
|
|
if(!bFirst)
|
|
{
|
|
while(*pString && !Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
}
|
|
|
|
// Skip any delimiters
|
|
while(*pString && Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
|
|
const char16_t* const pBegin = pString;
|
|
|
|
// Calculate the length of the string
|
|
while(*pString && !Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
|
|
if(pBegin != pString)
|
|
{
|
|
*pResultLength = static_cast<size_t>(pString - pBegin);
|
|
return pBegin;
|
|
}
|
|
|
|
*pResultLength = 0;
|
|
return NULL;
|
|
}
|
|
|
|
EASTDC_API const char32_t* Strtok2(const char32_t* pString, const char32_t* pDelimiters, size_t* pResultLength, bool bFirst)
|
|
{
|
|
// Skip any non-delimiters
|
|
if(!bFirst)
|
|
{
|
|
while(*pString && !Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
}
|
|
|
|
// Skip any delimiters
|
|
while(*pString && Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
|
|
const char32_t* const pBegin = pString;
|
|
|
|
// Calculate the length of the string
|
|
while(*pString && !Strchr(pDelimiters, *pString))
|
|
++pString;
|
|
|
|
if(pBegin != pString)
|
|
{
|
|
*pResultLength = static_cast<size_t>(pString - pBegin);
|
|
return pBegin;
|
|
}
|
|
|
|
*pResultLength = 0;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* Strset(char* pString, int c)
|
|
{
|
|
char* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
*pStringTemp++ = (char)c;
|
|
|
|
return pString;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strset(char16_t* pString, char16_t c)
|
|
{
|
|
char16_t* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
*pStringTemp++ = c;
|
|
|
|
return pString;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strset(char32_t* pString, char32_t c)
|
|
{
|
|
char32_t* pStringTemp = pString;
|
|
|
|
while(*pStringTemp)
|
|
*pStringTemp++ = c;
|
|
|
|
return pString;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* Strnset(char* pString, int c, size_t n)
|
|
{
|
|
char* pSaved = pString;
|
|
|
|
for(size_t i = 0; *pString && (i < n); ++i)
|
|
*pString++ = (char)c;
|
|
|
|
return pSaved;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strnset(char16_t* pString, char16_t c, size_t n)
|
|
{
|
|
char16_t* pSaved = pString;
|
|
|
|
for(size_t i = 0; *pString && (i < n); ++i)
|
|
*pString++ = c;
|
|
|
|
return pSaved;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strnset(char32_t* pString, char32_t c, size_t n)
|
|
{
|
|
char32_t* pSaved = pString;
|
|
|
|
for(size_t i = 0; *pString && (i < n); ++i)
|
|
*pString++ = c;
|
|
|
|
return pSaved;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* Strrev(char* pString)
|
|
{
|
|
for(char* p1 = pString, *p2 = (pString + Strlen(pString)) - 1; p1 < p2; ++p1, --p2)
|
|
{
|
|
char c = *p2;
|
|
*p2 = *p1;
|
|
*p1 = c;
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
EASTDC_API char16_t* Strrev(char16_t* pString)
|
|
{
|
|
for(char16_t* p1 = pString, *p2 = (pString + Strlen(pString)) - 1; p1 < p2; ++p1, --p2)
|
|
{
|
|
char16_t c = *p2;
|
|
*p2 = *p1;
|
|
*p1 = c;
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
EASTDC_API char32_t* Strrev(char32_t* pString)
|
|
{
|
|
for(char32_t* p1 = pString, *p2 = (pString + Strlen(pString)) - 1; p1 < p2; ++p1, --p2)
|
|
{
|
|
char32_t c = *p2;
|
|
*p2 = *p1;
|
|
*p1 = c;
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
|
|
EASTDC_API char* Strstrip(char* pString)
|
|
{
|
|
// Walk forward from the beginning and find the first non-whitespace.
|
|
while(EA::StdC::Isspace(*pString)) // Isspace returns false for *pString == '\0'.
|
|
++pString;
|
|
|
|
if(*pString)
|
|
{
|
|
// Walk backward from the end and find the last whitespace.
|
|
size_t length = EA::StdC::Strlen(pString);
|
|
char* pEnd = (pString + length) - 1;
|
|
|
|
while((pEnd > pString) && EA::StdC::Isspace(*pEnd))
|
|
pEnd--;
|
|
|
|
pEnd[1] = '\0';
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
|
|
EASTDC_API char16_t* Strstrip(char16_t* pString)
|
|
{
|
|
// Walk forward from the beginning and find the first non-whitespace.
|
|
while(EA::StdC::Isspace(*pString)) // Isspace returns false for *pString == '\0'.
|
|
++pString;
|
|
|
|
if(*pString)
|
|
{
|
|
// Walk backward from the end and find the last whitespace.
|
|
size_t length = EA::StdC::Strlen(pString);
|
|
char16_t* pEnd = (pString + length) - 1;
|
|
|
|
while((pEnd > pString) && EA::StdC::Isspace(*pEnd))
|
|
pEnd--;
|
|
|
|
pEnd[1] = '\0';
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
|
|
EASTDC_API char32_t* Strstrip(char32_t* pString)
|
|
{
|
|
// Walk forward from the beginning and find the first non-whitespace.
|
|
while(EA::StdC::Isspace(*pString)) // Isspace returns false for *pString == '\0'.
|
|
++pString;
|
|
|
|
if(*pString)
|
|
{
|
|
// Walk backward from the end and find the last whitespace.
|
|
size_t length = EA::StdC::Strlen(pString);
|
|
char32_t* pEnd = (pString + length) - 1;
|
|
|
|
while((pEnd > pString) && EA::StdC::Isspace(*pEnd))
|
|
pEnd--;
|
|
|
|
pEnd[1] = '\0';
|
|
}
|
|
|
|
return pString;
|
|
}
|
|
|
|
|
|
|
|
// Optimized Strcmp
|
|
//
|
|
// This function assumes that we can read the last size_t-sized word at
|
|
// the end of the string, even if as many as three of the word bytes are
|
|
// beyond the end of the string. This is typically a valid assumption
|
|
// because valid memory is always aligned to big power-of-2 sizes.
|
|
//
|
|
// There could be faster strcmp implementations with some additional
|
|
// tricks, asm, SSE, etc. But this version works well while being simple.
|
|
// To do: Implement a vector-specific version for at least x64-based platforms.
|
|
|
|
#if EASTDC_STATIC_ANALYSIS_ENABLED
|
|
#define EASTDC_ENABLE_OPTIMIZED_STRCMP 0 // Disabled because the optimized strcmp reads words and the string may have some uninitialized chars at the
|
|
#else // end past the trailing 0 char. Valgrind reports this as an error, but it's not actually an error in practice.
|
|
#define EASTDC_ENABLE_OPTIMIZED_STRCMP 1
|
|
#endif
|
|
|
|
#if EASTDC_ENABLE_OPTIMIZED_STRCMP
|
|
#if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_OSX)
|
|
// Some platforms have an optimized vector implementation of strcmp which is fast and which provides
|
|
// identical return value behavior to our Strcmp (which is to return the byte difference and not just
|
|
// -1, 0, +1). And so until we have our own vectored version we use the built-in version.
|
|
EASTDC_API int Strcmp(const char* pString1, const char* pString2)
|
|
{
|
|
return strcmp(pString1, pString2);
|
|
}
|
|
#else
|
|
// To do: Implement an x86/x64 vectorized version of Strcmp, which can work on 16 byte chunks and thus be faster than below.
|
|
|
|
EASTDC_API int Strcmp(const char* pString1, const char* pString2)
|
|
{
|
|
if(IsAligned<const char, sizeof(word_type)>(pString1) && // If pString1 and pString2 are word-aligned... compare in word-sized chunks.
|
|
IsAligned<const char, sizeof(word_type)>(pString2))
|
|
{
|
|
const word_type* pWord1 = (word_type*)pString1;
|
|
const word_type* pWord2 = (word_type*)pString2;
|
|
|
|
while(*pWord1 == *pWord2)
|
|
{
|
|
if(ZeroPresent8(*pWord1++))
|
|
return 0;
|
|
++pWord2;
|
|
}
|
|
|
|
// At this point, the strings differ somewhere in the bytes pointed to by pWord1/pWord2.
|
|
pString1 = reinterpret_cast<const char*>(pWord1); // Fall through and do byte comparisons for the rest of the string.
|
|
pString2 = reinterpret_cast<const char*>(pWord2);
|
|
}
|
|
|
|
while(*pString1 && (*pString1 == *pString2))
|
|
{
|
|
++pString1;
|
|
++pString2;
|
|
}
|
|
|
|
return ((uint8_t)*pString1 - (uint8_t)*pString2);
|
|
}
|
|
#endif
|
|
#else
|
|
EASTDC_API int Strcmp(const char* pString1, const char* pString2)
|
|
{
|
|
char c1, c2;
|
|
|
|
while((c1 = *pString1++) == (c2 = *pString2++))
|
|
{
|
|
if(c1 == 0)
|
|
return 0;
|
|
}
|
|
|
|
return ((uint8_t)c1 - (uint8_t)c2);
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EASTDC_ENABLE_OPTIMIZED_STRCMP
|
|
// To do: Implement an x86/x64 vectorized version of Strcmp, which can work on 16 byte chunks and thus be faster than below.
|
|
|
|
EASTDC_API int Strcmp(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
if(IsAligned<const char16_t, sizeof(word_type)>(pString1) && // If pString1 and pString2 are word-aligned... compare in word-sized chunks.
|
|
IsAligned<const char16_t, sizeof(word_type)>(pString2))
|
|
{
|
|
const word_type* pWord1 = (word_type*)pString1;
|
|
const word_type* pWord2 = (word_type*)pString2;
|
|
|
|
while(*pWord1 == *pWord2)
|
|
{
|
|
if(ZeroPresent16(*pWord1++))
|
|
return 0;
|
|
++pWord2;
|
|
}
|
|
|
|
// At this point, the strings differ somewhere in the bytes pointed to by pWord1/pWord2.
|
|
pString1 = reinterpret_cast<const char16_t*>(pWord1); // Fall through and do byte comparisons for the rest of the string.
|
|
pString2 = reinterpret_cast<const char16_t*>(pWord2);
|
|
}
|
|
|
|
while(*pString1 && (*pString1 == *pString2))
|
|
{
|
|
++pString1;
|
|
++pString2;
|
|
}
|
|
|
|
return ((uint16_t)*pString1 - (uint16_t)*pString2);
|
|
}
|
|
#else
|
|
EASTDC_API int Strcmp(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
char16_t c1, c2;
|
|
|
|
while((c1 = *pString1++) == (c2 = *pString2++))
|
|
{
|
|
if(c1 == 0) // If we've reached the end of the string with no difference...
|
|
return 0;
|
|
}
|
|
|
|
EA_COMPILETIME_ASSERT(sizeof(int) > sizeof(uint16_t));
|
|
return ((uint16_t)c1 - (uint16_t)c2);
|
|
}
|
|
#endif
|
|
|
|
|
|
EASTDC_API int Strcmp(const char32_t* pString1, const char32_t* pString2)
|
|
{
|
|
char32_t c1, c2;
|
|
|
|
while((c1 = *pString1++) == (c2 = *pString2++))
|
|
{
|
|
if(c1 == 0) // If we've reached the end of the string with no difference...
|
|
return 0;
|
|
}
|
|
|
|
// We can't just return c1 - c2, because the difference might be greater than INT_MAX.
|
|
return ((uint32_t)c1 > (uint32_t)c2) ? 1 : -1;
|
|
}
|
|
|
|
|
|
|
|
#if EASTDC_ENABLE_OPTIMIZED_STRCMP
|
|
// Some platforms have an optimized vector implementation of strncmp which is fast and which provides
|
|
// identical return value behavior to our Strncmp (which is to return the byte difference and not just
|
|
// -1, 0, +1). And so until we have our own vectored version we use the built-in version.
|
|
EASTDC_API int Strncmp(const char* pString1, const char* pString2, size_t n)
|
|
{
|
|
return strncmp(pString1, pString2, n);
|
|
}
|
|
|
|
// To do: Implement a general portable version of a more optimized Strncmp.
|
|
|
|
#else
|
|
EASTDC_API int Strncmp(const char* pString1, const char* pString2, size_t n)
|
|
{
|
|
char c1, c2;
|
|
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((c1 = *pString1++) != (c2 = *pString2++))
|
|
return ((uint8_t)c1 - (uint8_t)c2);
|
|
else if(c1 == 0)
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
EASTDC_API int Strncmp(const char16_t* pString1, const char16_t* pString2, size_t n)
|
|
{
|
|
char16_t c1, c2;
|
|
|
|
// Code below which uses (c1 - c2) assumes this.
|
|
EA_COMPILETIME_ASSERT(sizeof(int) > sizeof(uint16_t));
|
|
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((c1 = *pString1++) != (c2 = *pString2++))
|
|
return ((uint16_t)c1 - (uint16_t)c2);
|
|
else if(c1 == 0)
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API int Strncmp(const char32_t* pString1, const char32_t* pString2, size_t n)
|
|
{
|
|
char32_t c1, c2;
|
|
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((c1 = *pString1++) != (c2 = *pString2++))
|
|
{
|
|
// We can't just return c1 - c2, because the difference might be greater than INT_MAX.
|
|
return ((uint32_t)c1 > (uint32_t)c2) ? 1 : -1;
|
|
}
|
|
else if(c1 == 0)
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#if EASTDC_ENABLE_OPTIMIZED_STRCMP && (defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_OSX))
|
|
// Some platforms have an optimized vector implementation of stricmp/strcasecmp which is fast and which provides
|
|
// identical return value behavior to our Stricmp (which is to return the byte difference and not just
|
|
// -1, 0, +1). And so until we have our own vectored version we use the built-in version.
|
|
EASTDC_API int Stricmp(const char* pString1, const char* pString2)
|
|
{
|
|
return strcasecmp(pString1, pString2);
|
|
}
|
|
|
|
// To do: Implement a general portable version of a more optimized Stricmp.
|
|
|
|
#else
|
|
EASTDC_API int Stricmp(const char* pString1, const char* pString2)
|
|
{
|
|
char c1, c2;
|
|
|
|
while((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++)))
|
|
{
|
|
if(c1 == 0)
|
|
return 0;
|
|
}
|
|
|
|
return ((uint8_t)c1 - (uint8_t)c2);
|
|
}
|
|
#endif
|
|
|
|
|
|
EASTDC_API int Stricmp(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
char16_t c1, c2;
|
|
|
|
while((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++)))
|
|
{
|
|
if(c1 == 0)
|
|
return 0;
|
|
}
|
|
|
|
// Code below which uses (c1 - c2) assumes this.
|
|
EA_COMPILETIME_ASSERT(sizeof(int) > sizeof(uint16_t));
|
|
return ((uint16_t)c1 - (uint16_t)c2);
|
|
}
|
|
|
|
EASTDC_API int Stricmp(const char32_t* pString1, const char32_t* pString2)
|
|
{
|
|
char32_t c1, c2;
|
|
|
|
while((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++)))
|
|
{
|
|
if(c1 == 0)
|
|
return 0;
|
|
}
|
|
|
|
// We can't just return c1 - c2, because the difference might be greater than INT_MAX.
|
|
return ((uint32_t)c1 > (uint32_t)c2) ? 1 : -1;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API int Strnicmp(const char* pString1, const char* pString2, size_t n)
|
|
{
|
|
char c1, c2;
|
|
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((c1 = Tolower(*pString1++)) != (c2 = Tolower(*pString2++)))
|
|
return ((uint8_t)c1 - (uint8_t)c2);
|
|
else if(c1 == 0)
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API int Strnicmp(const char16_t* pString1, const char16_t* pString2, size_t n)
|
|
{
|
|
char16_t c1, c2;
|
|
|
|
// Code below which uses (c1 - c2) assumes this.
|
|
EA_COMPILETIME_ASSERT(sizeof(int) > sizeof(uint16_t));
|
|
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((c1 = Tolower(*pString1++)) != (c2 = Tolower(*pString2++)))
|
|
return ((uint16_t)c1 - (uint16_t)c2);
|
|
else if(c1 == 0)
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API int Strnicmp(const char32_t* pString1, const char32_t* pString2, size_t n)
|
|
{
|
|
char32_t c1, c2;
|
|
|
|
++n;
|
|
while(--n)
|
|
{
|
|
if((c1 = Tolower(*pString1++)) != (c2 = Tolower(*pString2++)))
|
|
{
|
|
// We can't just return c1 - c2, because the difference might be greater than INT_MAX.
|
|
return ((uint32_t)c1 > (uint32_t)c2) ? 1 : -1;
|
|
}
|
|
else if(c1 == 0)
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
// *** this function is deprecated. ***
|
|
EASTDC_API int StrcmpAlnum(const char* pString1, const char* pString2)
|
|
{
|
|
char c1, c2;
|
|
const char* pStart1 = pString1;
|
|
const char* pStart2 = pString2;
|
|
const char* pDigitStart1 = pString1;
|
|
|
|
while(((c1 = *pString1++) == (c2 = *pString2++)) && c1)
|
|
{
|
|
if(!Isdigit(c1))
|
|
pDigitStart1 = pString1;
|
|
}
|
|
|
|
const int c1d = Isdigit(c1);
|
|
const int c2d = Isdigit(c2);
|
|
|
|
if(c1d && c2d)
|
|
return (int)StrtoI32(pDigitStart1, NULL, 10) - (int)StrtoI32(pStart2 + (pDigitStart1 - pStart1), NULL, 10);
|
|
|
|
if(c1d != c2d) // If one char is decimal and the other is not..
|
|
return c1d ? 1 : -1;
|
|
|
|
return ((uint8_t)c1 - (uint8_t)c2);
|
|
}
|
|
|
|
|
|
// *** this function is deprecated. ***
|
|
EASTDC_API int StrcmpAlnum(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
char16_t c1, c2;
|
|
const char16_t* pStart1 = pString1;
|
|
const char16_t* pStart2 = pString2;
|
|
const char16_t* pDigitStart1 = pString1;
|
|
|
|
while(((c1 = *pString1++) == (c2 = *pString2++)) && c1)
|
|
{
|
|
if(!Isdigit(c1))
|
|
pDigitStart1 = pString1;
|
|
}
|
|
|
|
const int c1d = Isdigit(c1);
|
|
const int c2d = Isdigit(c2);
|
|
|
|
if(c1d && c2d)
|
|
return (int)StrtoI32(pDigitStart1, NULL, 10) - (int)StrtoI32(pStart2 + (pDigitStart1 - pStart1), NULL, 10);
|
|
|
|
if(c1d != c2d) // If one char is decimal and the other is not..
|
|
return c1d ? 1 : -1;
|
|
|
|
return ((uint16_t)c1 - (uint16_t)c2);
|
|
}
|
|
|
|
|
|
// *** this function is deprecated. ***
|
|
EASTDC_API int StricmpAlnum(const char* pString1, const char* pString2)
|
|
{
|
|
char c1, c2;
|
|
const char* pStart1 = pString1;
|
|
const char* pStart2 = pString2;
|
|
const char* pDigitStart1 = pString1;
|
|
|
|
while(((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++))) && c1)
|
|
{
|
|
if(!Isdigit(c1))
|
|
pDigitStart1 = pString1;
|
|
}
|
|
|
|
const int c1d = Isdigit(c1);
|
|
const int c2d = Isdigit(c2);
|
|
|
|
if(c1d && c2d)
|
|
return (int)StrtoI32(pDigitStart1, NULL, 10) - (int)StrtoI32(pStart2 + (pDigitStart1 - pStart1), NULL, 10);
|
|
|
|
if(c1d != c2d) // If one char is decimal and the other is not..
|
|
return c1d ? 1 : -1;
|
|
|
|
return ((uint8_t)c1 - (uint8_t)c2);
|
|
}
|
|
|
|
|
|
// *** this function is deprecated. ***
|
|
EASTDC_API int StricmpAlnum(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
char16_t c1, c2;
|
|
const char16_t* pStart1 = pString1;
|
|
const char16_t* pStart2 = pString2;
|
|
const char16_t* pDigitStart1 = pString1;
|
|
|
|
while(((c1 = Tolower(*pString1++)) == (c2 = Tolower(*pString2++))) && c1)
|
|
{
|
|
if(!Isdigit(c1))
|
|
pDigitStart1 = pString1;
|
|
}
|
|
|
|
const int c1d = Isdigit(c1);
|
|
const int c2d = Isdigit(c2);
|
|
|
|
if(c1d && c2d)
|
|
return (int)StrtoI32(pDigitStart1, NULL, 10) - (int)StrtoI32(pStart2 + (pDigitStart1 - pStart1), NULL, 10);
|
|
|
|
if(c1d != c2d) // If one char is decimal and the other is not..
|
|
return c1d ? 1 : -1;
|
|
|
|
return ((uint16_t)c1 - (uint16_t)c2);
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API int StrcmpNumeric(const char* pString1, const char* pString2,
|
|
size_t length1, size_t length2,
|
|
char decimal, char thousandsSeparator)
|
|
{
|
|
// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
|
|
EA_UNUSED(pString1);
|
|
EA_UNUSED(pString2);
|
|
EA_UNUSED(length1);
|
|
EA_UNUSED(length2);
|
|
EA_UNUSED(decimal);
|
|
EA_UNUSED(thousandsSeparator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API int StrcmpNumeric(const char16_t* pString1, const char16_t* pString2,
|
|
size_t length1, size_t length2,
|
|
char16_t decimal, char16_t thousandsSeparator)
|
|
{
|
|
// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
|
|
EA_UNUSED(pString1);
|
|
EA_UNUSED(pString2);
|
|
EA_UNUSED(length1);
|
|
EA_UNUSED(length2);
|
|
EA_UNUSED(decimal);
|
|
EA_UNUSED(thousandsSeparator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API int StrcmpNumeric(const char32_t* pString1, const char32_t* pString2,
|
|
size_t length1, size_t length2,
|
|
char32_t decimal, char32_t thousandsSeparator)
|
|
{
|
|
// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
|
|
EA_UNUSED(pString1);
|
|
EA_UNUSED(pString2);
|
|
EA_UNUSED(length1);
|
|
EA_UNUSED(length2);
|
|
EA_UNUSED(decimal);
|
|
EA_UNUSED(thousandsSeparator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API int StricmpNumeric(const char* pString1, const char* pString2,
|
|
size_t length1, size_t length2,
|
|
char decimal, char thousandsSeparator)
|
|
{
|
|
// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
|
|
EA_UNUSED(pString1);
|
|
EA_UNUSED(pString2);
|
|
EA_UNUSED(length1);
|
|
EA_UNUSED(length2);
|
|
EA_UNUSED(decimal);
|
|
EA_UNUSED(thousandsSeparator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API int StricmpNumeric(const char16_t* pString1, const char16_t* pString2,
|
|
size_t length1, size_t length2,
|
|
char16_t decimal, char16_t thousandsSeparator)
|
|
{
|
|
// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
|
|
EA_UNUSED(pString1);
|
|
EA_UNUSED(pString2);
|
|
EA_UNUSED(length1);
|
|
EA_UNUSED(length2);
|
|
EA_UNUSED(decimal);
|
|
EA_UNUSED(thousandsSeparator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EASTDC_API int StricmpNumeric(const char32_t* pString1, const char32_t* pString2,
|
|
size_t length1, size_t length2,
|
|
char32_t decimal, char32_t thousandsSeparator)
|
|
{
|
|
// To do: Implement this function. Ask Paul Pedriana to implement this if you need it.
|
|
EA_UNUSED(pString1);
|
|
EA_UNUSED(pString2);
|
|
EA_UNUSED(length1);
|
|
EA_UNUSED(length2);
|
|
EA_UNUSED(decimal);
|
|
EA_UNUSED(thousandsSeparator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EASTDC_API int Strcoll(const char* pString1, const char* pString2)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strcmp(pString1, pString2);
|
|
}
|
|
|
|
EASTDC_API int Strcoll(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strcmp(pString1, pString2);
|
|
}
|
|
|
|
EASTDC_API int Strcoll(const char32_t* pString1, const char32_t* pString2)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strcmp(pString1, pString2);
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API int Strncoll(const char* pString1, const char* pString2, size_t n)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strncmp(pString1, pString2, n);
|
|
}
|
|
|
|
EASTDC_API int Strncoll(const char16_t* pString1, const char16_t* pString2, size_t n)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strncmp(pString1, pString2, n);
|
|
}
|
|
|
|
EASTDC_API int Strncoll(const char32_t* pString1, const char32_t* pString2, size_t n)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strncmp(pString1, pString2, n);
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API int Stricoll(const char* pString1, const char* pString2)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Stricmp(pString1, pString2);
|
|
}
|
|
|
|
EASTDC_API int Stricoll(const char16_t* pString1, const char16_t* pString2)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Stricmp(pString1, pString2);
|
|
}
|
|
|
|
EASTDC_API int Stricoll(const char32_t* pString1, const char32_t* pString2)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Stricmp(pString1, pString2);
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API int Strnicoll(const char* pString1, const char* pString2, size_t n)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strnicmp(pString1, pString2, n);
|
|
}
|
|
|
|
EASTDC_API int Strnicoll(const char16_t* pString1, const char16_t* pString2, size_t n)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strnicmp(pString1, pString2, n);
|
|
}
|
|
|
|
EASTDC_API int Strnicoll(const char32_t* pString1, const char32_t* pString2, size_t n)
|
|
{
|
|
// The user needs to use a localization package to get proper localized collation.
|
|
return Strnicmp(pString1, pString2, n);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EcvtBuf / FcvtBuf
|
|
//
|
|
#if EASTDC_NATIVE_FCVT
|
|
EASTDC_API char* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char* buffer)
|
|
{
|
|
#ifdef __GNUC__
|
|
const char* const pResult = ecvt(dValue, nDigitCount, decimalPos, sign);
|
|
#else
|
|
const char* const pResult = _ecvt(dValue, nDigitCount, decimalPos, sign);
|
|
#endif
|
|
|
|
strcpy(buffer, pResult);
|
|
|
|
#if EASTDC_NATIVE_FCVT_SHORT
|
|
// For ecvt, nDigitCount is the resulting length of the buffer of digits, regardless of the decimal point location.
|
|
if(nDigitCount > 15) // The '> 15' part is a quick check to avoid the rest of the code for most cases.
|
|
{
|
|
int len = (int)strlen(buffer);
|
|
|
|
while(len < nDigitCount)
|
|
buffer[len++] = '0';
|
|
buffer[len] = 0;
|
|
}
|
|
#endif
|
|
|
|
return buffer;
|
|
}
|
|
|
|
EASTDC_API char* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char* buffer)
|
|
{
|
|
#ifdef __GNUC__
|
|
const char* const pResult = fcvt(dValue, nDigitCountAfterDecimal, decimalPos, sign);
|
|
#else
|
|
char pResult[_CVTBUFSIZE+1];
|
|
_fcvt_s(pResult, sizeof(pResult), dValue, nDigitCountAfterDecimal, decimalPos, sign);
|
|
#endif
|
|
|
|
strcpy(buffer, pResult);
|
|
|
|
#if EASTDC_NATIVE_FCVT_SHORT
|
|
// For fcvt, nDigitCount is the resulting length of the buffer of digits after the decimal point location.
|
|
nDigitCountAfterDecimal += *decimalPos;
|
|
|
|
if(nDigitCountAfterDecimal > 15) // The '> 15' part is a quick check to avoid the rest of the code for most cases.
|
|
{
|
|
int len = (int)strlen(buffer);
|
|
|
|
while(len < nDigitCountAfterDecimal)
|
|
buffer[len++] = '0';
|
|
buffer[len] = 0;
|
|
}
|
|
#endif
|
|
|
|
return buffer;
|
|
}
|
|
|
|
#else
|
|
|
|
#if defined(EA_COMPILER_MSVC)
|
|
#include <float.h>
|
|
#define isnan(x) _isnan(x)
|
|
//#define isinf(x) !_finite(x)
|
|
#endif
|
|
|
|
#if !defined(isnan)
|
|
|
|
inline bool isnan(double fValue)
|
|
{
|
|
const union {
|
|
double f;
|
|
int64_t i;
|
|
} converter = { fValue };
|
|
|
|
// An IEEE real value is a NaN if all exponent bits are one and
|
|
// the mantissa is not zero.
|
|
return (converter.i & ~kFloat64SignMask) > kFloat64ExponentMask;
|
|
}
|
|
#endif
|
|
|
|
union DoubleShape
|
|
{
|
|
double mValue;
|
|
uint32_t mUint64;
|
|
|
|
#if defined(EA_SYSTEM_LITTLE_ENDIAN)
|
|
struct numberStruct
|
|
{
|
|
unsigned int fraction1 : 32;
|
|
unsigned int fraction0 : 20;
|
|
unsigned int exponent : 11;
|
|
unsigned int sign : 1;
|
|
} mNumber;
|
|
#else
|
|
struct numberStruct
|
|
{
|
|
unsigned int sign : 1;
|
|
unsigned int exponent : 11;
|
|
unsigned int fraction0 : 20;
|
|
unsigned int fraction1 : 32;
|
|
} mNumber;
|
|
#endif
|
|
};
|
|
|
|
union FloatShape
|
|
{
|
|
float mValue;
|
|
uint32_t mUint32;
|
|
|
|
#if defined(EA_SYSTEM_LITTLE_ENDIAN)
|
|
struct numberStruct
|
|
{
|
|
unsigned int fraction : 23;
|
|
unsigned int exponent : 8;
|
|
unsigned int sign : 1;
|
|
} mNumber;
|
|
#else
|
|
struct numberStruct
|
|
{
|
|
unsigned int sign : 1;
|
|
unsigned int exponent : 8;
|
|
unsigned int fraction : 23;
|
|
} mNumber;
|
|
#endif
|
|
};
|
|
|
|
|
|
EASTDC_API char* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char* buffer)
|
|
{
|
|
int nDigitCountAfterDecimal;
|
|
double fract;
|
|
double integer;
|
|
double tmp;
|
|
int neg = 0;
|
|
int expcnt = 0;
|
|
char* buf = buffer;
|
|
char* t = buf;
|
|
char* p = buf + kEcvtBufMaxSize - 1;
|
|
char* pbuf = p;
|
|
|
|
// We follow the same preconditions as Microsoft does with its _ecvt function.
|
|
EA_ASSERT((nDigitCount >= 0) && (decimalPos != NULL) && (sign != NULL) && (buffer != NULL));
|
|
|
|
// assume decimal to left of digits in string
|
|
*decimalPos = 0;
|
|
|
|
// To consider: Enable the following.
|
|
//if(nDigitCount > 16) // It turns out that we can't get any more precision than this.
|
|
// nDigitCount = 16; // Any digits beyond 16 would be nearly meaningless.
|
|
|
|
if(sizeof(double) == sizeof(float)) // If the user has the compiler set to use doubles that are smaller...
|
|
{
|
|
FloatShape floatShape;
|
|
floatShape.mValue = (float)dValue; // This should be a lossless conversion.
|
|
|
|
if(floatShape.mNumber.exponent == 0xff) // If not finite...
|
|
{
|
|
if(floatShape.mUint32 & 0x007fffff) // If is a NAN...
|
|
{
|
|
*t++ = 'N';
|
|
*t++ = 'A';
|
|
*t++ = 'N';
|
|
}
|
|
else
|
|
{
|
|
*t++ = 'I';
|
|
*t++ = 'N';
|
|
*t++ = 'F';
|
|
}
|
|
*t = 0;
|
|
return buffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DoubleShape doubleShape;
|
|
doubleShape.mValue = dValue;
|
|
|
|
if(doubleShape.mNumber.exponent == 0x7ff) // If not finite...
|
|
{
|
|
if(isnan(dValue)) // If is a NAN...
|
|
{
|
|
*t++ = 'N';
|
|
*t++ = 'A';
|
|
*t++ = 'N';
|
|
}
|
|
else
|
|
{
|
|
*t++ = 'I';
|
|
*t++ = 'N';
|
|
*t++ = 'F';
|
|
}
|
|
*t = 0;
|
|
return buffer;
|
|
}
|
|
}
|
|
|
|
if(dValue < 0)
|
|
{
|
|
neg = 1;
|
|
dValue = -dValue;
|
|
}
|
|
|
|
fract = modf(dValue, &integer);
|
|
|
|
if(dValue >= 1.0f)
|
|
{
|
|
for(; integer; ++expcnt)
|
|
{
|
|
tmp = modf(integer / 10.0f, &integer);
|
|
*p-- = (char)((int)((tmp + 0.01f) * 10.0f) + '0');
|
|
EA_ASSERT(p >= buffer);
|
|
}
|
|
}
|
|
|
|
*t++ = 0; // Extra slot for rounding
|
|
buf += 1; // Point return value to beginning of string.
|
|
|
|
int tempExp = expcnt;
|
|
nDigitCountAfterDecimal = nDigitCount - expcnt;
|
|
|
|
if(expcnt)
|
|
{
|
|
//if expcnt > nDigitCount, need to round the integer part, and reset expcnt
|
|
if(expcnt > nDigitCount)
|
|
{
|
|
pbuf = p + nDigitCount + 1;
|
|
|
|
if(*pbuf >= '5')
|
|
{
|
|
do
|
|
{
|
|
pbuf--;
|
|
if(++*pbuf <= '9')
|
|
break;
|
|
*pbuf = '0';
|
|
}
|
|
while(pbuf >= p+1);
|
|
}
|
|
|
|
expcnt = nDigitCount;
|
|
fract = 0.0;//no more rounding will be needed down below!
|
|
}
|
|
|
|
for(++p; expcnt--;)
|
|
*t++ = *p++;
|
|
}
|
|
|
|
if(nDigitCountAfterDecimal >= 0)
|
|
{
|
|
// Per spec, don't actually put decimal in string, just let caller know where it should be...
|
|
*decimalPos = (int)(ptrdiff_t)(t - buf); // Count of chars into string when to place decimal point
|
|
}
|
|
else
|
|
*decimalPos = (int)tempExp;
|
|
|
|
bool leading = dValue < 1.0f ? true : false;//for Ecvt, leading zeros need to be omitted and decimalPos needs to be readjusted
|
|
while((nDigitCountAfterDecimal > 0) && fract)
|
|
{
|
|
fract = modf(fract * 10.0f, &tmp);
|
|
|
|
if(leading && (int)tmp == 0)
|
|
{
|
|
(*decimalPos)--;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
leading = false;
|
|
*t++ = (char)((int)tmp + '0');
|
|
nDigitCountAfterDecimal -= 1;
|
|
}
|
|
}
|
|
|
|
if(fract)
|
|
{
|
|
char* scan = (t - 1);
|
|
|
|
// round off the number
|
|
modf(fract * 10.0f, &tmp);
|
|
|
|
if(tmp > 4)
|
|
{
|
|
for(; ; --scan)
|
|
{
|
|
if(*scan == '.')
|
|
scan -= 1;
|
|
if(++*scan <= '9')
|
|
break;
|
|
*scan = '0';
|
|
if(scan == buf)
|
|
{
|
|
*--scan = '1';
|
|
buf -= 1; // Rounded into holding spot
|
|
++*decimalPos; // This line added by Paul Pedriana, May 8 2008, in order to fix a bug where ("%.1f", 0.952) gave "0.1" instead of "1.0". I need to investigate this more to verify the fix.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if(neg)
|
|
{
|
|
// fix ("%.3f", -0.0004) giving -0.000
|
|
for( ; ; scan -= 1)
|
|
{
|
|
if(scan <= buf)
|
|
break;
|
|
if(*scan == '.')
|
|
scan -= 1;
|
|
if(*scan != '0')
|
|
break;
|
|
if(scan == buf)
|
|
neg = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(nDigitCountAfterDecimal<0)//this means the digitcount is smaller than integre part and need to round the integer part
|
|
nDigitCountAfterDecimal = 0;
|
|
|
|
while(nDigitCountAfterDecimal--)
|
|
*t++ = '0';
|
|
*t++ = 0; // Always terminate the string of digits
|
|
|
|
if(*buffer == 0) // If the above rounding place wasn't necessary...
|
|
memmove(buffer, buffer + 1, (size_t)(t - (buffer + 1)));
|
|
|
|
*sign = neg ? 1 : 0;
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
EASTDC_API char* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char* buffer)
|
|
{
|
|
double fract;
|
|
double integer;
|
|
double tmp;
|
|
int neg = 0;
|
|
int expcnt = 0;
|
|
char* buf = buffer;
|
|
char* t = buf;
|
|
char* p = buf + kFcvtBufMaxSize - 1;
|
|
|
|
// We follow the same preconditions as Microsoft does with its _fcvt function.
|
|
EA_ASSERT((nDigitCountAfterDecimal >= 0) && (decimalPos != NULL) && (sign != NULL) && (buffer != NULL));
|
|
|
|
// assume decimal to left of digits in string
|
|
*decimalPos = 0;
|
|
|
|
if(sizeof(double) == sizeof(float)) // If the user has the compiler set to use doubles that are smaller...
|
|
{
|
|
FloatShape floatShape;
|
|
floatShape.mValue = (float)dValue; // This should be a lossless conversion.
|
|
|
|
if(floatShape.mNumber.exponent == 0xff) // If not finite...
|
|
{
|
|
if(floatShape.mUint32 & 0x007fffff) // If is a NAN...
|
|
{
|
|
*t++ = 'N';
|
|
*t++ = 'A';
|
|
*t++ = 'N';
|
|
}
|
|
else
|
|
{
|
|
*t++ = 'I';
|
|
*t++ = 'N';
|
|
*t++ = 'F';
|
|
}
|
|
*t = 0;
|
|
return buffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DoubleShape doubleShape;
|
|
doubleShape.mValue = dValue;
|
|
|
|
if(doubleShape.mNumber.exponent == 0x7ff) // If not finite...
|
|
{
|
|
if(isnan(dValue)) // If is a NAN...
|
|
{
|
|
*t++ = 'N';
|
|
*t++ = 'A';
|
|
*t++ = 'N';
|
|
}
|
|
else
|
|
{
|
|
*t++ = 'I';
|
|
*t++ = 'N';
|
|
*t++ = 'F';
|
|
}
|
|
*t = 0;
|
|
return buffer;
|
|
}
|
|
}
|
|
|
|
if(dValue < 0)
|
|
{
|
|
neg = 1;
|
|
dValue = -dValue;
|
|
}
|
|
|
|
fract = modf(dValue, &integer);
|
|
|
|
if(dValue >= 1.0f)
|
|
{
|
|
for(; integer; ++expcnt)
|
|
{
|
|
tmp = modf(integer / 10.0f, &integer);
|
|
*p-- = (char)((int)((tmp + 0.01f) * 10.0f) + '0');
|
|
EA_ASSERT(p >= buffer);
|
|
}
|
|
}
|
|
|
|
*t++ = 0; // Extra slot for rounding
|
|
buf += 1; // Point return value to beginning of string.
|
|
|
|
if(expcnt)
|
|
{
|
|
for(++p; expcnt--;)
|
|
*t++ = *p++;
|
|
}
|
|
|
|
// Per spec, don't actually put decimal in string, just let caller know where it should be...
|
|
*decimalPos = (int)(ptrdiff_t)(t - buf); // Count of chars into string when to place decimal point.
|
|
|
|
// We give up trying to calculate fractions beyond 16 digits, which is the maximum possible precision with a double.
|
|
int count = (nDigitCountAfterDecimal <= 16) ? nDigitCountAfterDecimal : 16;
|
|
while(count && fract)
|
|
{
|
|
fract = modf(fract * 10.0f, &tmp);
|
|
*t++ = (char)((int)tmp + '0');
|
|
nDigitCountAfterDecimal--;
|
|
count--;
|
|
}
|
|
|
|
if(fract)
|
|
{
|
|
char* scan = (t - 1);
|
|
|
|
// round off the number
|
|
modf(fract * 10.0f, &tmp);
|
|
|
|
if(tmp > 4)
|
|
{
|
|
for(; ; --scan)
|
|
{
|
|
if(*scan == '.')
|
|
scan -= 1;
|
|
if(++*scan <= '9')
|
|
break;
|
|
*scan = '0';
|
|
if(scan == buf)
|
|
{
|
|
*--scan = '1';
|
|
buf -= 1; // Rounded into holding spot
|
|
++*decimalPos; // This line added by Paul Pedriana, May 8 2008, in order to fix a bug where ("%.1f", 0.952) gave "0.1" instead of "1.0". I need to investigate this more to verify the fix.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if(neg)
|
|
{
|
|
// fix ("%.3f", -0.0004) giving -0.000
|
|
for( ; ; --scan)
|
|
{
|
|
if(scan <= buf)
|
|
break;
|
|
if(*scan == '.')
|
|
scan -= 1;
|
|
if(*scan != '0')
|
|
break;
|
|
if(scan == buf)
|
|
neg = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
while(nDigitCountAfterDecimal--)
|
|
*t++ = '0';
|
|
*t++ = 0; // Always terminate the string of digits
|
|
|
|
if(*buffer == 0) // If the above rounding place wasn't necessary...
|
|
memmove(buffer, buffer + 1, (size_t)(t - (buffer + 1)));
|
|
|
|
*sign = neg ? 1 : 0;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
// Matching #undef for each #define above for unity build friendliness.
|
|
#if defined(EA_COMPILER_MSVC)
|
|
#undef isnan
|
|
//#undef isinf
|
|
#endif
|
|
|
|
#endif // Compiler support
|
|
|
|
|
|
|
|
EASTDC_API char16_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char16_t* buffer)
|
|
{
|
|
// We implement this by calling the 8 bit version and copying its data.
|
|
char pBufferCvt8[kEcvtBufMaxSize];
|
|
char16_t* pCurrent16 = buffer;
|
|
|
|
EcvtBuf(dValue, nDigitCount, decimalPos, sign, pBufferCvt8);
|
|
|
|
for(char* pCurrent8 = pBufferCvt8; *pCurrent8; ) // Do a 8 bit to 16 bit strcpy.
|
|
*pCurrent16++ = (char16_t)(unsigned char)*pCurrent8++;
|
|
|
|
*pCurrent16 = 0;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
EASTDC_API char32_t* EcvtBuf(double dValue, int nDigitCount, int* decimalPos, int* sign, char32_t* buffer)
|
|
{
|
|
// We implement this by calling the 8 bit version and copying its data.
|
|
char pBufferCvt8[kEcvtBufMaxSize];
|
|
char32_t* pCurrent32 = buffer;
|
|
|
|
EcvtBuf(dValue, nDigitCount, decimalPos, sign, pBufferCvt8);
|
|
|
|
for(char* pCurrent8 = pBufferCvt8; *pCurrent8; ) // Do a 8 bit to 32 bit strcpy.
|
|
*pCurrent32++ = (char32_t)(unsigned char)*pCurrent8++;
|
|
|
|
*pCurrent32 = 0;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char16_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char16_t* buffer)
|
|
{
|
|
// We implement this by calling the 8 bit version and copying its data.
|
|
char pBufferCvt8[kEcvtBufMaxSize];
|
|
char16_t* pCurrent16 = buffer;
|
|
|
|
FcvtBuf(dValue, nDigitCountAfterDecimal, decimalPos, sign, pBufferCvt8);
|
|
|
|
for(char* pCurrent8 = pBufferCvt8; *pCurrent8; ) // Do a 8 bit to 16 bit strcpy.
|
|
*pCurrent16++ = (char16_t)(unsigned char)*pCurrent8++;
|
|
|
|
*pCurrent16 = 0;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
EASTDC_API char32_t* FcvtBuf(double dValue, int nDigitCountAfterDecimal, int* decimalPos, int* sign, char32_t* buffer)
|
|
{
|
|
// We implement this by calling the 8 bit version and copying its data.
|
|
char pBufferCvt8[kEcvtBufMaxSize];
|
|
char32_t* pCurrent32 = buffer;
|
|
|
|
FcvtBuf(dValue, nDigitCountAfterDecimal, decimalPos, sign, pBufferCvt8);
|
|
|
|
for(char* pCurrent8 = pBufferCvt8; *pCurrent8; ) // Do a 8 bit to 32 bit strcpy.
|
|
*pCurrent32++ = (char32_t)(unsigned char)*pCurrent8++;
|
|
|
|
*pCurrent32 = 0;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
// end of EcvtBuf / FcvtBuf
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Optimization technique:
|
|
// https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920
|
|
// This results in performance improvements of 2x to 5x depending on the input value. Our general test in
|
|
// TestString.cpp showed a 3.1x performance gain on VC++/x64.
|
|
//
|
|
static uint32_t digits10(uint64_t v)
|
|
{
|
|
if(v < 10)
|
|
return 1;
|
|
if(v < 100)
|
|
return 2;
|
|
if(v < 1000)
|
|
return 3;
|
|
if(v < UINT64_C(1000000000000))
|
|
{
|
|
if(v < UINT64_C(100000000))
|
|
{
|
|
if(v < 1000000)
|
|
{
|
|
if (v < 10000)
|
|
return 4;
|
|
return (uint32_t)(5 + (v >= 100000));
|
|
}
|
|
|
|
return (uint32_t)(7 + (v >= 10000000));
|
|
}
|
|
|
|
if(v < UINT64_C(10000000000))
|
|
return (uint32_t)(9 + (v >= UINT64_C(1000000000)));
|
|
|
|
return (uint32_t)(11 + (v >= UINT64_C(100000000000)));
|
|
}
|
|
|
|
return 12 + digits10(v / UINT64_C(1000000000000));
|
|
}
|
|
|
|
char* X64toaCommon10(uint64_t nValue, char* pBuffer)
|
|
{
|
|
static const char digits[201] =
|
|
"0001020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849"
|
|
"5051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
|
|
|
|
uint32_t length = digits10(nValue);
|
|
uint32_t next = length - 1;
|
|
|
|
pBuffer[length] = '\0';
|
|
|
|
while(nValue >= 100)
|
|
{
|
|
const uint64_t i = (nValue % 100) * 2;
|
|
nValue /= 100;
|
|
pBuffer[next] = digits[i + 1];
|
|
pBuffer[next - 1] = digits[i];
|
|
next -= 2;
|
|
}
|
|
|
|
if (nValue < 10)
|
|
pBuffer[next] = (char)('0' + (uint32_t)nValue);
|
|
else
|
|
{
|
|
const uint32_t i = (uint32_t)nValue * 2;
|
|
pBuffer[next] = digits[i + 1];
|
|
pBuffer[next - 1] = digits[i];
|
|
}
|
|
|
|
return pBuffer;
|
|
}
|
|
|
|
|
|
static char* X64toaCommon(uint64_t nValue, char* pBuffer, int nBase, bool bNegative)
|
|
{
|
|
char* pCurrent = pBuffer;
|
|
|
|
if(bNegative)
|
|
*pCurrent++ = '-';
|
|
|
|
if(nBase == 10)
|
|
X64toaCommon10(nValue, pCurrent);
|
|
else
|
|
{
|
|
char* pFirstDigit = pCurrent;
|
|
|
|
do{
|
|
const unsigned nDigit = (unsigned)(nValue % nBase);
|
|
nValue /= nBase;
|
|
|
|
if(nDigit > 9)
|
|
*pCurrent++ = (char)(nDigit - 10 + 'a');
|
|
else
|
|
*pCurrent++ = (char)(nDigit + '0');
|
|
} while(nValue > 0);
|
|
|
|
// Need to reverse the string.
|
|
*pCurrent-- = 0;
|
|
|
|
do{
|
|
const char cTemp = *pCurrent;
|
|
*pCurrent-- = *pFirstDigit;
|
|
*pFirstDigit++ = cTemp;
|
|
}while(pFirstDigit < pCurrent);
|
|
}
|
|
|
|
return pBuffer;
|
|
}
|
|
|
|
|
|
static char16_t* X64toaCommon(uint64_t nValue, char16_t* pBuffer, int nBase, bool bNegative)
|
|
{
|
|
char16_t* pCurrent = pBuffer;
|
|
|
|
if(bNegative)
|
|
*pCurrent++ = '-';
|
|
|
|
char16_t* pFirstDigit = pCurrent;
|
|
|
|
do{
|
|
const unsigned nDigit = (unsigned)(nValue % nBase);
|
|
nValue /= nBase;
|
|
|
|
if(nDigit > 9)
|
|
*pCurrent++ = (char16_t)(nDigit - 10 + 'a');
|
|
else
|
|
*pCurrent++ = (char16_t)(nDigit + '0');
|
|
} while(nValue > 0);
|
|
|
|
// Need to reverse the string.
|
|
*pCurrent-- = 0;
|
|
|
|
do{
|
|
const char16_t cTemp = *pCurrent;
|
|
*pCurrent-- = *pFirstDigit;
|
|
*pFirstDigit++ = cTemp;
|
|
}while(pFirstDigit < pCurrent);
|
|
|
|
return pBuffer;
|
|
}
|
|
|
|
static char32_t* X64toaCommon(uint64_t nValue, char32_t* pBuffer, int nBase, bool bNegative)
|
|
{
|
|
char32_t* pCurrent = pBuffer;
|
|
|
|
if(bNegative)
|
|
*pCurrent++ = '-';
|
|
|
|
char32_t* pFirstDigit = pCurrent;
|
|
|
|
do{
|
|
const unsigned nDigit = (unsigned)(nValue % nBase);
|
|
nValue /= nBase;
|
|
|
|
if(nDigit > 9)
|
|
*pCurrent++ = (char32_t)(nDigit - 10 + 'a');
|
|
else
|
|
*pCurrent++ = (char32_t)(nDigit + '0');
|
|
} while(nValue > 0);
|
|
|
|
// Need to reverse the string.
|
|
*pCurrent-- = 0;
|
|
|
|
do{
|
|
const char32_t cTemp = *pCurrent;
|
|
*pCurrent-- = *pFirstDigit;
|
|
*pFirstDigit++ = cTemp;
|
|
}while(pFirstDigit < pCurrent);
|
|
|
|
return pBuffer;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* I32toa(int32_t nValue, char* pBuffer, int nBase)
|
|
{
|
|
const bool bNegative = (nValue < 0) && (nBase == 10);
|
|
|
|
if(bNegative)
|
|
{
|
|
#if defined(__GNUC__) // -INT32_MIN => INT32_MIN, but with GCC on Android it's acting differently.
|
|
if(nValue != INT32_MIN)
|
|
#endif
|
|
nValue = -nValue;
|
|
}
|
|
|
|
return X64toaCommon((uint64_t)(uint32_t)nValue, pBuffer, nBase, bNegative);
|
|
}
|
|
|
|
EASTDC_API char16_t* I32toa(int32_t nValue, char16_t* pBuffer, int nBase)
|
|
{
|
|
const bool bNegative = (nValue < 0) && (nBase == 10);
|
|
|
|
if(bNegative)
|
|
{
|
|
#if defined(__GNUC__) // -INT32_MIN => INT32_MIN, but with GCC on Android it's acting differently.
|
|
if(nValue != INT32_MIN)
|
|
#endif
|
|
nValue = -nValue;
|
|
}
|
|
|
|
return X64toaCommon((uint64_t)(uint32_t)nValue, pBuffer, nBase, bNegative);
|
|
}
|
|
|
|
EASTDC_API char32_t* I32toa(int32_t nValue, char32_t* pBuffer, int nBase)
|
|
{
|
|
const bool bNegative = (nValue < 0) && (nBase == 10);
|
|
|
|
if(bNegative)
|
|
{
|
|
#if defined(__GNUC__) // -INT32_MIN => INT32_MIN, but with GCC on Android it's acting differently.
|
|
if(nValue != INT32_MIN)
|
|
#endif
|
|
nValue = -nValue;
|
|
}
|
|
|
|
return X64toaCommon((uint64_t)(uint32_t)nValue, pBuffer, nBase, bNegative);
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* U32toa(uint32_t nValue, char* pBuffer, int nBase)
|
|
{
|
|
return X64toaCommon((uint64_t)nValue, pBuffer, nBase, 0);
|
|
}
|
|
|
|
EASTDC_API char16_t* U32toa(uint32_t nValue, char16_t* pBuffer, int nBase)
|
|
{
|
|
return X64toaCommon((uint64_t)nValue, pBuffer, nBase, 0);
|
|
}
|
|
|
|
EASTDC_API char32_t* U32toa(uint32_t nValue, char32_t* pBuffer, int nBase)
|
|
{
|
|
return X64toaCommon((uint64_t)nValue, pBuffer, nBase, 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* I64toa(int64_t nValue, char* pBuffer, int nBase)
|
|
{
|
|
const bool bNegative = (nValue < 0) && (nBase == 10);
|
|
|
|
if(bNegative)
|
|
nValue = -(uint64_t)nValue;
|
|
|
|
return X64toaCommon((uint64_t)nValue, pBuffer, nBase, bNegative);
|
|
}
|
|
|
|
EASTDC_API char16_t* I64toa(int64_t nValue, char16_t* pBuffer, int nBase)
|
|
{
|
|
const bool bNegative = (nValue < 0) && (nBase == 10);
|
|
|
|
if(bNegative)
|
|
nValue = -(uint64_t)nValue;
|
|
|
|
return X64toaCommon((uint64_t)nValue, pBuffer, nBase, bNegative);
|
|
}
|
|
|
|
EASTDC_API char32_t* I64toa(int64_t nValue, char32_t* pBuffer, int nBase)
|
|
{
|
|
const bool bNegative = (nValue < 0) && (nBase == 10);
|
|
|
|
if(bNegative)
|
|
nValue = -(uint64_t)nValue;
|
|
|
|
return X64toaCommon((uint64_t)nValue, pBuffer, nBase, bNegative);
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API char* U64toa(uint64_t nValue, char* pBuffer, int nBase)
|
|
{
|
|
return X64toaCommon(nValue, pBuffer, nBase, 0);
|
|
}
|
|
|
|
EASTDC_API char16_t* U64toa(uint64_t nValue, char16_t* pBuffer, int nBase)
|
|
{
|
|
return X64toaCommon(nValue, pBuffer, nBase, 0);
|
|
}
|
|
|
|
EASTDC_API char32_t* U64toa(uint64_t nValue, char32_t* pBuffer, int nBase)
|
|
{
|
|
return X64toaCommon(nValue, pBuffer, nBase, 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EASTDC_API double StrtodEnglish(const char* pValue, char** ppEnd)
|
|
{
|
|
// This implementation is an exact copy of StrtodEnglish but
|
|
// with char in place of char16_t. For the time being, if
|
|
// you do maintenance on either of these functions, you need to
|
|
// copy the result to the other version.
|
|
int c;
|
|
double dTotal(0.0);
|
|
char chSign('+');
|
|
const char* pEnd = pValue;
|
|
|
|
while(Isspace(*pValue))
|
|
++pValue; //Remove leading spaces.
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
if(c == '-' || c == '+'){
|
|
chSign = (char)c;
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
while((c >= '0') && (c <= '9')){
|
|
dTotal = (10 * dTotal) + (c - '0');
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
if(c == '.'){
|
|
double dMultiplier(1); //Possibly some BCD variable would be more accurate.
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
while((c >= '0') && (c <= '9')){
|
|
dMultiplier *= 0.1;
|
|
dTotal += (c - '0') * dMultiplier;
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
}
|
|
|
|
if(c == 'e' || c == 'E'){
|
|
int nExponentValue(0);
|
|
double dExponentTotal;
|
|
char chExponentSign('+');
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++; //Move past the exponent.
|
|
|
|
if(c == '-' || c == '+'){
|
|
chExponentSign = (char)c;
|
|
pEnd = pValue;
|
|
c = *pValue++; //Move past the '+' or '-' sign.
|
|
}
|
|
|
|
while((c >= '0') && (c <= '9')){
|
|
nExponentValue = (10 * nExponentValue) + (c - '0');
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
dExponentTotal = ::pow(10.0, (double)nExponentValue); // The CRT pow function is actually somewhat slow and weak.
|
|
// It would be very nice to change this to at least implement
|
|
if(chExponentSign == '-') // the low exponents with a lookup table.
|
|
dExponentTotal = 1/dExponentTotal;
|
|
dTotal *= dExponentTotal;
|
|
}
|
|
|
|
if(ppEnd)
|
|
*ppEnd = (char*)pEnd;
|
|
|
|
if(chSign == '-')
|
|
return -dTotal;
|
|
return dTotal;
|
|
}
|
|
|
|
EASTDC_API double StrtodEnglish(const char16_t* pValue, char16_t** ppEnd)
|
|
{
|
|
// This implementation is an exact copy of StrtodEnglish8 but
|
|
// with char16_t in place of char. For the time being, if you
|
|
// do maintenance on either of these functions, you need to
|
|
// copy the result to the other version.
|
|
char16_t c;
|
|
double dTotal(0.0);
|
|
char16_t chSign('+');
|
|
const char16_t* pEnd = pValue;
|
|
|
|
while(Isspace(*pValue))
|
|
++pValue; // Remove leading spaces.
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
if(c == '-' || c == '+'){
|
|
chSign = (char16_t)c;
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
while((c >= '0') && (c <= '9')){
|
|
dTotal = (10 * dTotal) + (c - '0');
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
if(c == '.'){
|
|
double dMultiplier(1); // Possibly some BCD variable would be more accurate.
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
while((c >= '0') && (c <= '9')){
|
|
dMultiplier *= 0.1;
|
|
dTotal += (c - '0') * dMultiplier;
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
}
|
|
|
|
if(c == 'e' || c == 'E'){
|
|
int nExponentValue(0);
|
|
double dExponentTotal;
|
|
char16_t chExponentSign('+');
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++; //Move past the exponent.
|
|
|
|
if(c == '-' || c == '+'){
|
|
chExponentSign = (char16_t)c;
|
|
pEnd = pValue;
|
|
c = *pValue++; // Move past the '+' or '-' sign.
|
|
}
|
|
|
|
while((c >= '0') && (c <= '9')){
|
|
nExponentValue = (int)((10 * nExponentValue) + (c - '0'));
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
dExponentTotal = ::pow(10.0, (double)nExponentValue); // The CRT pow function is actually somewhat slow and weak.
|
|
// It would be very nice to change this to at least implement
|
|
if(chExponentSign == '-') // the low exponents with a lookup table.
|
|
dExponentTotal = 1/dExponentTotal;
|
|
dTotal *= dExponentTotal;
|
|
}
|
|
|
|
if(ppEnd)
|
|
*ppEnd = (char16_t*)pEnd;
|
|
|
|
if(chSign == '-')
|
|
return -dTotal;
|
|
return dTotal;
|
|
}
|
|
|
|
EASTDC_API double StrtodEnglish(const char32_t* pValue, char32_t** ppEnd)
|
|
{
|
|
// This implementation is an exact copy of StrtodEnglish8 but
|
|
// with char32_t in place of char. For the time being, if you
|
|
// do maintenance on either of these functions, you need to
|
|
// copy the result to the other version.
|
|
char32_t c;
|
|
double dTotal(0.0);
|
|
char32_t chSign('+');
|
|
const char32_t* pEnd = pValue;
|
|
|
|
while(Isspace(*pValue))
|
|
++pValue; // Remove leading spaces.
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
if(c == '-' || c == '+'){
|
|
chSign = (char32_t)c;
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
while((c >= '0') && (c <= '9')){
|
|
dTotal = (10 * dTotal) + (c - '0');
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
if(c == '.'){
|
|
double dMultiplier(1); // Possibly some BCD variable would be more accurate.
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
while((c >= '0') && (c <= '9')){
|
|
dMultiplier *= 0.1;
|
|
dTotal += (c - '0') * dMultiplier;
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
}
|
|
|
|
if(c == 'e' || c == 'E'){
|
|
int nExponentValue(0);
|
|
double dExponentTotal;
|
|
char32_t chExponentSign('+');
|
|
|
|
pEnd = pValue;
|
|
c = *pValue++; //Move past the exponent.
|
|
|
|
if(c == '-' || c == '+'){
|
|
chExponentSign = (char32_t)c;
|
|
pEnd = pValue;
|
|
c = *pValue++; // Move past the '+' or '-' sign.
|
|
}
|
|
|
|
while((c >= '0') && (c <= '9')){
|
|
nExponentValue = (int)((10 * nExponentValue) + (c - '0'));
|
|
pEnd = pValue;
|
|
c = *pValue++;
|
|
}
|
|
|
|
dExponentTotal = ::pow(10.0, (double)nExponentValue); // The CRT pow function is actually somewhat slow and weak.
|
|
// It would be very nice to change this to at least implement
|
|
if(chExponentSign == '-') // the low exponents with a lookup table.
|
|
dExponentTotal = 1/dExponentTotal;
|
|
dTotal *= dExponentTotal;
|
|
}
|
|
|
|
if(ppEnd)
|
|
*ppEnd = (char32_t*)pEnd;
|
|
|
|
if(chSign == '-')
|
|
return -dTotal;
|
|
return dTotal;
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint64_t StrtoU64Common(const char* pValue, char** ppEnd, int nBase, bool bUnsigned)
|
|
{
|
|
uint64_t nValue(0); // Current value
|
|
const char* p = pValue; // Current position
|
|
char c; // Temp value
|
|
char chSign('+'); // One of either '+' or '-'
|
|
bool bDigitWasRead(false); // True if any digits were read.
|
|
bool bOverflowOccurred(false); // True if integer overflow occurred.
|
|
|
|
// Skip leading whitespace
|
|
c = *p++;
|
|
while(Isspace(c))
|
|
c = *p++;
|
|
|
|
// Check for sign.
|
|
if((c == '-') || (c == '+')){
|
|
chSign = c;
|
|
c = *p++;
|
|
}
|
|
|
|
// Do checks on nBase.
|
|
if((nBase < 0) || (nBase == 1) || (nBase > 36)){
|
|
if(ppEnd)
|
|
*ppEnd = (char*)pValue;
|
|
return 0;
|
|
}
|
|
else if(nBase == 0){
|
|
// Auto detect one of base 8, 10, or 16.
|
|
if(c != '0')
|
|
nBase = 10;
|
|
else if(*p == 'x' || *p == 'X')
|
|
nBase = 16;
|
|
else
|
|
nBase = 8;
|
|
}
|
|
if(nBase == 16){
|
|
// If there is a leading '0x', then skip past it.
|
|
if((c == '0') && ((*p == 'x') || (*p == 'X'))) {
|
|
++p;
|
|
c = *p++;
|
|
}
|
|
}
|
|
|
|
// If nValue exceeds this, an integer overflow is reported.
|
|
#if (EA_PLATFORM_WORD_SIZE >= 8)
|
|
const uint64_t nMaxValue(UINT64_MAX / nBase);
|
|
const uint64_t nModValue(UINT64_MAX % nBase);
|
|
#else
|
|
// 32 bit platforms are very slow at doing 64 bit div and mod operations.
|
|
uint64_t nMaxValue;
|
|
uint64_t nModValue;
|
|
|
|
switch(nBase)
|
|
{
|
|
case 2:
|
|
nMaxValue = UINT64_C(9223372036854775807);
|
|
nModValue = 1;
|
|
break;
|
|
case 8:
|
|
nMaxValue = UINT64_C(2305843009213693951);
|
|
nModValue = 7;
|
|
break;
|
|
case 10:
|
|
nMaxValue = UINT64_C(1844674407370955161);
|
|
nModValue = 5;
|
|
break;
|
|
case 16:
|
|
nMaxValue = UINT64_C(1152921504606846975);
|
|
nModValue = 15;
|
|
break;
|
|
default:
|
|
nMaxValue = (UINT64_MAX / nBase);
|
|
nModValue = (UINT64_MAX % nBase);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
for(unsigned nCurrentDigit; ; ){
|
|
if(Isdigit(c))
|
|
nCurrentDigit = (unsigned)(c - '0');
|
|
else if(Isalpha(c))
|
|
nCurrentDigit = (unsigned)(Toupper(c) - 'A' + 10);
|
|
else
|
|
break; // The digit is invalid.
|
|
|
|
if(nCurrentDigit >= (unsigned)nBase)
|
|
break; // The digit is invalid.
|
|
|
|
bDigitWasRead = true;
|
|
|
|
// Check for overflow.
|
|
if((nValue < nMaxValue) || ((nValue == nMaxValue) && ((uint64_t)nCurrentDigit <= nModValue)))
|
|
nValue = (nValue * nBase) + nCurrentDigit;
|
|
else
|
|
bOverflowOccurred = true; // Set the flag, but continue processing.
|
|
|
|
c = *p++;
|
|
}
|
|
|
|
--p; // Go back to the last character
|
|
|
|
if(!bDigitWasRead){
|
|
if(ppEnd)
|
|
p = pValue; // We'll assign 'ppEnd' below.
|
|
}
|
|
else if(bOverflowOccurred || (!bUnsigned && (((chSign == '-') && (nValue > ((uint64_t)INT64_MAX + 1))) || ((chSign == '+') && (nValue > (uint64_t)INT64_MAX))))){
|
|
// Integer overflow occurred.
|
|
if(bUnsigned)
|
|
nValue = UINT64_MAX;
|
|
else if(chSign == '-')
|
|
nValue = (uint64_t)INT64_MAX + 1; // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
|
|
else
|
|
nValue = INT64_MAX;
|
|
|
|
errno = ERANGE; // The standard specifies that we set this value.
|
|
}
|
|
|
|
if(ppEnd)
|
|
*ppEnd = (char*)p;
|
|
|
|
if(chSign == '-')
|
|
nValue = -nValue;
|
|
|
|
return nValue;
|
|
}
|
|
|
|
static uint64_t StrtoU64Common(const char16_t* pValue, char16_t** ppEnd, int nBase, bool bUnsigned)
|
|
{
|
|
uint64_t nValue(0); // Current value
|
|
const char16_t* p = pValue; // Current position
|
|
char16_t c; // Temp value
|
|
char16_t chSign('+'); // One of either '+' or '-'
|
|
bool bDigitWasRead(false); // True if any digits were read.
|
|
bool bOverflowOccurred(false); // True if integer overflow occurred.
|
|
|
|
// Skip leading whitespace
|
|
c = *p++;
|
|
while(Isspace(c))
|
|
c = *p++;
|
|
|
|
// Check for sign.
|
|
if((c == '-') || (c == '+')){
|
|
chSign = c;
|
|
c = *p++;
|
|
}
|
|
|
|
// Do checks on nBase.
|
|
if((nBase < 0) || (nBase == 1) || (nBase > 36)){
|
|
if(ppEnd)
|
|
*ppEnd = (char16_t*)pValue;
|
|
return 0;
|
|
}
|
|
else if(nBase == 0){
|
|
// Auto detect one of base 8, 10, or 16.
|
|
if(c != '0')
|
|
nBase = 10;
|
|
else if(*p == 'x' || *p == 'X')
|
|
nBase = 16;
|
|
else
|
|
nBase = 8;
|
|
}
|
|
if(nBase == 16){
|
|
// If there is a leading '0x', then skip past it.
|
|
if((c == '0') && ((*p == 'x') || (*p == 'X'))) {
|
|
++p;
|
|
c = *p++;
|
|
}
|
|
}
|
|
|
|
// If nValue exceeds this, an integer overflow is reported.
|
|
#if (EA_PLATFORM_WORD_SIZE >= 8)
|
|
const uint64_t nMaxValue(UINT64_MAX / nBase);
|
|
const uint64_t nModValue(UINT64_MAX % nBase);
|
|
#else
|
|
// 32 bit platforms are very slow at doing 64 bit div and mod operations.
|
|
uint64_t nMaxValue;
|
|
uint64_t nModValue;
|
|
|
|
switch(nBase)
|
|
{
|
|
case 2:
|
|
nMaxValue = UINT64_C(9223372036854775807);
|
|
nModValue = 1;
|
|
break;
|
|
case 8:
|
|
nMaxValue = UINT64_C(2305843009213693951);
|
|
nModValue = 7;
|
|
break;
|
|
case 10:
|
|
nMaxValue = UINT64_C(1844674407370955161);
|
|
nModValue = 5;
|
|
break;
|
|
case 16:
|
|
nMaxValue = UINT64_C(1152921504606846975);
|
|
nModValue = 15;
|
|
break;
|
|
default:
|
|
nMaxValue = (UINT64_MAX / nBase);
|
|
nModValue = (UINT64_MAX % nBase);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
for(unsigned nCurrentDigit; ;){
|
|
if(Isdigit(c))
|
|
nCurrentDigit = (unsigned)(c - '0');
|
|
else if(Isalpha(c))
|
|
nCurrentDigit = (unsigned)(Toupper(c) - 'A' + 10);
|
|
else
|
|
break; // The digit is invalid.
|
|
|
|
if(nCurrentDigit >= (unsigned)nBase)
|
|
break; // The digit is invalid.
|
|
|
|
bDigitWasRead = true;
|
|
|
|
// Check for overflow.
|
|
if((nValue < nMaxValue) || ((nValue == nMaxValue) && ((uint64_t)nCurrentDigit <= nModValue)))
|
|
nValue = (nValue * nBase) + nCurrentDigit;
|
|
else
|
|
bOverflowOccurred = true; // Set the flag, but continue processing.
|
|
|
|
c = *p++;
|
|
}
|
|
|
|
--p; // Go back to the last character
|
|
|
|
if(!bDigitWasRead){
|
|
if(ppEnd)
|
|
p = pValue; // We'll assign 'ppEnd' below.
|
|
} // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
|
|
else if(bOverflowOccurred || (!bUnsigned && (((chSign == '-') && (nValue > ((uint64_t)INT64_MAX + 1))) || ((chSign == '+') && (nValue > (uint64_t)INT64_MAX))))){
|
|
// Integer overflow occurred.
|
|
if(bUnsigned)
|
|
nValue = UINT64_MAX;
|
|
else if(chSign == '-')
|
|
nValue = (uint64_t)INT64_MAX + 1; // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
|
|
else
|
|
nValue = INT64_MAX;
|
|
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoU64Common: Range underflow or overflow.");}
|
|
errno = ERANGE; // The standard specifies that we set this value.
|
|
}
|
|
|
|
if(ppEnd)
|
|
*ppEnd = (char16_t*)p;
|
|
|
|
if(chSign == '-')
|
|
nValue = -nValue;
|
|
|
|
return nValue;
|
|
}
|
|
|
|
static uint64_t StrtoU64Common(const char32_t* pValue, char32_t** ppEnd, int nBase, bool bUnsigned)
|
|
{
|
|
uint64_t nValue(0); // Current value
|
|
const char32_t* p = pValue; // Current position
|
|
char32_t c; // Temp value
|
|
char32_t chSign('+'); // One of either '+' or '-'
|
|
bool bDigitWasRead(false); // True if any digits were read.
|
|
bool bOverflowOccurred(false); // True if integer overflow occurred.
|
|
|
|
// Skip leading whitespace
|
|
c = *p++;
|
|
while(Isspace(c))
|
|
c = *p++;
|
|
|
|
// Check for sign.
|
|
if((c == '-') || (c == '+')){
|
|
chSign = c;
|
|
c = *p++;
|
|
}
|
|
|
|
// Do checks on nBase.
|
|
if((nBase < 0) || (nBase == 1) || (nBase > 36)){
|
|
if(ppEnd)
|
|
*ppEnd = (char32_t*)pValue;
|
|
return 0;
|
|
}
|
|
else if(nBase == 0){
|
|
// Auto detect one of base 8, 10, or 32.
|
|
if(c != '0')
|
|
nBase = 10;
|
|
else if(*p == 'x' || *p == 'X')
|
|
nBase = 32;
|
|
else
|
|
nBase = 8;
|
|
}
|
|
if(nBase == 16){
|
|
// If there is a leading '0x', then skip past it.
|
|
if((c == '0') && ((*p == 'x') || (*p == 'X'))) {
|
|
++p;
|
|
c = *p++;
|
|
}
|
|
}
|
|
|
|
// If nValue exceeds this, an integer overflow is reported.
|
|
#if (EA_PLATFORM_WORD_SIZE >= 8)
|
|
const uint64_t nMaxValue(UINT64_MAX / nBase);
|
|
const uint64_t nModValue(UINT64_MAX % nBase);
|
|
#else
|
|
// 32 bit platforms are very slow at doing 64 bit div and mod operations.
|
|
uint64_t nMaxValue;
|
|
uint64_t nModValue;
|
|
|
|
switch(nBase)
|
|
{
|
|
case 2:
|
|
nMaxValue = UINT64_C(9223372036854775807);
|
|
nModValue = 1;
|
|
break;
|
|
case 8:
|
|
nMaxValue = UINT64_C(2305843009213693951);
|
|
nModValue = 7;
|
|
break;
|
|
case 10:
|
|
nMaxValue = UINT64_C(1844674407370955161);
|
|
nModValue = 5;
|
|
break;
|
|
case 16:
|
|
nMaxValue = UINT64_C(1152921504606846975);
|
|
nModValue = 15;
|
|
break;
|
|
default:
|
|
nMaxValue = (UINT64_MAX / nBase);
|
|
nModValue = (UINT64_MAX % nBase);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
for(unsigned nCurrentDigit; ;){
|
|
if(Isdigit(c))
|
|
nCurrentDigit = (unsigned)(c - '0');
|
|
else if(Isalpha(c))
|
|
nCurrentDigit = (unsigned)(Toupper(c) - 'A' + 10);
|
|
else
|
|
break; // The digit is invalid.
|
|
|
|
if(nCurrentDigit >= (unsigned)nBase)
|
|
break; // The digit is invalid.
|
|
|
|
bDigitWasRead = true;
|
|
|
|
// Check for overflow.
|
|
if((nValue < nMaxValue) || ((nValue == nMaxValue) && ((uint64_t)nCurrentDigit <= nModValue)))
|
|
nValue = (nValue * nBase) + nCurrentDigit;
|
|
else
|
|
bOverflowOccurred = true; // Set the flag, but continue processing.
|
|
|
|
c = *p++;
|
|
}
|
|
|
|
--p; // Go back to the last character
|
|
|
|
if(!bDigitWasRead){
|
|
if(ppEnd)
|
|
p = pValue; // We'll assign 'ppEnd' below.
|
|
} // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
|
|
else if(bOverflowOccurred || (!bUnsigned && (((chSign == '-') && (nValue > ((uint64_t)INT64_MAX + 1))) || ((chSign == '+') && (nValue > (uint64_t)INT64_MAX))))){
|
|
// Integer overflow occurred.
|
|
if(bUnsigned)
|
|
nValue = UINT64_MAX;
|
|
else if(chSign == '-')
|
|
nValue = (uint64_t)INT64_MAX + 1; // INT64_MAX + 1 is the same thing as -INT64_MIN with most compilers.
|
|
else
|
|
nValue = INT64_MAX;
|
|
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoU64Common: Range underflow or overflow.");}
|
|
errno = ERANGE; // The standard specifies that we set this value.
|
|
}
|
|
|
|
if(ppEnd)
|
|
*ppEnd = (char32_t*)p;
|
|
|
|
if(chSign == '-')
|
|
nValue = -nValue;
|
|
|
|
return nValue;
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API int32_t StrtoI32(const char* pValue, char** ppEnd, int nBase)
|
|
{
|
|
int64_t val = (int64_t) StrtoU64Common(pValue, ppEnd, nBase, false);
|
|
|
|
if(val < INT32_MIN)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoI32: Range underflow. You may need to use StrtoI64 instead."); }
|
|
errno = ERANGE;
|
|
return (int32_t)INT32_MIN;
|
|
}
|
|
|
|
if(val > INT32_MAX)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoI32: Range overflow. You may need to use StrtoU32 or StrtoU64 instead."); }
|
|
errno = ERANGE;
|
|
return INT32_MAX;
|
|
}
|
|
|
|
return (int32_t) val;
|
|
}
|
|
|
|
EASTDC_API int32_t StrtoI32(const char16_t* pValue, char16_t** ppEnd, int nBase)
|
|
{
|
|
int64_t val = (int64_t) StrtoU64Common(pValue, ppEnd, nBase, false);
|
|
|
|
if(val < INT32_MIN)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoI32: Range underflow. You may need to use StrtoI64 instead."); }
|
|
errno = ERANGE;
|
|
return (int32_t)INT32_MIN;
|
|
}
|
|
|
|
if(val > INT32_MAX)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoI32: Range overflow. You may need to use StrtoU32 or StrtoU64 instead."); }
|
|
errno = ERANGE;
|
|
return INT32_MAX;
|
|
}
|
|
|
|
return (int32_t) val;
|
|
}
|
|
|
|
EASTDC_API int32_t StrtoI32(const char32_t* pValue, char32_t** ppEnd, int nBase)
|
|
{
|
|
int64_t val = (int64_t) StrtoU64Common(pValue, ppEnd, nBase, false);
|
|
|
|
if(val < INT32_MIN)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoI32: Range underflow. You may need to use StrtoI64 instead."); }
|
|
errno = ERANGE;
|
|
return (int32_t)INT32_MIN;
|
|
}
|
|
|
|
if(val > INT32_MAX)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoI32: Range overflow. You may need to use StrtoU32 or StrtoU64 instead."); }
|
|
errno = ERANGE;
|
|
return INT32_MAX;
|
|
}
|
|
|
|
return (int32_t) val;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API uint32_t StrtoU32(const char* pValue, char** ppEnd, int nBase)
|
|
{
|
|
uint64_t val = StrtoU64Common(pValue, ppEnd, nBase, true);
|
|
|
|
if(val > UINT32_MAX)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoU32: Range overflow. You may need to use StrtoU64 instead."); }
|
|
errno = ERANGE;
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
return (uint32_t)val;
|
|
}
|
|
|
|
EASTDC_API uint32_t StrtoU32(const char16_t* pValue, char16_t** ppEnd, int nBase)
|
|
{
|
|
uint64_t val = StrtoU64Common(pValue, ppEnd, nBase, true);
|
|
|
|
if(val > UINT32_MAX)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoU32: Range overflow. You may need to use StrtoU64 instead."); }
|
|
errno = ERANGE;
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
return (uint32_t)val;
|
|
}
|
|
|
|
EASTDC_API uint32_t StrtoU32(const char32_t* pValue, char32_t** ppEnd, int nBase)
|
|
{
|
|
uint64_t val = StrtoU64Common(pValue, ppEnd, nBase, true);
|
|
|
|
if(val > UINT32_MAX)
|
|
{
|
|
if(EA::StdC::GetAssertionsEnabled())
|
|
{ EA_FAIL_MSG("StrtoU32: Range overflow. You may need to use StrtoU64 instead."); }
|
|
errno = ERANGE;
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
return (uint32_t)val;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API int64_t StrtoI64(const char* pString, char** ppStringEnd, int nBase)
|
|
{
|
|
return (int64_t)StrtoU64Common(pString, ppStringEnd, nBase, false);
|
|
}
|
|
|
|
EASTDC_API int64_t StrtoI64(const char16_t* pString, char16_t** ppStringEnd, int nBase)
|
|
{
|
|
return (int64_t)StrtoU64Common(pString, ppStringEnd, nBase, false);
|
|
}
|
|
|
|
EASTDC_API int64_t StrtoI64(const char32_t* pString, char32_t** ppStringEnd, int nBase)
|
|
{
|
|
return (int64_t)StrtoU64Common(pString, ppStringEnd, nBase, false);
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API uint64_t StrtoU64(const char* pString, char** ppStringEnd, int nBase)
|
|
{
|
|
return StrtoU64Common(pString, ppStringEnd, nBase, true);
|
|
}
|
|
|
|
EASTDC_API uint64_t StrtoU64(const char16_t* pString, char16_t** ppStringEnd, int nBase)
|
|
{
|
|
return StrtoU64Common(pString, ppStringEnd, nBase, true);
|
|
}
|
|
|
|
EASTDC_API uint64_t StrtoU64(const char32_t* pString, char32_t** ppStringEnd, int nBase)
|
|
{
|
|
return StrtoU64Common(pString, ppStringEnd, nBase, true);
|
|
}
|
|
|
|
|
|
|
|
EASTDC_API char* FtoaEnglish(double dValue, char* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
|
|
{
|
|
// Note that this function is a duplicate of FtoaEnglish16 but
|
|
// with char instead of char16_t. Modifications to either of
|
|
// these functions should be replicated to the other.
|
|
|
|
int nDecimalPosition, nSign;
|
|
int nPositionResult(0);
|
|
int nPositionTemp(0);
|
|
int i;
|
|
int nExponent;
|
|
|
|
if(nResultCapacity <= 0)
|
|
return NULL;
|
|
|
|
if(bExponentEnabled){
|
|
if(dValue == 0.0)
|
|
nExponent = 0;
|
|
else
|
|
{
|
|
const double dValueAbs = fabs(dValue);
|
|
const double dValueLog = ::log10(dValueAbs);
|
|
nExponent = (int)::floor(dValueLog);
|
|
}
|
|
|
|
if((nExponent >= nPrecision) || (nExponent < -4)){ // printf's %g switches to exponential whenever exp >= precision || exp < -4.
|
|
// Compute how many digits we need for the exponent.
|
|
int nDigits = 1;
|
|
int nLimit = 10;
|
|
|
|
while(nLimit <= nExponent){
|
|
nLimit *= 10;
|
|
++nDigits;
|
|
}
|
|
|
|
const double dExpPow = ::pow(10.0, (double)-nExponent);
|
|
|
|
if(FtoaEnglish(dValue * dExpPow, pResult, nResultCapacity - nDigits - 2, nPrecision, false)){
|
|
char* p = pResult + Strlen(pResult);
|
|
|
|
*p++ = (char)'e';
|
|
*p++ = ((nExponent < 0) ? (char)'-' : (char)'+');
|
|
I32toa(abs(nExponent), p, 10);
|
|
return pResult;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// fcvt is a function that converts a floating point value to its component
|
|
// string, sign, and decimal position. It doesn't convert it to a fully
|
|
// finished string because sign and decimal usage usually varies between
|
|
// locales and this function is trying to be locale-independent. It is up
|
|
// to the user of this function to present the final data in a form that
|
|
// is locale-savvy. Actually, not all compilers implement fcvt.
|
|
#if EASTDC_NATIVE_FCVT
|
|
#ifdef __GNUC__ // nPrecision refers to the number of digits after the decimal point.
|
|
const char* const pResultTemp = fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
|
|
#else
|
|
char pResultTemp[_CVTBUFSIZE+1];
|
|
_fcvt_s(pResultTemp, sizeof(pResultTemp), dValue, nPrecision, &nDecimalPosition, &nSign);
|
|
#endif
|
|
#else
|
|
char bufferTemp[kFcvtBufMaxSize];
|
|
const char* const pResultTemp = FcvtBuf(dValue, nPrecision, &nDecimalPosition, &nSign, bufferTemp);
|
|
#endif
|
|
|
|
// If the value is negative, then add a leading '-' sign.
|
|
if(nSign){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult] = '-';
|
|
nPositionResult++;
|
|
}
|
|
|
|
// If the value is < 1, then add a leading '0' digit.
|
|
if(fabs(dValue) < 1.0){
|
|
#if EASTDC_NATIVE_FCVT && defined(__GNUC__)
|
|
// GCC's fcvt has a quirk: If the input dValue is 0 (but no other value, fractional or not),
|
|
// it yields an output string with a leading "0." So we need to make a special case to
|
|
// detect this here.
|
|
if(dValue != 0.0)
|
|
#endif
|
|
{
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '0';
|
|
}
|
|
}
|
|
|
|
// Read digits up to the decimal position and write them to the output string.
|
|
if(nDecimalPosition > 0){ // If the input was something like 1000.0
|
|
for(i = 0; (i < nDecimalPosition) && pResultTemp[nPositionTemp]; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = pResultTemp[nPositionTemp++];
|
|
}
|
|
}
|
|
|
|
if(pResultTemp[nPositionTemp]){
|
|
// Find the last of the zeroes in the pResultTemp string. We don't want
|
|
// to add unnecessary trailing zeroes to the returned string and don't
|
|
// want to return a decimal point in the string if it isn't necessary.
|
|
int nFirstTrailingZeroPosition(nPositionTemp);
|
|
int nLastPositionTemp(nPositionTemp);
|
|
|
|
while(pResultTemp[nLastPositionTemp]){
|
|
if(pResultTemp[nLastPositionTemp] != '0')
|
|
nFirstTrailingZeroPosition = nLastPositionTemp + 1;
|
|
nLastPositionTemp++;
|
|
}
|
|
|
|
// If there is any reason to write a decimal point, then we write
|
|
// it and write the data that comes after it.
|
|
if((nFirstTrailingZeroPosition > nPositionTemp) && (nPrecision > 0)){
|
|
// Add a decimal point.
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '.';
|
|
|
|
if(nDecimalPosition < 0){ // If there are zeroes after the decimal...
|
|
for(i = nDecimalPosition; i < 0; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '0';
|
|
--nPrecision;
|
|
}
|
|
}
|
|
|
|
// Read digits after the decimal position and write them to the output string.
|
|
for(i = 0; (i < nPrecision) && (nPositionTemp < nFirstTrailingZeroPosition) && pResultTemp[nPositionTemp]; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
//What we do here is possibly erase trailing zeroes that we've written after the decimal.
|
|
int nEndPosition = EASTDC_MAX(nPositionResult - 1, 0);
|
|
pResult[nEndPosition] = 0;
|
|
while((--nEndPosition > 0) && (pResult[nEndPosition] == '0'))
|
|
pResult[nEndPosition] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = pResultTemp[nPositionTemp++];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write the final terminating zero.
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult] = 0;
|
|
return pResult;
|
|
}
|
|
|
|
EASTDC_API char16_t* FtoaEnglish(double dValue, char16_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
|
|
{
|
|
// Note that this function is a duplicate of FtoaEnglish8 but
|
|
// with char16_t instead of char. Modifications to either of
|
|
// these functions should be replicated to the other.
|
|
|
|
int nDecimalPosition, nSign;
|
|
int nPositionResult(0);
|
|
int nPositionTemp(0);
|
|
int i;
|
|
int nExponent;
|
|
|
|
if(nResultCapacity <= 0)
|
|
return NULL;
|
|
|
|
if(bExponentEnabled){
|
|
if(dValue == 0.0)
|
|
nExponent = 0;
|
|
else
|
|
{
|
|
const double dValueAbs = fabs(dValue);
|
|
const double dValueLog = ::log10(dValueAbs);
|
|
nExponent = (int)::floor(dValueLog);
|
|
}
|
|
|
|
if((nExponent >= nPrecision) || (nExponent < -4)){ // printf's %g switches to exponential whenever exp >= mnPrecisionUsed || exp < -4.
|
|
// Compute how many digits we need for the exponent.
|
|
int nDigits = 1;
|
|
int nLimit = 10;
|
|
|
|
while(nLimit <= nExponent){
|
|
nLimit *= 10;
|
|
++nDigits;
|
|
}
|
|
|
|
const double dExpPow = ::pow(10.0, (double)-nExponent);
|
|
|
|
if(FtoaEnglish(dValue * dExpPow, pResult, nResultCapacity - nDigits - 2, nPrecision, false)){
|
|
char16_t* p = pResult + Strlen(pResult);
|
|
|
|
*p++ = (char16_t)'e';
|
|
*p++ = ((nExponent < 0) ? (char16_t)'-' : (char16_t)'+');
|
|
I32toa(abs(nExponent), p, 10);
|
|
return pResult;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// fcvt is a function that converts a floating point value to its component
|
|
// string, sign, and decimal position. It doesn't convert it to a fully
|
|
// finished string because sign and decimal usage usually varies between
|
|
// locales and this function is trying to be locale-independent. It is up
|
|
// to the user of this function to present the final data in a form that
|
|
// is locale-savvy. Actually, not all compilers implement fcvt.
|
|
#if EASTDC_NATIVE_FCVT
|
|
#ifdef __GNUC__
|
|
const char* const pResultTemp = fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
|
|
#else
|
|
const char* const pResultTemp = _fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
|
|
#endif
|
|
#else
|
|
char bufferTemp[kFcvtBufMaxSize];
|
|
const char* const pResultTemp = FcvtBuf(dValue, nPrecision, &nDecimalPosition, &nSign, bufferTemp);
|
|
#endif
|
|
|
|
// If the value is negative, then add a leading '-' sign.
|
|
if(nSign){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult] = '-';
|
|
nPositionResult++;
|
|
}
|
|
|
|
// If the value is < 1, then add a leading '0' digit.
|
|
if(fabs(dValue) < 1.0){
|
|
#if EASTDC_NATIVE_FCVT && defined(__GNUC__)
|
|
// GCC's fcvt has a quirk: If the input dValue is 0 (but no other value, fractional or not),
|
|
// it yields an output string with a leading "0." So we need to make a special case to
|
|
// detect this here.
|
|
if(dValue != 0.0)
|
|
#endif
|
|
{
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '0';
|
|
}
|
|
}
|
|
|
|
// Read digits up to the decimal position and write them to the output string.
|
|
if(nDecimalPosition > 0){ // If the input was something like 1000.0
|
|
for(i = 0; (i < nDecimalPosition) && pResultTemp[nPositionTemp]; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = (char16_t)pResultTemp[nPositionTemp++];
|
|
}
|
|
}
|
|
|
|
if(pResultTemp[nPositionTemp]){
|
|
// Find the last of the zeroes in the pResultTemp string. We don't want
|
|
// to add unnecessary trailing zeroes to the returned string and don't
|
|
// want to return a decimal point in the string if it isn't necessary.
|
|
int nFirstTrailingZeroPosition(nPositionTemp);
|
|
int nLastPositionTemp(nPositionTemp);
|
|
|
|
while(pResultTemp[nLastPositionTemp]){
|
|
if(pResultTemp[nLastPositionTemp] != '0')
|
|
nFirstTrailingZeroPosition = nLastPositionTemp + 1;
|
|
nLastPositionTemp++;
|
|
}
|
|
|
|
// If there is any reason to write a decimal point, then we write
|
|
// it and write the data that comes after it.
|
|
if((nFirstTrailingZeroPosition > nPositionTemp) && (nPrecision > 0)){
|
|
// Add a decimal point.
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '.';
|
|
|
|
if(nDecimalPosition < 0){ // If there are zeroes after the decimal...
|
|
for(i = nDecimalPosition; i < 0; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '0';
|
|
--nPrecision;
|
|
}
|
|
}
|
|
|
|
// Read digits after the decimal position and write them to the output string.
|
|
for(i = 0; (i < nPrecision) && (nPositionTemp < nFirstTrailingZeroPosition) && pResultTemp[nPositionTemp]; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
//What we do here is possibly erase trailing zeroes that we've written after the decimal.
|
|
int nEndPosition = EASTDC_MAX(nPositionResult - 1, 0);
|
|
pResult[nEndPosition] = 0;
|
|
while((--nEndPosition > 0) && (pResult[nEndPosition] == '0'))
|
|
pResult[nEndPosition] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = (char16_t)pResultTemp[nPositionTemp++];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write the final terminating zero.
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult] = 0;
|
|
return pResult;
|
|
}
|
|
|
|
EASTDC_API char32_t* FtoaEnglish(double dValue, char32_t* pResult, int nResultCapacity, int nPrecision, bool bExponentEnabled)
|
|
{
|
|
// Note that this function is a duplicate of FtoaEnglish8 but
|
|
// with char32_t instead of char. Modifications to either of
|
|
// these functions should be replicated to the other.
|
|
|
|
int nDecimalPosition, nSign;
|
|
int nPositionResult(0);
|
|
int nPositionTemp(0);
|
|
int i;
|
|
int nExponent;
|
|
|
|
if(nResultCapacity <= 0)
|
|
return NULL;
|
|
|
|
if(bExponentEnabled){
|
|
if(dValue == 0.0)
|
|
nExponent = 0;
|
|
else
|
|
{
|
|
const double dValueAbs = fabs(dValue);
|
|
const double dValueLog = ::log10(dValueAbs);
|
|
nExponent = (int)::floor(dValueLog);
|
|
}
|
|
|
|
if((nExponent >= nPrecision) || (nExponent < -4)){ // printf's %g switches to exponential whenever exp >= mnPrecisionUsed || exp < -4.
|
|
// Compute how many digits we need for the exponent.
|
|
int nDigits = 1;
|
|
int nLimit = 10;
|
|
|
|
while(nLimit <= nExponent){
|
|
nLimit *= 10;
|
|
++nDigits;
|
|
}
|
|
|
|
const double dExpPow = ::pow(10.0, (double)-nExponent);
|
|
|
|
if(FtoaEnglish(dValue * dExpPow, pResult, nResultCapacity - nDigits - 2, nPrecision, false)){
|
|
char32_t* p = pResult + Strlen(pResult);
|
|
|
|
*p++ = (char32_t)'e';
|
|
*p++ = ((nExponent < 0) ? (char32_t)'-' : (char32_t)'+');
|
|
I32toa(abs(nExponent), p, 10);
|
|
return pResult;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// fcvt is a function that converts a floating point value to its component
|
|
// string, sign, and decimal position. It doesn't convert it to a fully
|
|
// finished string because sign and decimal usage usually varies between
|
|
// locales and this function is trying to be locale-independent. It is up
|
|
// to the user of this function to present the final data in a form that
|
|
// is locale-savvy. Actually, not all compilers implement fcvt.
|
|
#if EASTDC_NATIVE_FCVT
|
|
#ifdef __GNUC__
|
|
const char* const pResultTemp = fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
|
|
#else
|
|
const char* const pResultTemp = _fcvt(dValue, nPrecision, &nDecimalPosition, &nSign);
|
|
#endif
|
|
#else
|
|
char bufferTemp[kFcvtBufMaxSize];
|
|
const char* const pResultTemp = FcvtBuf(dValue, nPrecision, &nDecimalPosition, &nSign, bufferTemp);
|
|
#endif
|
|
|
|
// If the value is negative, then add a leading '-' sign.
|
|
if(nSign){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult] = '-';
|
|
nPositionResult++;
|
|
}
|
|
|
|
// If the value is < 1, then add a leading '0' digit.
|
|
if(fabs(dValue) < 1.0){
|
|
#if EASTDC_NATIVE_FCVT && defined(__GNUC__)
|
|
// GCC's fcvt has a quirk: If the input dValue is 0 (but no other value, fractional or not),
|
|
// it yields an output string with a leading "0." So we need to make a special case to
|
|
// detect this here.
|
|
if(dValue != 0.0)
|
|
#endif
|
|
{
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '0';
|
|
}
|
|
}
|
|
|
|
// Read digits up to the decimal position and write them to the output string.
|
|
if(nDecimalPosition > 0){ // If the input was something like 1000.0
|
|
for(i = 0; (i < nDecimalPosition) && pResultTemp[nPositionTemp]; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = (char32_t)pResultTemp[nPositionTemp++];
|
|
}
|
|
}
|
|
|
|
if(pResultTemp[nPositionTemp]){
|
|
// Find the last of the zeroes in the pResultTemp string. We don't want
|
|
// to add unnecessary trailing zeroes to the returned string and don't
|
|
// want to return a decimal point in the string if it isn't necessary.
|
|
int nFirstTrailingZeroPosition(nPositionTemp);
|
|
int nLastPositionTemp(nPositionTemp);
|
|
|
|
while(pResultTemp[nLastPositionTemp]){
|
|
if(pResultTemp[nLastPositionTemp] != '0')
|
|
nFirstTrailingZeroPosition = nLastPositionTemp + 1;
|
|
nLastPositionTemp++;
|
|
}
|
|
|
|
// If there is any reason to write a decimal point, then we write
|
|
// it and write the data that comes after it.
|
|
if((nFirstTrailingZeroPosition > nPositionTemp) && (nPrecision > 0)){
|
|
// Add a decimal point.
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '.';
|
|
|
|
if(nDecimalPosition < 0){ // If there are zeroes after the decimal...
|
|
for(i = nDecimalPosition; i < 0; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = '0';
|
|
--nPrecision;
|
|
}
|
|
}
|
|
|
|
// Read digits after the decimal position and write them to the output string.
|
|
for(i = 0; (i < nPrecision) && (nPositionTemp < nFirstTrailingZeroPosition) && pResultTemp[nPositionTemp]; i++){
|
|
if(nPositionResult >= nResultCapacity){
|
|
//What we do here is possibly erase trailing zeroes that we've written after the decimal.
|
|
int nEndPosition = EASTDC_MAX(nPositionResult - 1, 0);
|
|
pResult[nEndPosition] = 0;
|
|
while((--nEndPosition > 0) && (pResult[nEndPosition] == '0'))
|
|
pResult[nEndPosition] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult++] = (char32_t)pResultTemp[nPositionTemp++];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write the final terminating zero.
|
|
if(nPositionResult >= nResultCapacity){
|
|
pResult[EASTDC_MAX(nPositionResult - 1, 0)] = 0;
|
|
return NULL;
|
|
}
|
|
pResult[nPositionResult] = 0;
|
|
return pResult;
|
|
}
|
|
|
|
|
|
|
|
|
|
EASTDC_API size_t ReduceFloatString(char* pString, size_t nLength)
|
|
{
|
|
if(nLength == (size_t)-1)
|
|
nLength = strlen(pString);
|
|
|
|
size_t nNewLength(nLength);
|
|
|
|
if(nLength > 0)
|
|
{
|
|
// Get the decimal index and exponent index. We won't chop off any zeros
|
|
// unless they are after the decimal position and before an exponent position.
|
|
int nDecimalIndex = -1;
|
|
int nExponentIndex = -1;
|
|
int nCurrentIndex = 0;
|
|
|
|
while(nCurrentIndex < (int)nLength)
|
|
{
|
|
if(pString[nCurrentIndex] == '.')
|
|
nDecimalIndex = nCurrentIndex;
|
|
if((pString[nCurrentIndex] == 'e') || (pString[nCurrentIndex] == 'E'))
|
|
nExponentIndex = nCurrentIndex;
|
|
nCurrentIndex++;
|
|
}
|
|
|
|
// Now we need to go to the end of the string and walk backwards to
|
|
// find any contiguous zero digits after a decimal point.
|
|
if(nDecimalIndex >= 0) // If there is any decimal point...
|
|
{
|
|
const int nFirstDigitToCheck(nDecimalIndex + 1);
|
|
const int nLastDigitToCheck ((nExponentIndex >= 0) ? (nExponentIndex - 1) : (int)(nLength - 1));
|
|
|
|
nCurrentIndex = nLastDigitToCheck;
|
|
|
|
while(nCurrentIndex >= nFirstDigitToCheck)
|
|
{
|
|
// assert((pString[nCurrentIndex] >= '0') && (pString[nCurrentIndex] <= '9'));
|
|
|
|
if(pString[nCurrentIndex] == '0')
|
|
{
|
|
// Copy the string downward. Note that we copy the trailing
|
|
// terminator of the string as well.
|
|
for(int i = nCurrentIndex; i < (int)nNewLength; i++)
|
|
pString[i] = pString[i + 1]; // Copy the string downward.
|
|
nNewLength--;
|
|
}
|
|
else
|
|
break;
|
|
nCurrentIndex--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the string is all zeroes, convert it to just one zero.
|
|
size_t i;
|
|
|
|
for(i = 0; (i < nLength) && (pString[i] == '0'); i++)
|
|
{ } // Do nothing.
|
|
|
|
if(i == nLength)
|
|
nLength = 0; // And fall through to the code below.
|
|
}
|
|
|
|
// It is possible that the input string was "000", in which case the above code would
|
|
// erase the entire string. Here we simply make a string of "0" and return it.
|
|
if(nLength == 0)
|
|
{
|
|
pString[0] = '0';
|
|
pString[1] = 0;
|
|
nNewLength = 1;
|
|
}
|
|
else
|
|
{
|
|
// We may have a number such as "234.", in which case we remove the trailing decimal.
|
|
if((nDecimalIndex >= 0) && (nDecimalIndex == ((int)(unsigned)nNewLength - 1)))
|
|
{
|
|
pString[nDecimalIndex] = 0;
|
|
nNewLength--;
|
|
}
|
|
|
|
size_t i;
|
|
|
|
// It is also posible that we now have a string like "0." or "000." or just ".".
|
|
// In this case, we simply set the string to "0".
|
|
for(i = 0; i < nNewLength; i++)
|
|
{
|
|
if((pString[i] != '0') && (pString[i] != '.'))
|
|
break;
|
|
}
|
|
|
|
if(i == nNewLength) // If the string was all zeros...
|
|
{
|
|
pString[0] = '0';
|
|
pString[1] = 0;
|
|
nNewLength = 1;
|
|
}
|
|
|
|
if((nNewLength >= 3) && (pString[0] == '0') && (pString[1] == '.')) // If we have "0.x"
|
|
{
|
|
memmove(pString, pString + 1, nNewLength * sizeof(char));
|
|
nNewLength--;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nNewLength;
|
|
}
|
|
|
|
EASTDC_API size_t ReduceFloatString(char16_t* pString, size_t nLength)
|
|
{
|
|
// We implement this by calling the 8 bit version and copying its data.
|
|
char pBuffer8[64];
|
|
char* pCurrent8;
|
|
char16_t* pCurrent16;
|
|
size_t n = 0;
|
|
|
|
if(nLength < 63)
|
|
nLength = 63;
|
|
|
|
for(pCurrent8 = pBuffer8, pCurrent16 = pString; *pCurrent16 && (n < nLength); ++n) // Do a 16 bit to 8 bit strcpy.
|
|
*pCurrent8++ = (char)(unsigned char)*pCurrent16++;
|
|
|
|
*pCurrent8 = 0;
|
|
|
|
n = ReduceFloatString(pBuffer8, n);
|
|
|
|
for(pCurrent8 = pBuffer8, pCurrent16 = pString; *pCurrent8; ) // Do a 8 bit to 16 bit strcpy.
|
|
*pCurrent16++ = (char16_t)(unsigned char)*pCurrent8++;
|
|
|
|
*pCurrent16 = 0;
|
|
|
|
return n;
|
|
}
|
|
|
|
EASTDC_API size_t ReduceFloatString(char32_t* pString, size_t nLength)
|
|
{
|
|
// We implement this by calling the 8 bit version and copying its data.
|
|
char pBuffer8[64];
|
|
char* pCurrent8;
|
|
char32_t* pCurrent32;
|
|
size_t n = 0;
|
|
|
|
if(nLength < 63)
|
|
nLength = 63;
|
|
|
|
for(pCurrent8 = pBuffer8, pCurrent32 = pString; *pCurrent32 && (n < nLength); ++n) // Do a 32 bit to 8 bit strcpy.
|
|
*pCurrent8++ = (char)(unsigned char)*pCurrent32++;
|
|
|
|
*pCurrent8 = 0;
|
|
|
|
n = ReduceFloatString(pBuffer8, n);
|
|
|
|
for(pCurrent8 = pBuffer8, pCurrent32 = pString; *pCurrent8; ) // Do a 8 bit to 32 bit strcpy.
|
|
*pCurrent32++ = (char32_t)(unsigned char)*pCurrent8++;
|
|
|
|
*pCurrent32 = 0;
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
} // namespace StdC
|
|
} // namespace EA
|
|
|
|
|
|
#undef EASTDC_MIN
|
|
#undef EASTDC_MAX
|
|
|
|
|
|
#if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007)
|
|
EA_RESTORE_GCC_WARNING()
|
|
#endif
|
|
EA_RESTORE_VC_WARNING()
|
|
|
|
|
|
|