365 lines
12 KiB
C++
365 lines
12 KiB
C++
#define DN_CONVERT_CPP
|
|
|
|
DN_API int DN_CVT_FmtBuffer3DotTruncate(char *buffer, int size, DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
int size_required = DN_VSNPrintF(buffer, size, fmt, args);
|
|
int result = DN_Max(DN_Min(size_required, size - 1), 0);
|
|
if (result == size - 1) {
|
|
buffer[size - 2] = '.';
|
|
buffer[size - 3] = '.';
|
|
}
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_CVTU64Str8 DN_CVT_U64ToStr8(uint64_t val, char separator)
|
|
{
|
|
DN_CVTU64Str8 result = {};
|
|
if (val == 0) {
|
|
result.data[result.size++] = '0';
|
|
} else {
|
|
// NOTE: The number is written in reverse because we form the string by
|
|
// dividing by 10, so we write it in, then reverse it out after all is
|
|
// done.
|
|
DN_CVTU64Str8 temp = {};
|
|
for (DN_USize digit_count = 0; val > 0; digit_count++) {
|
|
if (separator && (digit_count != 0) && (digit_count % 3 == 0))
|
|
temp.data[temp.size++] = separator;
|
|
|
|
auto digit = DN_CAST(char)(val % 10);
|
|
temp.data[temp.size++] = '0' + digit;
|
|
val /= 10;
|
|
}
|
|
|
|
// NOTE: Reverse the string
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(6293) // Ill-defined for-loop
|
|
DN_MSVC_WARNING_DISABLE(6385) // Reading invalid data from 'temp.data' unsigned overflow is valid for loop termination
|
|
for (DN_USize temp_index = temp.size - 1; temp_index < temp.size; temp_index--) {
|
|
char ch = temp.data[temp_index];
|
|
result.data[result.size++] = ch;
|
|
}
|
|
DN_MSVC_WARNING_POP
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_CVTU64ByteSize DN_CVT_U64ToByteSize(uint64_t bytes, DN_CVTU64ByteSizeType desired_type)
|
|
{
|
|
DN_CVTU64ByteSize result = {};
|
|
result.bytes = DN_CAST(DN_F64) bytes;
|
|
if (!DN_Check(desired_type != DN_CVTU64ByteSizeType_Count)) {
|
|
result.suffix = DN_CVT_U64ByteSizeTypeString(result.type);
|
|
return result;
|
|
}
|
|
|
|
if (desired_type == DN_CVTU64ByteSizeType_Auto)
|
|
for (; result.type < DN_CVTU64ByteSizeType_Count && result.bytes >= 1024.0; result.type = DN_CAST(DN_CVTU64ByteSizeType)(DN_CAST(DN_USize) result.type + 1))
|
|
result.bytes /= 1024.0;
|
|
else
|
|
for (; result.type < desired_type; result.type = DN_CAST(DN_CVTU64ByteSizeType)(DN_CAST(DN_USize) result.type + 1))
|
|
result.bytes /= 1024.0;
|
|
|
|
result.suffix = DN_CVT_U64ByteSizeTypeString(result.type);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_CVT_U64ToByteSizeStr8(DN_Arena *arena, uint64_t bytes, DN_CVTU64ByteSizeType desired_type)
|
|
{
|
|
DN_CVTU64ByteSize byte_size = DN_CVT_U64ToByteSize(bytes, desired_type);
|
|
DN_Str8 result = DN_Str8_InitF(arena, "%.2f%.*s", byte_size.bytes, DN_STR_FMT(byte_size.suffix));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_CVT_U64ByteSizeTypeString(DN_CVTU64ByteSizeType type)
|
|
{
|
|
DN_Str8 result = DN_STR8("");
|
|
switch (type) {
|
|
case DN_CVTU64ByteSizeType_B: result = DN_STR8("B"); break;
|
|
case DN_CVTU64ByteSizeType_KiB: result = DN_STR8("KiB"); break;
|
|
case DN_CVTU64ByteSizeType_MiB: result = DN_STR8("MiB"); break;
|
|
case DN_CVTU64ByteSizeType_GiB: result = DN_STR8("GiB"); break;
|
|
case DN_CVTU64ByteSizeType_TiB: result = DN_STR8("TiB"); break;
|
|
case DN_CVTU64ByteSizeType_Count: result = DN_STR8(""); break;
|
|
case DN_CVTU64ByteSizeType_Auto: result = DN_STR8(""); break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_CVT_U64ToAge(DN_Arena *arena, DN_U64 age_s, DN_CVTU64AgeUnit unit)
|
|
{
|
|
DN_Str8 result = {};
|
|
if (!arena)
|
|
return result;
|
|
|
|
char buffer[512];
|
|
DN_Arena stack_arena = DN_Arena_InitFromBuffer(buffer, sizeof(buffer), DN_ArenaFlags_NoPoison);
|
|
DN_Str8Builder builder = DN_Str8Builder_Init(&stack_arena);
|
|
DN_U64 remainder = age_s;
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Year) {
|
|
DN_USize value = remainder / DN_YearsToSec(1);
|
|
remainder -= DN_YearsToSec(value);
|
|
if (value)
|
|
DN_Str8Builder_AppendF(&builder, "%s%zuyr", builder.string_size ? " " : "", value);
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Week) {
|
|
DN_USize value = remainder / DN_WeeksToSec(1);
|
|
remainder -= DN_WeeksToSec(value);
|
|
if (value)
|
|
DN_Str8Builder_AppendF(&builder, "%s%zuw", builder.string_size ? " " : "", value);
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Day) {
|
|
DN_USize value = remainder / DN_DaysToSec(1);
|
|
remainder -= DN_DaysToSec(value);
|
|
if (value)
|
|
DN_Str8Builder_AppendF(&builder, "%s%zud", builder.string_size ? " " : "", value);
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Hr) {
|
|
DN_USize value = remainder / DN_HoursToSec(1);
|
|
remainder -= DN_HoursToSec(value);
|
|
if (value)
|
|
DN_Str8Builder_AppendF(&builder, "%s%zuh", builder.string_size ? " " : "", value);
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Min) {
|
|
DN_USize value = remainder / DN_MinutesToSec(1);
|
|
remainder -= DN_MinutesToSec(value);
|
|
if (value)
|
|
DN_Str8Builder_AppendF(&builder, "%s%zum", builder.string_size ? " " : "", value);
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Sec) {
|
|
DN_USize value = remainder;
|
|
if (value || builder.string_size == 0)
|
|
DN_Str8Builder_AppendF(&builder, "%s%zus", builder.string_size ? " " : "", value);
|
|
}
|
|
|
|
result = DN_Str8Builder_Build(&builder, arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_CVT_F64ToAge(DN_Arena *arena, DN_F64 age_s, DN_CVTU64AgeUnit unit)
|
|
{
|
|
DN_Str8 result = {};
|
|
if (!arena)
|
|
return result;
|
|
|
|
char buffer[128];
|
|
DN_Arena stack_arena = DN_Arena_InitFromBuffer(buffer, sizeof(buffer), DN_ArenaFlags_NoPoison);
|
|
DN_Str8Builder builder = DN_Str8Builder_Init(&stack_arena);
|
|
DN_F64 remainder = age_s;
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Year) {
|
|
DN_F64 value = remainder / DN_CAST(DN_F64) DN_YearsToSec(1);
|
|
if (value >= 1.0) {
|
|
remainder -= DN_YearsToSec(value);
|
|
DN_Str8Builder_AppendF(&builder, "%s%.1fyr", builder.string_size ? " " : "", value);
|
|
}
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Week) {
|
|
DN_F64 value = remainder / DN_CAST(DN_F64) DN_WeeksToSec(1);
|
|
if (value >= 1.0) {
|
|
remainder -= DN_WeeksToSec(value);
|
|
DN_Str8Builder_AppendF(&builder, "%s%.1fw", builder.string_size ? " " : "", value);
|
|
}
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Day) {
|
|
DN_F64 value = remainder / DN_CAST(DN_F64) DN_DaysToSec(1);
|
|
if (value >= 1.0) {
|
|
remainder -= DN_WeeksToSec(value);
|
|
DN_Str8Builder_AppendF(&builder, "%s%.1fd", builder.string_size ? " " : "", value);
|
|
}
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Hr) {
|
|
DN_F64 value = remainder / DN_CAST(DN_F64) DN_HoursToSec(1);
|
|
if (value >= 1.0) {
|
|
remainder -= DN_HoursToSec(value);
|
|
DN_Str8Builder_AppendF(&builder, "%s%.1fh", builder.string_size ? " " : "", value);
|
|
}
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Min) {
|
|
DN_F64 value = remainder / DN_CAST(DN_F64) DN_MinutesToSec(1);
|
|
if (value >= 1.0) {
|
|
remainder -= DN_MinutesToSec(value);
|
|
DN_Str8Builder_AppendF(&builder, "%s%.1fm", builder.string_size ? " " : "", value);
|
|
}
|
|
}
|
|
|
|
if (unit & DN_CVTU64AgeUnit_Sec) {
|
|
DN_F64 value = remainder;
|
|
DN_Str8Builder_AppendF(&builder, "%s%.1fs", builder.string_size ? " " : "", value);
|
|
}
|
|
|
|
result = DN_Str8Builder_Build(&builder, arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API uint64_t DN_CVT_HexToU64(DN_Str8 hex)
|
|
{
|
|
DN_Str8 real_hex = DN_Str8_TrimPrefix(DN_Str8_TrimPrefix(hex, DN_STR8("0x")), DN_STR8("0X"));
|
|
DN_USize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/;
|
|
DN_Assert(real_hex.size <= max_hex_size);
|
|
|
|
DN_USize size = DN_Min(max_hex_size, real_hex.size);
|
|
uint64_t result = 0;
|
|
for (DN_USize index = 0; index < size; index++) {
|
|
char ch = real_hex.data[index];
|
|
DN_CharHexToU8 val = DN_Char_HexToU8(ch);
|
|
if (!val.success)
|
|
break;
|
|
result = (result << 4) | val.value;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_CVT_U64ToHex(DN_Arena *arena, uint64_t number, uint32_t flags)
|
|
{
|
|
DN_Str8 prefix = {};
|
|
if ((flags & DN_CVTHexU64Str8Flags_0xPrefix))
|
|
prefix = DN_STR8("0x");
|
|
|
|
char const *fmt = (flags & DN_CVTHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
|
DN_USize required_size = DN_CStr8_FSize(fmt, number) + prefix.size;
|
|
DN_Str8 result = DN_Str8_Alloc(arena, required_size, DN_ZeroMem_No);
|
|
|
|
if (DN_Str8_HasData(result)) {
|
|
DN_Memcpy(result.data, prefix.data, prefix.size);
|
|
int space = DN_CAST(int) DN_Max((result.size - prefix.size) + 1, 0); /*null-terminator*/
|
|
DN_SNPrintF(result.data + prefix.size, space, fmt, number);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_CVTU64HexStr8 DN_CVT_U64ToHexStr8(uint64_t number, DN_CVTHexU64Str8Flags flags)
|
|
{
|
|
DN_Str8 prefix = {};
|
|
if (flags & DN_CVTHexU64Str8Flags_0xPrefix)
|
|
prefix = DN_STR8("0x");
|
|
|
|
DN_CVTU64HexStr8 result = {};
|
|
DN_Memcpy(result.data, prefix.data, prefix.size);
|
|
result.size += DN_CAST(int8_t) prefix.size;
|
|
|
|
char const *fmt = (flags & DN_CVTHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
|
int size = DN_SNPrintF(result.data + result.size, DN_ArrayCountU(result.data) - result.size, fmt, number);
|
|
result.size += DN_CAST(uint8_t) size;
|
|
DN_Assert(result.size < DN_ArrayCountU(result.data));
|
|
|
|
// NOTE: snprintf returns the required size of the format string
|
|
// irrespective of if there's space or not, but, always null terminates so
|
|
// the last byte is wasted.
|
|
result.size = DN_Min(result.size, DN_ArrayCountU(result.data) - 1);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CVT_BytesToHexPtr(void const *src, DN_USize src_size, char *dest, DN_USize dest_size)
|
|
{
|
|
if (!src || !dest)
|
|
return false;
|
|
|
|
if (!DN_Check(dest_size >= src_size * 2))
|
|
return false;
|
|
|
|
char const *HEX = "0123456789abcdef";
|
|
unsigned char const *src_u8 = DN_CAST(unsigned char const *) src;
|
|
for (DN_USize src_index = 0, dest_index = 0; src_index < src_size; src_index++) {
|
|
char byte = src_u8[src_index];
|
|
char hex01 = (byte >> 4) & 0b1111;
|
|
char hex02 = (byte >> 0) & 0b1111;
|
|
dest[dest_index++] = HEX[(int)hex01];
|
|
dest[dest_index++] = HEX[(int)hex02];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_CVT_BytesToHex(DN_Arena *arena, void const *src, DN_USize size)
|
|
{
|
|
DN_Str8 result = {};
|
|
if (!src || size <= 0)
|
|
return result;
|
|
|
|
result = DN_Str8_Alloc(arena, size * 2, DN_ZeroMem_No);
|
|
result.data[result.size] = 0;
|
|
bool converted = DN_CVT_BytesToHexPtr(src, size, result.data, result.size);
|
|
DN_Assert(converted);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_CVT_HexToBytesPtrUnchecked(DN_Str8 hex, void *dest, DN_USize dest_size)
|
|
{
|
|
DN_USize result = 0;
|
|
unsigned char *dest_u8 = DN_CAST(unsigned char *) dest;
|
|
|
|
for (DN_USize hex_index = 0; hex_index < hex.size; hex_index += 2, result += 1) {
|
|
char hex01 = hex.data[hex_index];
|
|
char hex02 = (hex_index + 1 < hex.size) ? hex.data[hex_index + 1] : 0;
|
|
char bit4_01 = DN_Char_HexToU8(hex01).value;
|
|
char bit4_02 = DN_Char_HexToU8(hex02).value;
|
|
char byte = (bit4_01 << 4) | (bit4_02 << 0);
|
|
dest_u8[result] = byte;
|
|
}
|
|
|
|
DN_Assert(result <= dest_size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_CVT_HexToBytesPtr(DN_Str8 hex, void *dest, DN_USize dest_size)
|
|
{
|
|
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0x"));
|
|
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0X"));
|
|
|
|
DN_USize result = 0;
|
|
if (!DN_Str8_HasData(hex))
|
|
return result;
|
|
|
|
// NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB"
|
|
// Either way, the size can be odd or even, hence we round up to the nearest
|
|
// multiple of two to ensure that we calculate the min buffer size orrectly.
|
|
DN_USize hex_size_rounded_up = hex.size + (hex.size % 2);
|
|
DN_USize min_buffer_size = hex_size_rounded_up / 2;
|
|
if (hex.size <= 0 || !DN_Check(dest_size >= min_buffer_size))
|
|
return result;
|
|
|
|
result = DN_CVT_HexToBytesPtrUnchecked(hex, dest, dest_size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_CVT_HexToBytesUnchecked(DN_Arena *arena, DN_Str8 hex)
|
|
{
|
|
DN_USize hex_size_rounded_up = hex.size + (hex.size % 2);
|
|
DN_Str8 result = DN_Str8_Alloc(arena, (hex_size_rounded_up / 2), DN_ZeroMem_No);
|
|
if (result.data) {
|
|
DN_USize bytes_written = DN_CVT_HexToBytesPtr(hex, result.data, result.size);
|
|
DN_Assert(bytes_written == result.size);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_CVT_HexToBytes(DN_Arena *arena, DN_Str8 hex)
|
|
{
|
|
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0x"));
|
|
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0X"));
|
|
|
|
DN_Str8 result = {};
|
|
if (!DN_Str8_HasData(hex))
|
|
return result;
|
|
|
|
if (!DN_Check(DN_Str8_IsAll(hex, DN_Str8IsAll_Hex)))
|
|
return result;
|
|
|
|
result = DN_CVT_HexToBytesUnchecked(arena, hex);
|
|
return result;
|
|
}
|