508 lines
17 KiB
C
508 lines
17 KiB
C
|
/***************************************************************************************************
|
||
|
|
||
|
Zyan Core Library (Zycore-C)
|
||
|
|
||
|
Original Author : Florian Bernd
|
||
|
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in all
|
||
|
* copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
|
||
|
***************************************************************************************************/
|
||
|
|
||
|
#include <Zycore/Format.h>
|
||
|
#include <Zycore/LibC.h>
|
||
|
|
||
|
/* ============================================================================================== */
|
||
|
/* Constants */
|
||
|
/* ============================================================================================== */
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
/* Defines */
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
#define ZYCORE_MAXCHARS_DEC_32 10
|
||
|
#define ZYCORE_MAXCHARS_DEC_64 20
|
||
|
#define ZYCORE_MAXCHARS_HEX_32 8
|
||
|
#define ZYCORE_MAXCHARS_HEX_64 16
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
/* Lookup Tables */
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
static const char* const DECIMAL_LOOKUP =
|
||
|
"00010203040506070809"
|
||
|
"10111213141516171819"
|
||
|
"20212223242526272829"
|
||
|
"30313233343536373839"
|
||
|
"40414243444546474849"
|
||
|
"50515253545556575859"
|
||
|
"60616263646566676869"
|
||
|
"70717273747576777879"
|
||
|
"80818283848586878889"
|
||
|
"90919293949596979899";
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
/* Static strings */
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
static const ZyanStringView STR_ADD = ZYAN_DEFINE_STRING_VIEW("+");
|
||
|
static const ZyanStringView STR_SUB = ZYAN_DEFINE_STRING_VIEW("-");
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
/* ============================================================================================== */
|
||
|
/* Internal macros */
|
||
|
/* ============================================================================================== */
|
||
|
|
||
|
/**
|
||
|
* Writes a terminating '\0' character at the end of the string data.
|
||
|
*/
|
||
|
#define ZYCORE_STRING_NULLTERMINATE(string) \
|
||
|
*(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
|
||
|
|
||
|
/* ============================================================================================== */
|
||
|
/* Internal functions */
|
||
|
/* ============================================================================================== */
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
/* Decimal */
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN)
|
||
|
ZyanStatus ZyanStringAppendDecU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length)
|
||
|
{
|
||
|
if (!string)
|
||
|
{
|
||
|
return ZYAN_STATUS_INVALID_ARGUMENT;
|
||
|
}
|
||
|
|
||
|
char buffer[ZYCORE_MAXCHARS_DEC_32];
|
||
|
char *buffer_end = &buffer[ZYCORE_MAXCHARS_DEC_32];
|
||
|
char *buffer_write_pointer = buffer_end;
|
||
|
while (value >= 100)
|
||
|
{
|
||
|
const ZyanU32 value_old = value;
|
||
|
buffer_write_pointer -= 2;
|
||
|
value /= 100;
|
||
|
ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[(value_old - (value * 100)) * 2], 2);
|
||
|
}
|
||
|
buffer_write_pointer -= 2;
|
||
|
ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[value * 2], 2);
|
||
|
|
||
|
const ZyanUSize offset_odd = (ZyanUSize)(value < 10);
|
||
|
const ZyanUSize length_number = buffer_end - buffer_write_pointer - offset_odd;
|
||
|
const ZyanUSize length_total = ZYAN_MAX(length_number, padding_length);
|
||
|
const ZyanUSize length_target = string->vector.size;
|
||
|
|
||
|
if (string->vector.size + length_total > string->vector.capacity)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringResize(string, string->vector.size + length_total - 1));
|
||
|
}
|
||
|
|
||
|
ZyanUSize offset_write = 0;
|
||
|
if (padding_length > length_number)
|
||
|
{
|
||
|
offset_write = padding_length - length_number;
|
||
|
ZYAN_MEMSET((char*)string->vector.data + length_target - 1, '0', offset_write);
|
||
|
}
|
||
|
|
||
|
ZYAN_MEMCPY((char*)string->vector.data + length_target + offset_write - 1,
|
||
|
buffer_write_pointer + offset_odd, length_number);
|
||
|
string->vector.size = length_target + length_total;
|
||
|
ZYCORE_STRING_NULLTERMINATE(string);
|
||
|
|
||
|
return ZYAN_STATUS_SUCCESS;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
ZyanStatus ZyanStringAppendDecU64(ZyanString* string, ZyanU64 value, ZyanU8 padding_length)
|
||
|
{
|
||
|
if (!string)
|
||
|
{
|
||
|
return ZYAN_STATUS_INVALID_ARGUMENT;
|
||
|
}
|
||
|
|
||
|
char buffer[ZYCORE_MAXCHARS_DEC_64];
|
||
|
char *buffer_end = &buffer[ZYCORE_MAXCHARS_DEC_64];
|
||
|
char *buffer_write_pointer = buffer_end;
|
||
|
while (value >= 100)
|
||
|
{
|
||
|
const ZyanU64 value_old = value;
|
||
|
buffer_write_pointer -= 2;
|
||
|
value /= 100;
|
||
|
ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[(value_old - (value * 100)) * 2], 2);
|
||
|
}
|
||
|
buffer_write_pointer -= 2;
|
||
|
ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[value * 2], 2);
|
||
|
|
||
|
const ZyanUSize offset_odd = (ZyanUSize)(value < 10);
|
||
|
const ZyanUSize length_number = buffer_end - buffer_write_pointer - offset_odd;
|
||
|
const ZyanUSize length_total = ZYAN_MAX(length_number, padding_length);
|
||
|
const ZyanUSize length_target = string->vector.size;
|
||
|
|
||
|
if (string->vector.size + length_total > string->vector.capacity)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringResize(string, string->vector.size + length_total - 1));
|
||
|
}
|
||
|
|
||
|
ZyanUSize offset_write = 0;
|
||
|
if (padding_length > length_number)
|
||
|
{
|
||
|
offset_write = padding_length - length_number;
|
||
|
ZYAN_MEMSET((char*)string->vector.data + length_target - 1, '0', offset_write);
|
||
|
}
|
||
|
|
||
|
ZYAN_MEMCPY((char*)string->vector.data + length_target + offset_write - 1,
|
||
|
buffer_write_pointer + offset_odd, length_number);
|
||
|
string->vector.size = length_target + length_total;
|
||
|
ZYCORE_STRING_NULLTERMINATE(string);
|
||
|
|
||
|
return ZYAN_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
/* Hexadecimal */
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
#if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN)
|
||
|
ZyanStatus ZyanStringAppendHexU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length,
|
||
|
ZyanBool uppercase)
|
||
|
{
|
||
|
if (!string)
|
||
|
{
|
||
|
return ZYAN_STATUS_INVALID_ARGUMENT;
|
||
|
}
|
||
|
|
||
|
const ZyanUSize len = string->vector.size;
|
||
|
ZyanUSize remaining = string->vector.capacity - string->vector.size;
|
||
|
|
||
|
if (remaining < (ZyanUSize)padding_length)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringResize(string, len + padding_length - 1));
|
||
|
remaining = padding_length;
|
||
|
}
|
||
|
|
||
|
if (!value)
|
||
|
{
|
||
|
const ZyanU8 n = (padding_length ? padding_length : 1);
|
||
|
|
||
|
if (remaining < (ZyanUSize)n)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringResize(string, string->vector.size + n - 1));
|
||
|
}
|
||
|
|
||
|
ZYAN_MEMSET((char*)string->vector.data + len - 1, '0', n);
|
||
|
string->vector.size = len + n;
|
||
|
ZYCORE_STRING_NULLTERMINATE(string);
|
||
|
|
||
|
return ZYAN_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
ZyanU8 n = 0;
|
||
|
char* buffer = ZYAN_NULL;
|
||
|
for (ZyanI8 i = ZYCORE_MAXCHARS_HEX_32 - 1; i >= 0; --i)
|
||
|
{
|
||
|
const ZyanU8 v = (value >> i * 4) & 0x0F;
|
||
|
if (!n)
|
||
|
{
|
||
|
if (!v)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if (remaining <= (ZyanU8)i)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringResize(string, string->vector.size + i));
|
||
|
}
|
||
|
buffer = (char*)string->vector.data + len - 1;
|
||
|
if (padding_length > i)
|
||
|
{
|
||
|
n = padding_length - i - 1;
|
||
|
ZYAN_MEMSET(buffer, '0', n);
|
||
|
}
|
||
|
}
|
||
|
ZYAN_ASSERT(buffer);
|
||
|
if (uppercase)
|
||
|
{
|
||
|
buffer[n++] = "0123456789ABCDEF"[v];
|
||
|
} else
|
||
|
{
|
||
|
buffer[n++] = "0123456789abcdef"[v];
|
||
|
}
|
||
|
}
|
||
|
string->vector.size = len + n;
|
||
|
ZYCORE_STRING_NULLTERMINATE(string);
|
||
|
|
||
|
return ZYAN_STATUS_SUCCESS;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
ZyanStatus ZyanStringAppendHexU64(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
|
||
|
ZyanBool uppercase)
|
||
|
{
|
||
|
if (!string)
|
||
|
{
|
||
|
return ZYAN_STATUS_INVALID_ARGUMENT;
|
||
|
}
|
||
|
|
||
|
const ZyanUSize len = string->vector.size;
|
||
|
ZyanUSize remaining = string->vector.capacity - string->vector.size;
|
||
|
|
||
|
if (remaining < (ZyanUSize)padding_length)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringResize(string, len + padding_length - 1));
|
||
|
remaining = padding_length;
|
||
|
}
|
||
|
|
||
|
if (!value)
|
||
|
{
|
||
|
const ZyanU8 n = (padding_length ? padding_length : 1);
|
||
|
|
||
|
if (remaining < (ZyanUSize)n)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringResize(string, string->vector.size + n - 1));
|
||
|
}
|
||
|
|
||
|
ZYAN_MEMSET((char*)string->vector.data + len - 1, '0', n);
|
||
|
string->vector.size = len + n;
|
||
|
ZYCORE_STRING_NULLTERMINATE(string);
|
||
|
|
||
|
return ZYAN_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
ZyanU8 n = 0;
|
||
|
char* buffer = ZYAN_NULL;
|
||
|
for (ZyanI8 i = ((value & 0xFFFFFFFF00000000) ?
|
||
|
ZYCORE_MAXCHARS_HEX_64 : ZYCORE_MAXCHARS_HEX_32) - 1; i >= 0; --i)
|
||
|
{
|
||
|
const ZyanU8 v = (value >> i * 4) & 0x0F;
|
||
|
if (!n)
|
||
|
{
|
||
|
if (!v)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if (remaining <= (ZyanU8)i)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringResize(string, string->vector.size + i));
|
||
|
}
|
||
|
buffer = (char*)string->vector.data + len - 1;
|
||
|
if (padding_length > i)
|
||
|
{
|
||
|
n = padding_length - i - 1;
|
||
|
ZYAN_MEMSET(buffer, '0', n);
|
||
|
}
|
||
|
}
|
||
|
ZYAN_ASSERT(buffer);
|
||
|
if (uppercase)
|
||
|
{
|
||
|
buffer[n++] = "0123456789ABCDEF"[v];
|
||
|
} else
|
||
|
{
|
||
|
buffer[n++] = "0123456789abcdef"[v];
|
||
|
}
|
||
|
}
|
||
|
string->vector.size = len + n;
|
||
|
ZYCORE_STRING_NULLTERMINATE(string);
|
||
|
|
||
|
return ZYAN_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
/* ============================================================================================== */
|
||
|
/* Exported functions */
|
||
|
/* ============================================================================================== */
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
/* Insertion */
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
//ZyanStatus ZyanStringInsertFormat(ZyanString* string, ZyanUSize index, const char* format, ...)
|
||
|
//{
|
||
|
//
|
||
|
//}
|
||
|
//
|
||
|
///* ---------------------------------------------------------------------------------------------- */
|
||
|
//
|
||
|
//ZyanStatus ZyanStringInsertDecU(ZyanString* string, ZyanUSize index, ZyanU64 value,
|
||
|
// ZyanUSize padding_length)
|
||
|
//{
|
||
|
//
|
||
|
//}
|
||
|
//
|
||
|
//ZyanStatus ZyanStringInsertDecS(ZyanString* string, ZyanUSize index, ZyanI64 value,
|
||
|
// ZyanUSize padding_length, ZyanBool force_sign, const ZyanString* prefix)
|
||
|
//{
|
||
|
//
|
||
|
//}
|
||
|
//
|
||
|
//ZyanStatus ZyanStringInsertHexU(ZyanString* string, ZyanUSize index, ZyanU64 value,
|
||
|
// ZyanUSize padding_length, ZyanBool uppercase)
|
||
|
//{
|
||
|
//
|
||
|
//}
|
||
|
//
|
||
|
//ZyanStatus ZyanStringInsertHexS(ZyanString* string, ZyanUSize index, ZyanI64 value,
|
||
|
// ZyanUSize padding_length, ZyanBool uppercase, ZyanBool force_sign, const ZyanString* prefix)
|
||
|
//{
|
||
|
//
|
||
|
//}
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
/* Appending */
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
#ifndef ZYAN_NO_LIBC
|
||
|
|
||
|
ZyanStatus ZyanStringAppendFormat(ZyanString* string, const char* format, ...)
|
||
|
{
|
||
|
if (!string || !format)
|
||
|
{
|
||
|
return ZYAN_STATUS_INVALID_ARGUMENT;
|
||
|
}
|
||
|
|
||
|
ZyanVAList arglist;
|
||
|
ZYAN_VA_START(arglist, format);
|
||
|
|
||
|
const ZyanUSize len = string->vector.size;
|
||
|
|
||
|
ZyanI32 w = ZYAN_VSNPRINTF((char*)string->vector.data + len - 1,
|
||
|
string->vector.capacity - len + 1, format, arglist);
|
||
|
if (w < 0)
|
||
|
{
|
||
|
ZYAN_VA_END(arglist);
|
||
|
return ZYAN_STATUS_FAILED;
|
||
|
}
|
||
|
if (w <= (ZyanI32)(string->vector.capacity - len))
|
||
|
{
|
||
|
string->vector.size = len + w;
|
||
|
|
||
|
ZYAN_VA_END(arglist);
|
||
|
return ZYAN_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// The remaining capacity was not sufficent to fit the formatted string. Trying to resize ..
|
||
|
const ZyanStatus status = ZyanStringResize(string, string->vector.size + w - 1);
|
||
|
if (!ZYAN_SUCCESS(status))
|
||
|
{
|
||
|
ZYAN_VA_END(arglist);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
w = ZYAN_VSNPRINTF((char*)string->vector.data + len - 1,
|
||
|
string->vector.capacity - string->vector.size + 1, format, arglist);
|
||
|
if (w < 0)
|
||
|
{
|
||
|
ZYAN_VA_END(arglist);
|
||
|
return ZYAN_STATUS_FAILED;
|
||
|
}
|
||
|
ZYAN_ASSERT(w <= (ZyanI32)(string->vector.capacity - string->vector.size));
|
||
|
|
||
|
ZYAN_VA_END(arglist);
|
||
|
return ZYAN_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
#endif // ZYAN_NO_LIBC
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
ZyanStatus ZyanStringAppendDecU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length)
|
||
|
{
|
||
|
#if defined(ZYAN_X64) || defined(ZYAN_AARCH64)
|
||
|
return ZyanStringAppendDecU64(string, value, padding_length);
|
||
|
#else
|
||
|
// Working with 64-bit values is slow on non 64-bit systems
|
||
|
if (value & 0xFFFFFFFF00000000)
|
||
|
{
|
||
|
return ZyanStringAppendDecU64(string, value, padding_length);
|
||
|
}
|
||
|
return ZyanStringAppendDecU32(string, (ZyanU32)value, padding_length);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
ZyanStatus ZyanStringAppendDecS(ZyanString* string, ZyanI64 value, ZyanU8 padding_length,
|
||
|
ZyanBool force_sign, const ZyanStringView* prefix)
|
||
|
{
|
||
|
if (value < 0)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringAppend(string, &STR_SUB));
|
||
|
if (prefix)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringAppend(string, prefix));
|
||
|
}
|
||
|
return ZyanStringAppendDecU(string, ZyanAbsI64(value), padding_length);
|
||
|
}
|
||
|
|
||
|
if (force_sign)
|
||
|
{
|
||
|
ZYAN_ASSERT(value >= 0);
|
||
|
ZYAN_CHECK(ZyanStringAppend(string, &STR_ADD));
|
||
|
}
|
||
|
|
||
|
if (prefix)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringAppend(string, prefix));
|
||
|
}
|
||
|
return ZyanStringAppendDecU(string, value, padding_length);
|
||
|
}
|
||
|
|
||
|
ZyanStatus ZyanStringAppendHexU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
|
||
|
ZyanBool uppercase)
|
||
|
{
|
||
|
#if defined(ZYAN_X64) || defined(ZYAN_AARCH64)
|
||
|
return ZyanStringAppendHexU64(string, value, padding_length, uppercase);
|
||
|
#else
|
||
|
// Working with 64-bit values is slow on non 64-bit systems
|
||
|
if (value & 0xFFFFFFFF00000000)
|
||
|
{
|
||
|
return ZyanStringAppendHexU64(string, value, padding_length, uppercase);
|
||
|
}
|
||
|
return ZyanStringAppendHexU32(string, (ZyanU32)value, padding_length, uppercase);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
ZyanStatus ZyanStringAppendHexS(ZyanString* string, ZyanI64 value, ZyanU8 padding_length,
|
||
|
ZyanBool uppercase, ZyanBool force_sign, const ZyanStringView* prefix)
|
||
|
{
|
||
|
if (value < 0)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringAppend(string, &STR_SUB));
|
||
|
if (prefix)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringAppend(string, prefix));
|
||
|
}
|
||
|
return ZyanStringAppendHexU(string, ZyanAbsI64(value), padding_length, uppercase);
|
||
|
}
|
||
|
|
||
|
if (force_sign)
|
||
|
{
|
||
|
ZYAN_ASSERT(value >= 0);
|
||
|
ZYAN_CHECK(ZyanStringAppend(string, &STR_ADD));
|
||
|
}
|
||
|
|
||
|
if (prefix)
|
||
|
{
|
||
|
ZYAN_CHECK(ZyanStringAppend(string, prefix));
|
||
|
}
|
||
|
return ZyanStringAppendHexU(string, value, padding_length, uppercase);
|
||
|
}
|
||
|
|
||
|
/* ---------------------------------------------------------------------------------------------- */
|
||
|
|
||
|
/* ============================================================================================== */
|