614 lines
12 KiB
C++
614 lines
12 KiB
C++
|
// File: crn_strutils.cpp
|
||
|
// See Copyright Notice and license at the end of inc/crnlib.h
|
||
|
#include "crn_core.h"
|
||
|
#include "crn_strutils.h"
|
||
|
|
||
|
namespace crnlib
|
||
|
{
|
||
|
char* crn_strdup(const char* pStr)
|
||
|
{
|
||
|
if (!pStr)
|
||
|
pStr = "";
|
||
|
|
||
|
size_t l = strlen(pStr) + 1;
|
||
|
char *p = (char *)crnlib_malloc(l);
|
||
|
if (p)
|
||
|
memcpy(p, pStr, l);
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
int crn_stricmp(const char *p, const char *q)
|
||
|
{
|
||
|
return _stricmp(p, q);
|
||
|
}
|
||
|
|
||
|
char* strcpy_safe(char* pDst, uint dst_len, const char* pSrc)
|
||
|
{
|
||
|
CRNLIB_ASSERT(pDst && pSrc && dst_len);
|
||
|
if (!dst_len)
|
||
|
return pDst;
|
||
|
|
||
|
char* q = pDst;
|
||
|
char c;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if (dst_len == 1)
|
||
|
{
|
||
|
*q++ = '\0';
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
c = *pSrc++;
|
||
|
*q++ = c;
|
||
|
|
||
|
dst_len--;
|
||
|
|
||
|
} while (c);
|
||
|
|
||
|
CRNLIB_ASSERT((q - pDst) <= (int)dst_len);
|
||
|
|
||
|
return pDst;
|
||
|
}
|
||
|
|
||
|
bool int_to_string(int value, char* pDst, uint len)
|
||
|
{
|
||
|
CRNLIB_ASSERT(pDst);
|
||
|
|
||
|
const uint cBufSize = 16;
|
||
|
char buf[cBufSize];
|
||
|
|
||
|
uint j = static_cast<uint>((value < 0) ? -value : value);
|
||
|
|
||
|
char* p = buf + cBufSize - 1;
|
||
|
|
||
|
*p-- = '\0';
|
||
|
|
||
|
do
|
||
|
{
|
||
|
*p-- = static_cast<uint8>('0' + (j % 10));
|
||
|
j /= 10;
|
||
|
} while (j);
|
||
|
|
||
|
if (value < 0)
|
||
|
*p-- = '-';
|
||
|
|
||
|
const size_t total_bytes = (buf + cBufSize - 1) - p;
|
||
|
if (total_bytes > len)
|
||
|
return false;
|
||
|
|
||
|
for (size_t i = 0; i < total_bytes; i++)
|
||
|
pDst[i] = p[1 + i];
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool uint_to_string(uint value, char* pDst, uint len)
|
||
|
{
|
||
|
CRNLIB_ASSERT(pDst);
|
||
|
|
||
|
const uint cBufSize = 16;
|
||
|
char buf[cBufSize];
|
||
|
|
||
|
char* p = buf + cBufSize - 1;
|
||
|
|
||
|
*p-- = '\0';
|
||
|
|
||
|
do
|
||
|
{
|
||
|
*p-- = static_cast<uint8>('0' + (value % 10));
|
||
|
value /= 10;
|
||
|
} while (value);
|
||
|
|
||
|
const size_t total_bytes = (buf + cBufSize - 1) - p;
|
||
|
if (total_bytes > len)
|
||
|
return false;
|
||
|
|
||
|
for (size_t i = 0; i < total_bytes; i++)
|
||
|
pDst[i] = p[1 + i];
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool string_to_int(const char*& pBuf, int& value)
|
||
|
{
|
||
|
value = 0;
|
||
|
|
||
|
CRNLIB_ASSERT(pBuf);
|
||
|
const char* p = pBuf;
|
||
|
|
||
|
while (*p && isspace(*p))
|
||
|
p++;
|
||
|
|
||
|
uint result = 0;
|
||
|
bool negative = false;
|
||
|
|
||
|
if (!isdigit(*p))
|
||
|
{
|
||
|
if (p[0] == '-')
|
||
|
{
|
||
|
negative = true;
|
||
|
p++;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
while (*p && isdigit(*p))
|
||
|
{
|
||
|
if (result & 0xE0000000U)
|
||
|
return false;
|
||
|
|
||
|
const uint result8 = result << 3U;
|
||
|
const uint result2 = result << 1U;
|
||
|
|
||
|
if (result2 > (0xFFFFFFFFU - result8))
|
||
|
return false;
|
||
|
|
||
|
result = result8 + result2;
|
||
|
|
||
|
uint c = p[0] - '0';
|
||
|
if (c > (0xFFFFFFFFU - result))
|
||
|
return false;
|
||
|
|
||
|
result += c;
|
||
|
|
||
|
p++;
|
||
|
}
|
||
|
|
||
|
if (negative)
|
||
|
{
|
||
|
if (result > 0x80000000U)
|
||
|
{
|
||
|
value = 0;
|
||
|
return false;
|
||
|
}
|
||
|
value = -static_cast<int>(result);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (result > 0x7FFFFFFFU)
|
||
|
{
|
||
|
value = 0;
|
||
|
return false;
|
||
|
}
|
||
|
value = static_cast<int>(result);
|
||
|
}
|
||
|
|
||
|
pBuf = p;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool string_to_int64(const char*& pBuf, int64& value)
|
||
|
{
|
||
|
value = 0;
|
||
|
|
||
|
CRNLIB_ASSERT(pBuf);
|
||
|
const char* p = pBuf;
|
||
|
|
||
|
while (*p && isspace(*p))
|
||
|
p++;
|
||
|
|
||
|
uint64 result = 0;
|
||
|
bool negative = false;
|
||
|
|
||
|
if (!isdigit(*p))
|
||
|
{
|
||
|
if (p[0] == '-')
|
||
|
{
|
||
|
negative = true;
|
||
|
p++;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
while (*p && isdigit(*p))
|
||
|
{
|
||
|
if (result & 0xE000000000000000ULL)
|
||
|
return false;
|
||
|
|
||
|
const uint64 result8 = result << 3U;
|
||
|
const uint64 result2 = result << 1U;
|
||
|
|
||
|
if (result2 > (0xFFFFFFFFFFFFFFFFULL - result8))
|
||
|
return false;
|
||
|
|
||
|
result = result8 + result2;
|
||
|
|
||
|
uint c = p[0] - '0';
|
||
|
if (c > (0xFFFFFFFFFFFFFFFFULL - result))
|
||
|
return false;
|
||
|
|
||
|
result += c;
|
||
|
|
||
|
p++;
|
||
|
}
|
||
|
|
||
|
if (negative)
|
||
|
{
|
||
|
if (result > 0x8000000000000000ULL)
|
||
|
{
|
||
|
value = 0;
|
||
|
return false;
|
||
|
}
|
||
|
value = -static_cast<int64>(result);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (result > 0x7FFFFFFFFFFFFFFFULL)
|
||
|
{
|
||
|
value = 0;
|
||
|
return false;
|
||
|
}
|
||
|
value = static_cast<int64>(result);
|
||
|
}
|
||
|
|
||
|
pBuf = p;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool string_to_uint(const char*& pBuf, uint& value)
|
||
|
{
|
||
|
value = 0;
|
||
|
|
||
|
CRNLIB_ASSERT(pBuf);
|
||
|
const char* p = pBuf;
|
||
|
|
||
|
while (*p && isspace(*p))
|
||
|
p++;
|
||
|
|
||
|
uint result = 0;
|
||
|
|
||
|
if (!isdigit(*p))
|
||
|
return false;
|
||
|
|
||
|
while (*p && isdigit(*p))
|
||
|
{
|
||
|
if (result & 0xE0000000U)
|
||
|
return false;
|
||
|
|
||
|
const uint result8 = result << 3U;
|
||
|
const uint result2 = result << 1U;
|
||
|
|
||
|
if (result2 > (0xFFFFFFFFU - result8))
|
||
|
return false;
|
||
|
|
||
|
result = result8 + result2;
|
||
|
|
||
|
uint c = p[0] - '0';
|
||
|
if (c > (0xFFFFFFFFU - result))
|
||
|
return false;
|
||
|
|
||
|
result += c;
|
||
|
|
||
|
p++;
|
||
|
}
|
||
|
|
||
|
value = result;
|
||
|
|
||
|
pBuf = p;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool string_to_uint64(const char*& pBuf, uint64& value)
|
||
|
{
|
||
|
value = 0;
|
||
|
|
||
|
CRNLIB_ASSERT(pBuf);
|
||
|
const char* p = pBuf;
|
||
|
|
||
|
while (*p && isspace(*p))
|
||
|
p++;
|
||
|
|
||
|
uint64 result = 0;
|
||
|
|
||
|
if (!isdigit(*p))
|
||
|
return false;
|
||
|
|
||
|
while (*p && isdigit(*p))
|
||
|
{
|
||
|
if (result & 0xE000000000000000ULL)
|
||
|
return false;
|
||
|
|
||
|
const uint64 result8 = result << 3U;
|
||
|
const uint64 result2 = result << 1U;
|
||
|
|
||
|
if (result2 > (0xFFFFFFFFFFFFFFFFULL - result8))
|
||
|
return false;
|
||
|
|
||
|
result = result8 + result2;
|
||
|
|
||
|
uint c = p[0] - '0';
|
||
|
if (c > (0xFFFFFFFFFFFFFFFFULL - result))
|
||
|
return false;
|
||
|
|
||
|
result += c;
|
||
|
|
||
|
p++;
|
||
|
}
|
||
|
|
||
|
value = result;
|
||
|
|
||
|
pBuf = p;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool string_to_bool(const char* p, bool& value)
|
||
|
{
|
||
|
CRNLIB_ASSERT(p);
|
||
|
|
||
|
value = false;
|
||
|
|
||
|
if (_stricmp(p, "false") == 0)
|
||
|
return true;
|
||
|
|
||
|
if (_stricmp(p, "true") == 0)
|
||
|
{
|
||
|
value = true;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
const char* q = p;
|
||
|
uint v;
|
||
|
if (string_to_uint(q, v))
|
||
|
{
|
||
|
if (!v)
|
||
|
return true;
|
||
|
else if (v == 1)
|
||
|
{
|
||
|
value = true;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool string_to_float(const char*& p, float& value, uint round_digit)
|
||
|
{
|
||
|
double d;
|
||
|
if (!string_to_double(p, d, round_digit))
|
||
|
{
|
||
|
value = 0;
|
||
|
return false;
|
||
|
}
|
||
|
value = static_cast<float>(d);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool string_to_double(const char*& p, double& value, uint round_digit)
|
||
|
{
|
||
|
return string_to_double(p, p + 128, value, round_digit);
|
||
|
}
|
||
|
|
||
|
// I wrote this approx. 20 years ago in C/assembly using a limited FP emulator package, so it's a bit crude.
|
||
|
bool string_to_double(const char*& p, const char *pEnd, double& value, uint round_digit)
|
||
|
{
|
||
|
CRNLIB_ASSERT(p);
|
||
|
|
||
|
value = 0;
|
||
|
|
||
|
enum { AF_BLANK = 1, AF_SIGN = 2, AF_DPOINT = 3, AF_BADCHAR = 4, AF_OVRFLOW = 5, AF_EXPONENT = 6, AF_NODIGITS = 7 };
|
||
|
int status = 0;
|
||
|
|
||
|
const char* buf = p;
|
||
|
|
||
|
int got_sign_flag = 0, got_dp_flag = 0, got_num_flag = 0;
|
||
|
int got_e_flag = 0, got_e_sign_flag = 0, e_sign = 0;
|
||
|
uint whole_count = 0, frac_count = 0;
|
||
|
|
||
|
double whole = 0, frac = 0, scale = 1, exponent = 1;
|
||
|
|
||
|
if (p >= pEnd)
|
||
|
{
|
||
|
status = AF_NODIGITS;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
|
||
|
while (*buf)
|
||
|
{
|
||
|
if (!isspace(*buf))
|
||
|
break;
|
||
|
if (++buf >= pEnd)
|
||
|
{
|
||
|
status = AF_NODIGITS;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p = buf;
|
||
|
|
||
|
while (*buf)
|
||
|
{
|
||
|
p = buf;
|
||
|
if (buf >= pEnd)
|
||
|
break;
|
||
|
|
||
|
int i = *buf++;
|
||
|
|
||
|
switch (i)
|
||
|
{
|
||
|
case 'e':
|
||
|
case 'E':
|
||
|
{
|
||
|
got_e_flag = 1;
|
||
|
goto exit_while;
|
||
|
}
|
||
|
case '+':
|
||
|
{
|
||
|
if ((got_num_flag) || (got_sign_flag))
|
||
|
{
|
||
|
status = AF_SIGN;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
|
||
|
got_sign_flag = 1;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case '-':
|
||
|
{
|
||
|
if ((got_num_flag) || (got_sign_flag))
|
||
|
{
|
||
|
status = AF_SIGN;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
|
||
|
got_sign_flag = -1;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case '.':
|
||
|
{
|
||
|
if (got_dp_flag)
|
||
|
{
|
||
|
status = AF_DPOINT;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
|
||
|
got_dp_flag = 1;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
if ((i < '0') || (i > '9'))
|
||
|
goto exit_while;
|
||
|
else
|
||
|
{
|
||
|
i -= '0';
|
||
|
|
||
|
got_num_flag = 1;
|
||
|
|
||
|
if (got_dp_flag)
|
||
|
{
|
||
|
if (frac_count < round_digit)
|
||
|
{
|
||
|
frac = frac * 10.0f + i;
|
||
|
|
||
|
scale = scale * 10.0f;
|
||
|
}
|
||
|
else if (frac_count == round_digit)
|
||
|
{
|
||
|
if (i >= 5) /* check for round */
|
||
|
frac = frac + 1.0f;
|
||
|
}
|
||
|
|
||
|
frac_count++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
whole = whole * 10.0f + i;
|
||
|
|
||
|
whole_count++;
|
||
|
|
||
|
if (whole > 1e+100)
|
||
|
{
|
||
|
status = AF_OVRFLOW;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exit_while:
|
||
|
|
||
|
if (got_e_flag)
|
||
|
{
|
||
|
if ((got_num_flag == 0) && (got_dp_flag))
|
||
|
{
|
||
|
status = AF_EXPONENT;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
|
||
|
int e = 0;
|
||
|
e_sign = 1;
|
||
|
got_num_flag = 0;
|
||
|
got_e_sign_flag = 0;
|
||
|
|
||
|
while (*buf)
|
||
|
{
|
||
|
p = buf;
|
||
|
if (buf >= pEnd)
|
||
|
break;
|
||
|
|
||
|
int i = *buf++;
|
||
|
|
||
|
if (i == '+')
|
||
|
{
|
||
|
if ((got_num_flag) || (got_e_sign_flag))
|
||
|
{
|
||
|
status = AF_EXPONENT;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
|
||
|
e_sign = 1;
|
||
|
got_e_sign_flag = 1;
|
||
|
}
|
||
|
else if (i == '-')
|
||
|
{
|
||
|
if ((got_num_flag) || (got_e_sign_flag))
|
||
|
{
|
||
|
status = AF_EXPONENT;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
|
||
|
e_sign = -1;
|
||
|
got_e_sign_flag = 1;
|
||
|
}
|
||
|
else if ((i >= '0') && (i <= '9'))
|
||
|
{
|
||
|
got_num_flag = 1;
|
||
|
|
||
|
if ((e = (e * 10) + (i - 48)) > 100)
|
||
|
{
|
||
|
status = AF_EXPONENT;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (int i = 1; i <= e; i++) /* compute 10^e */
|
||
|
exponent = exponent * 10.0f;
|
||
|
}
|
||
|
|
||
|
if (((whole_count + frac_count) == 0) && (got_e_flag == 0))
|
||
|
{
|
||
|
status = AF_NODIGITS;
|
||
|
goto af_exit;
|
||
|
}
|
||
|
|
||
|
if (frac)
|
||
|
whole = whole + (frac / scale);
|
||
|
|
||
|
if (got_e_flag)
|
||
|
{
|
||
|
if (e_sign > 0)
|
||
|
whole = whole * exponent;
|
||
|
else
|
||
|
whole = whole / exponent;
|
||
|
}
|
||
|
|
||
|
if (got_sign_flag < 0)
|
||
|
whole = -whole;
|
||
|
|
||
|
value = whole;
|
||
|
|
||
|
af_exit:
|
||
|
return (status == 0);
|
||
|
}
|
||
|
|
||
|
} // namespace crnlib
|