Dengine/src/Common.c

354 lines
7.7 KiB
C

#include <stdlib.h>
#include "Dengine/Common.h"
#include "Dengine/MemoryArena.h"
void common_optimalArrayV2Create(OptimalArrayV2 *array)
{
array->ptr = array->fastStorage;
array->size = ARRAY_COUNT(array->fastStorage);
}
i32 common_optimalArrayV2Push(OptimalArrayV2 *array, v2 data)
{
if (array->index + 1 > array->size)
{
array->size += ARRAY_COUNT(array->fastStorage);
i32 newSizeInBytes = array->size * sizeof(v2);
/* If first time expanding, we need to manually malloc and copy */
if (array->ptr == array->fastStorage)
{
array->ptr = malloc(newSizeInBytes);
for (i32 i = 0; i < ARRAY_COUNT(array->fastStorage); i++)
{
array->ptr[i] = array->fastStorage[i];
}
}
else
{
array->ptr = realloc(array->ptr, newSizeInBytes);
}
if (!array->ptr) return optimalarrayerror_out_of_memory;
}
array->ptr[array->index++] = data;
return 0;
}
void common_optimalArrayV2Destroy(OptimalArrayV2 *array)
{
if (array->ptr != array->fastStorage)
{
free(array->ptr);
}
}
/*
* +-------------------------------------+
* | Header | C-String | Null Terminator |
* +-------------------------------------+
* |
* +--> Functions return the c-string for compatibility with other
* string libraries
*
* Headers are retrieved using pointer arithmetric from the C string. These
* strings are typechecked by their own typedef char String.
*/
typedef struct StringHeader
{
i32 len;
// NOTE(doyle): A string is stored as one contiguous chunk of memory. We
// don't use a pointer for storing the string as this'd require an extra
// 4 bytes to store the pointer, which we don't need if everything is
// contiguous. The string follows on from the len, and we return the address
// of the string to simulate a pointer.
String string;
} StringHeader;
// TODO(doyle): string capacity- append if already enough space
INTERNAL StringHeader *stringGetHeader(String *const string)
{
StringHeader *result = NULL;
// NOTE(doyle): C-String must be located at end of struct type for offset to
// be correct! We cannot just subtract the string-header since we start at
// the string ptr position
if (string)
{
i32 byteOffsetToHeader = sizeof(StringHeader) - sizeof(String *);
result = CAST(StringHeader *)((CAST(u8 *) string) - byteOffsetToHeader);
}
return result;
}
i32 common_stringLen(String *const string)
{
if (!string) return -1;
StringHeader *header = stringGetHeader(string);
i32 result = header->len;
return result;
}
String *const common_stringAppend(MemoryArena_ *const arena, String *oldString,
char *appendString, i32 appendLen)
{
if (!oldString || !appendString || !arena) return oldString;
/* Calculate size of new string */
StringHeader *oldHeader = stringGetHeader(oldString);
i32 newLen = oldHeader->len + appendLen;
String *newString = common_stringMakeLen(arena, newLen);
/* Append strings together */
String *insertPtr = newString;
common_strncpy(insertPtr, oldString, oldHeader->len);
insertPtr += oldHeader->len;
common_strncpy(insertPtr, appendString, appendLen);
/* Free old string */
common_stringFree(arena, oldString);
return newString;
}
void common_stringFree(MemoryArena_ *arena, String *string)
{
if (!string || !arena) return;
StringHeader *header = stringGetHeader(string);
i32 bytesToFree = sizeof(StringHeader) + header->len;
common_memset((u8 *)header, 0, bytesToFree);
// TODO(doyle): Mem free
// PLATFORM_MEM_FREE(arena, header, bytesToFree);
string = NULL;
}
String *const common_stringMake(MemoryArena_ *const arena, char *string)
{
if (!arena) return NULL;
i32 len = common_strlen(string);
String *result = common_stringMakeLen(arena, len);
common_strncpy(result, string, len);
return result;
}
String *const common_stringMakeLen(MemoryArena_ *const arena, i32 len)
{
if (!arena) return NULL;
// NOTE(doyle): Allocate the string header size plus the len. But _note_
// that StringHeader contains a single String character. This has
// a side-effect of already preallocating a byte for the null-terminating
// character. Whilst the len of a string counts up to the last character
// _not_ including null-terminator.
i32 bytesToAllocate = sizeof(StringHeader) + len;
void *chunk = memory_pushBytes(arena, bytesToAllocate * sizeof(u8));
if (!chunk) return NULL;
StringHeader *header = CAST(StringHeader *) chunk;
header->len = len;
return &header->string;
}
i32 common_strlen(const char *const string)
{
i32 result = 0;
while (string[result])
result++;
return result;
}
i32 common_strcmp(const char *a, const char *b)
{
while (*a == *b)
{
if (!*a) return 0;
a++;
b++;
}
return ((*a < *b) ? -1 : 1);
}
void common_strncat(char *dest, const char *src, i32 numChars)
{
char *stringPtr = dest;
while (*stringPtr)
stringPtr++;
for (i32 i = 0; i < numChars; i++)
*(stringPtr++) = src[i];
}
char *common_strncpy(char *dest, const char *src, i32 numChars)
{
for (i32 i = 0; i < numChars; i++)
dest[i] = src[i];
return dest;
}
u8 *common_memset(u8 *const ptr, const i32 value, const i32 numBytes)
{
for (i32 i = 0; i < numBytes; i++)
ptr[i] = value;
return ptr;
}
INTERNAL void reverseString(char *const buf, const i32 bufSize)
{
if (!buf || bufSize == 0 || bufSize == 1) return;
i32 mid = bufSize / 2;
for (i32 i = 0; i < mid; i++)
{
char tmp = buf[i];
buf[i] = buf[(bufSize - 1) - i];
buf[(bufSize - 1) - i] = tmp;
}
}
void common_itoa(i32 value, char *buf, i32 bufSize)
{
if (!buf || bufSize == 0) return;
if (value == 0)
{
buf[0] = '0';
return;
}
// NOTE(doyle): Max 32bit integer (+-)2147483647
i32 charIndex = 0;
b32 negative = FALSE;
if (value < 0) negative = TRUE;
if (negative) buf[charIndex++] = '-';
i32 val = ABS(value);
while (val != 0 && charIndex < bufSize)
{
i32 rem = val % 10;
buf[charIndex++] = rem + '0';
val /= 10;
}
// NOTE(doyle): If string is negative, we only want to reverse starting
// from the second character, so we don't put the negative sign at the end
if (negative)
{
reverseString(buf + 1, charIndex - 1);
}
else
{
reverseString(buf, charIndex);
}
}
// TODO(doyle): Consider if we should trash ptrs in string operations in general
i32 common_atoi(const char *string, const i32 len)
{
if (len == 0) return -1;
// TODO(doyle): Implement ATOI
i32 index = 0;
if (string[index] != '-' && string[index] != '+' &&
(string[index] < '0' || string[index] > '9'))
{
return -1;
}
b32 isNegative = FALSE;
if (string[index] == '-' || string[index] == '+')
{
if (string[index] == '-') isNegative = TRUE;
index++;
}
i32 result = 0;
for (i32 i = index; i < len; i++)
{
if (string[i] >= '0' && string[i] <= '9')
{
result *= 10;
result += string[i] - '0';
}
else
{
break;
}
}
if (isNegative) result *= -1;
return result;
}
u32 common_murmurHash2(const void *key, i32 len, u32 seed)
{
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
const u32 m = 0x5bd1e995;
const i32 r = 24;
// Initialize the hash to a 'random' value
u32 h = seed ^ len;
// Mix 4 bytes at a time into the hash
const unsigned char *data = (const unsigned char *)key;
while (len >= 4)
{
u32 k = *(u32 *)data;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
len -= 4;
}
// Handle the last few bytes of the input array
switch (len)
{
case 3:
h ^= data[2] << 16;
case 2:
h ^= data[1] << 8;
case 1:
h ^= data[0];
h *= m;
};
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}