Split single headers, add test to make sure things compile
This commit is contained in:
@@ -0,0 +1,414 @@
|
||||
#include "arena.h"
|
||||
#ifndef MA_ASSERT
|
||||
#include <assert.h>
|
||||
#define MA_ASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
#ifndef MA_MemoryZero
|
||||
#include <string.h>
|
||||
MA_API void MA_MemoryZero(void *p, size_t size) {
|
||||
memset(p, 0, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MA_MemoryCopy
|
||||
#include <string.h>
|
||||
MA_API void MA_MemoryCopy(void *dst, void *src, size_t size) {
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MA_CMalloc
|
||||
#include <stdlib.h>
|
||||
#define MA_CMalloc(x) malloc(x)
|
||||
#define MA_CFree(x) free(x)
|
||||
#endif
|
||||
|
||||
#ifndef MA_StaticFunc
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define MA_StaticFunc __attribute__((unused)) static
|
||||
#else
|
||||
#define MA_StaticFunc static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
MA_API size_t MA_GetAlignOffset(size_t size, size_t align) {
|
||||
size_t mask = align - 1;
|
||||
size_t val = size & mask;
|
||||
if (val) {
|
||||
val = align - val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
MA_API size_t MA_AlignUp(size_t size, size_t align) {
|
||||
size_t result = size + MA_GetAlignOffset(size, align);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API size_t MA_AlignDown(size_t size, size_t align) {
|
||||
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
|
||||
size_t result = size - (align - MA_GetAlignOffset(size, align));
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_StaticFunc uint8_t *MV__AdvanceCommit(MV_Memory *m, size_t *commit_size, size_t page_size) {
|
||||
size_t aligned_up_commit = MA_AlignUp(*commit_size, page_size);
|
||||
size_t to_be_total_commited_size = aligned_up_commit + m->commit;
|
||||
size_t to_be_total_commited_size_clamped_to_reserve = MA_CLAMP_TOP(to_be_total_commited_size, m->reserve);
|
||||
size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit;
|
||||
MA_ASSERT(adjusted_to_boundary_commit && "Reached the virtual memory reserved boundary");
|
||||
*commit_size = adjusted_to_boundary_commit;
|
||||
|
||||
if (adjusted_to_boundary_commit == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t *result = m->data + m->commit;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API void MA_DeallocateStub(MA_Arena *arena, void *p) {}
|
||||
|
||||
MA_API void MA_PopToPos(MA_Arena *arena, size_t pos) {
|
||||
pos = MA_CLAMP_TOP(pos, arena->len);
|
||||
arena->len = pos;
|
||||
}
|
||||
|
||||
MA_API void *MA_PopSize(MA_Arena *arena, size_t size) {
|
||||
size = MA_CLAMP_TOP(size, arena->len);
|
||||
arena->len -= size;
|
||||
return arena->memory.data + arena->len;
|
||||
}
|
||||
|
||||
MA_API void MA_DeallocateArena(MA_Arena *arena) {
|
||||
MV_Deallocate(&arena->memory);
|
||||
}
|
||||
|
||||
MA_API void MA_Reset(MA_Arena *arena) {
|
||||
MA_PopToPos(arena, 0);
|
||||
}
|
||||
|
||||
MA_StaticFunc size_t MA__AlignLen(MA_Arena *a) {
|
||||
size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||
size_t aligned = a->len + align_offset;
|
||||
return aligned;
|
||||
}
|
||||
|
||||
MA_API void *MA__BeginPackedArray(MA_Arena *arena, size_t element_size) {
|
||||
MA_ASSERT(arena->memory.data);
|
||||
arena->len = MA__AlignLen(arena);
|
||||
arena->saved_alignment = arena->alignment;
|
||||
arena->alignment = 0;
|
||||
arena->packed_array_begin = arena->len;
|
||||
arena->packed_array_element_size = element_size;
|
||||
void *result = arena->memory.data + arena->len;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API int MA_EndPackedArray(MA_Arena *arena) {
|
||||
arena->alignment = arena->saved_alignment;
|
||||
size_t different = (arena->len - arena->packed_array_begin);
|
||||
int result = (int)((arena->len - arena->packed_array_begin) / arena->packed_array_element_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API void MA_SetAlignment(MA_Arena *arena, int alignment) {
|
||||
arena->alignment = alignment;
|
||||
}
|
||||
|
||||
MA_API uint8_t *MA_GetTop(MA_Arena *a) {
|
||||
MA_ASSERT(a->memory.data);
|
||||
return a->memory.data + a->len;
|
||||
}
|
||||
|
||||
MA_API void *MA_PushSizeNonZeroed(MA_Arena *a, size_t size) {
|
||||
size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||
size_t aligned_len = a->len + align_offset;
|
||||
size_t size_with_alignment = size + align_offset;
|
||||
|
||||
if (a->len + size_with_alignment > a->memory.commit) {
|
||||
if (a->memory.reserve == 0) {
|
||||
#if MA_ZERO_IS_INITIALIZATION
|
||||
MA_Init(a);
|
||||
#else
|
||||
MA_ASSERT("Pushing on uninitialized arena");
|
||||
#endif
|
||||
}
|
||||
bool result = MV_Commit(&a->memory, size_with_alignment + MA_COMMIT_ADD_SIZE);
|
||||
MA_ASSERT(result && "Failed to commit memory");
|
||||
(void)result;
|
||||
}
|
||||
|
||||
uint8_t *result = a->memory.data + aligned_len;
|
||||
a->len += size_with_alignment;
|
||||
MA_ASSERT(a->len <= a->memory.commit);
|
||||
return (void *)result;
|
||||
}
|
||||
|
||||
MA_API void *MA_PushSize(MA_Arena *arena, size_t size) {
|
||||
void *result = MA_PushSizeNonZeroed(arena, size);
|
||||
MA_MemoryZero(result, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API void MA_InitEx(MA_Arena *a, size_t reserve) {
|
||||
a->memory = MV_Reserve(reserve);
|
||||
a->alignment = MA_DEFAULT_ALIGNMENT;
|
||||
MA_INIT_HOOK(a);
|
||||
}
|
||||
|
||||
MA_API void MA_Init(MA_Arena *a) {
|
||||
MA_InitEx(a, MA_DEFAULT_RESERVE_SIZE);
|
||||
}
|
||||
|
||||
MA_API void MA_MakeSureInitialized(MA_Arena *a) {
|
||||
if (a->memory.data == 0) {
|
||||
MA_Init(a);
|
||||
}
|
||||
}
|
||||
|
||||
MA_API MA_Arena *MA_Bootstrap(void) {
|
||||
MA_Arena bootstrap_arena = {0};
|
||||
MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena);
|
||||
*arena = bootstrap_arena;
|
||||
arena->allocator.obj = arena;
|
||||
return arena;
|
||||
}
|
||||
|
||||
MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size) {
|
||||
arena->memory.data = (uint8_t *)buffer;
|
||||
arena->memory.commit = size;
|
||||
arena->memory.reserve = size;
|
||||
arena->alignment = MA_DEFAULT_ALIGNMENT;
|
||||
MA_INIT_HOOK(arena);
|
||||
}
|
||||
|
||||
MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) {
|
||||
MA_Arena arena;
|
||||
MA_MemoryZero(&arena, sizeof(arena));
|
||||
MA_InitFromBuffer(&arena, buffer, size);
|
||||
return arena;
|
||||
}
|
||||
|
||||
MA_API char *MA_PushStringCopy(MA_Arena *arena, char *p, size_t size) {
|
||||
char *copy_buffer = (char *)MA_PushSizeNonZeroed(arena, size + 1);
|
||||
MA_MemoryCopy(copy_buffer, p, size);
|
||||
copy_buffer[size] = 0;
|
||||
return copy_buffer;
|
||||
}
|
||||
|
||||
MA_API void *MA_PushCopy(MA_Arena *arena, void *p, size_t size) {
|
||||
void *copy_buffer = MA_PushSizeNonZeroed(arena, size);
|
||||
MA_MemoryCopy(copy_buffer, p, size);
|
||||
return copy_buffer;
|
||||
}
|
||||
|
||||
MA_API bool MA_IsPointerInside(MA_Arena *arena, void *p) {
|
||||
uintptr_t pointer = (uintptr_t)p;
|
||||
uintptr_t start = (uintptr_t)arena->memory.data;
|
||||
uintptr_t stop = start + (uintptr_t)arena->len;
|
||||
bool result = pointer >= start && pointer < stop;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API MA_Arena MA_PushArena(MA_Arena *arena, size_t size) {
|
||||
MA_Arena result;
|
||||
MA_MemoryZero(&result, sizeof(result));
|
||||
result.memory.data = MA_PushArrayNonZeroed(arena, uint8_t, size);
|
||||
result.memory.commit = size;
|
||||
result.memory.reserve = size;
|
||||
result.alignment = arena->alignment;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API MA_Checkpoint MA_Save(MA_Arena *arena) {
|
||||
MA_Checkpoint result;
|
||||
result.pos = arena->len;
|
||||
result.arena = arena;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API void MA_Load(MA_Checkpoint checkpoint) {
|
||||
MA_PopToPos(checkpoint.arena, checkpoint.pos);
|
||||
}
|
||||
|
||||
MA_API void *M_AllocNonZeroed(M_Allocator allocator, size_t size) {
|
||||
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
MA_API void *M_Alloc(M_Allocator allocator, size_t size) {
|
||||
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size);
|
||||
MA_MemoryZero(p, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
MA_API void *M_AllocCopy(M_Allocator allocator, void *p, size_t size) {
|
||||
void *copy_buffer = M_AllocNonZeroed(allocator, size);
|
||||
MA_MemoryCopy(copy_buffer, p, size);
|
||||
return copy_buffer;
|
||||
}
|
||||
|
||||
MA_API void M_Dealloc(M_Allocator allocator, void *p) {
|
||||
allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0);
|
||||
}
|
||||
|
||||
MA_StaticFunc void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size) {
|
||||
if (kind == M_AllocatorOp_Allocate) {
|
||||
return MA_CMalloc(size);
|
||||
}
|
||||
|
||||
if (kind == M_AllocatorOp_Deallocate) {
|
||||
MA_CFree(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_ASSERT("MA_Arena invalid codepath");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_API void *MA_AllocatorProc(M_Allocator allocator, M_AllocatorOp kind, void *p, size_t size) {
|
||||
if (kind == M_AllocatorOp_Allocate) {
|
||||
return MA_PushSizeNonZeroed((MA_Arena *)allocator.obj, size);
|
||||
}
|
||||
|
||||
if (kind == M_AllocatorOp_Deallocate) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_ASSERT("MA_Arena invalid codepath");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_API M_Allocator M_GetSystemAllocator(void) {
|
||||
M_Allocator allocator;
|
||||
allocator.obj = 0;
|
||||
allocator.p = M_ClibAllocatorProc;
|
||||
return allocator;
|
||||
}
|
||||
|
||||
#ifndef MA_DISABLE_SCRATCH
|
||||
MA_THREAD_LOCAL MA_Arena MA_ScratchArenaPool[4];
|
||||
|
||||
MA_API MA_Checkpoint MA_GetScratchEx(MA_Arena **conflicts, int conflict_count) {
|
||||
MA_Arena *unoccupied = 0;
|
||||
for (int i = 0; i < MA_LENGTHOF(MA_ScratchArenaPool); i += 1) {
|
||||
MA_Arena *from_pool = MA_ScratchArenaPool + i;
|
||||
unoccupied = from_pool;
|
||||
for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) {
|
||||
MA_Arena *from_conflict = conflicts[conflict_i];
|
||||
if (from_pool == from_conflict) {
|
||||
unoccupied = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unoccupied) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MA_ASSERT(unoccupied);
|
||||
MA_Checkpoint result = MA_Save(unoccupied);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API MA_Checkpoint MA_GetScratch(void) {
|
||||
MA_Checkpoint result = MA_Save(MA_ScratchArenaPool + 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API MA_Checkpoint MA_GetScratch1(MA_Arena *conflict) {
|
||||
MA_Arena *conflicts[] = {conflict};
|
||||
return MA_GetScratchEx(conflicts, 1);
|
||||
}
|
||||
#endif // MA_DISABLE_SCRATCH
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
const size_t MV__WIN32_PAGE_SIZE = 4096;
|
||||
|
||||
MA_API MV_Memory MV_Reserve(size_t size) {
|
||||
MV_Memory result;
|
||||
MA_MemoryZero(&result, sizeof(result));
|
||||
size_t adjusted_size = MA_AlignUp(size, MV__WIN32_PAGE_SIZE);
|
||||
result.data = (uint8_t *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE);
|
||||
MA_ASSERT(result.data && "Failed to reserve virtual memory");
|
||||
result.reserve = adjusted_size;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
|
||||
uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__WIN32_PAGE_SIZE);
|
||||
if (pointer) {
|
||||
void *result = VirtualAlloc(pointer, commit, MEM_COMMIT, PAGE_READWRITE);
|
||||
MA_ASSERT(result && "Failed to commit more memory");
|
||||
if (result) {
|
||||
m->commit += commit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MA_API void MV_Deallocate(MV_Memory *m) {
|
||||
BOOL result = VirtualFree(m->data, 0, MEM_RELEASE);
|
||||
MA_ASSERT(result != 0 && "Failed to release MV_Memory");
|
||||
}
|
||||
|
||||
MA_API bool MV_DecommitPos(MV_Memory *m, size_t pos) {
|
||||
size_t aligned = MA_AlignDown(pos, MV__WIN32_PAGE_SIZE);
|
||||
size_t adjusted_pos = MA_CLAMP_TOP(aligned, m->commit);
|
||||
size_t size_to_decommit = m->commit - adjusted_pos;
|
||||
if (size_to_decommit) {
|
||||
uint8_t *base_address = m->data + adjusted_pos;
|
||||
BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT);
|
||||
if (result) {
|
||||
m->commit -= size_to_decommit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#elif __unix__ // _WIN32
|
||||
#include <sys/mman.h>
|
||||
#define MV__UNIX_PAGE_SIZE 4096
|
||||
MA_API MV_Memory MV_Reserve(size_t size) {
|
||||
MV_Memory result = {};
|
||||
size_t size_aligned = MA_AlignUp(size, MV__UNIX_PAGE_SIZE);
|
||||
result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
MA_ASSERT(result.data && "Failed to reserve memory using mmap!!");
|
||||
if (result.data) {
|
||||
result.reserve = size_aligned;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
|
||||
uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__UNIX_PAGE_SIZE);
|
||||
if (pointer) {
|
||||
int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE);
|
||||
MA_ASSERT(mprotect_result == 0 && "Failed to commit more memory using mmap");
|
||||
if (mprotect_result == 0) {
|
||||
m->commit += commit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MA_API void MV_Deallocate(MV_Memory *m) {
|
||||
int result = munmap(m->data, m->reserve);
|
||||
MA_ASSERT(result == 0 && "Failed to release virtual memory using munmap");
|
||||
}
|
||||
#else
|
||||
#error "unhandled arena platform"
|
||||
#endif // __unix__
|
||||
@@ -1,22 +1,3 @@
|
||||
/*
|
||||
## MA_Arena version 2
|
||||
A public domain, single-header-file library that provides easy to use
|
||||
arena data structure.
|
||||
|
||||
### Features
|
||||
- linear allocator (MA_Arena)
|
||||
- thread local scratch allocator (MA_Scratch) *optional*
|
||||
- allocator abstraction (M_Allocator) *overridable*
|
||||
- virtual memory abstraction (MV_Memory)
|
||||
|
||||
|
||||
### Usage, do this in *one* C or C++ file:
|
||||
```
|
||||
#define MA_IMPLEMENTATION
|
||||
#include "arena.h"
|
||||
```
|
||||
|
||||
*/
|
||||
#ifndef MA_HEADER
|
||||
#define MA_HEADER
|
||||
#include <stdint.h>
|
||||
@@ -207,420 +188,3 @@ struct MA_Scratch {
|
||||
#endif // __cplusplus
|
||||
#endif // MA_DISABLE_SCRATCH
|
||||
#endif // MA_HEADER
|
||||
|
||||
#ifdef MA_IMPLEMENTATION
|
||||
|
||||
#ifndef MA_ASSERT
|
||||
#include <assert.h>
|
||||
#define MA_ASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
#ifndef MA_MemoryZero
|
||||
#include <string.h>
|
||||
MA_API void MA_MemoryZero(void *p, size_t size) {
|
||||
memset(p, 0, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MA_MemoryCopy
|
||||
#include <string.h>
|
||||
MA_API void MA_MemoryCopy(void *dst, void *src, size_t size) {
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MA_CMalloc
|
||||
#include <stdlib.h>
|
||||
#define MA_CMalloc(x) malloc(x)
|
||||
#define MA_CFree(x) free(x)
|
||||
#endif
|
||||
|
||||
#ifndef MA_FN
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define MA_FN __attribute__((unused)) static
|
||||
#else
|
||||
#define MA_FN static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
MA_API size_t MA_GetAlignOffset(size_t size, size_t align) {
|
||||
size_t mask = align - 1;
|
||||
size_t val = size & mask;
|
||||
if (val) {
|
||||
val = align - val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
MA_API size_t MA_AlignUp(size_t size, size_t align) {
|
||||
size_t result = size + MA_GetAlignOffset(size, align);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API size_t MA_AlignDown(size_t size, size_t align) {
|
||||
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
|
||||
size_t result = size - (align - MA_GetAlignOffset(size, align));
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_FN uint8_t *MV__AdvanceCommit(MV_Memory *m, size_t *commit_size, size_t page_size) {
|
||||
size_t aligned_up_commit = MA_AlignUp(*commit_size, page_size);
|
||||
size_t to_be_total_commited_size = aligned_up_commit + m->commit;
|
||||
size_t to_be_total_commited_size_clamped_to_reserve = MA_CLAMP_TOP(to_be_total_commited_size, m->reserve);
|
||||
size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit;
|
||||
MA_ASSERT(adjusted_to_boundary_commit && "Reached the virtual memory reserved boundary");
|
||||
*commit_size = adjusted_to_boundary_commit;
|
||||
|
||||
if (adjusted_to_boundary_commit == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t *result = m->data + m->commit;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API void MA_DeallocateStub(MA_Arena *arena, void *p) {}
|
||||
|
||||
MA_API void MA_PopToPos(MA_Arena *arena, size_t pos) {
|
||||
pos = MA_CLAMP_TOP(pos, arena->len);
|
||||
arena->len = pos;
|
||||
}
|
||||
|
||||
MA_API void *MA_PopSize(MA_Arena *arena, size_t size) {
|
||||
size = MA_CLAMP_TOP(size, arena->len);
|
||||
arena->len -= size;
|
||||
return arena->memory.data + arena->len;
|
||||
}
|
||||
|
||||
MA_API void MA_DeallocateArena(MA_Arena *arena) {
|
||||
MV_Deallocate(&arena->memory);
|
||||
}
|
||||
|
||||
MA_API void MA_Reset(MA_Arena *arena) {
|
||||
MA_PopToPos(arena, 0);
|
||||
}
|
||||
|
||||
MA_FN size_t MA__AlignLen(MA_Arena *a) {
|
||||
size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||
size_t aligned = a->len + align_offset;
|
||||
return aligned;
|
||||
}
|
||||
|
||||
MA_API void *MA__BeginPackedArray(MA_Arena *arena, size_t element_size) {
|
||||
MA_ASSERT(arena->memory.data);
|
||||
arena->len = MA__AlignLen(arena);
|
||||
arena->saved_alignment = arena->alignment;
|
||||
arena->alignment = 0;
|
||||
arena->packed_array_begin = arena->len;
|
||||
arena->packed_array_element_size = element_size;
|
||||
void *result = arena->memory.data + arena->len;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API int MA_EndPackedArray(MA_Arena *arena) {
|
||||
arena->alignment = arena->saved_alignment;
|
||||
size_t different = (arena->len - arena->packed_array_begin);
|
||||
int result = (int)((arena->len - arena->packed_array_begin) / arena->packed_array_element_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API void MA_SetAlignment(MA_Arena *arena, int alignment) {
|
||||
arena->alignment = alignment;
|
||||
}
|
||||
|
||||
MA_API uint8_t *MA_GetTop(MA_Arena *a) {
|
||||
MA_ASSERT(a->memory.data);
|
||||
return a->memory.data + a->len;
|
||||
}
|
||||
|
||||
MA_API void *MA_PushSizeNonZeroed(MA_Arena *a, size_t size) {
|
||||
size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||
size_t aligned_len = a->len + align_offset;
|
||||
size_t size_with_alignment = size + align_offset;
|
||||
|
||||
if (a->len + size_with_alignment > a->memory.commit) {
|
||||
if (a->memory.reserve == 0) {
|
||||
#if MA_ZERO_IS_INITIALIZATION
|
||||
MA_Init(a);
|
||||
#else
|
||||
MA_ASSERT("Pushing on uninitialized arena");
|
||||
#endif
|
||||
}
|
||||
bool result = MV_Commit(&a->memory, size_with_alignment + MA_COMMIT_ADD_SIZE);
|
||||
MA_ASSERT(result && "Failed to commit memory");
|
||||
(void)result;
|
||||
}
|
||||
|
||||
uint8_t *result = a->memory.data + aligned_len;
|
||||
a->len += size_with_alignment;
|
||||
MA_ASSERT(a->len <= a->memory.commit);
|
||||
return (void *)result;
|
||||
}
|
||||
|
||||
MA_API void *MA_PushSize(MA_Arena *arena, size_t size) {
|
||||
void *result = MA_PushSizeNonZeroed(arena, size);
|
||||
MA_MemoryZero(result, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API void MA_InitEx(MA_Arena *a, size_t reserve) {
|
||||
a->memory = MV_Reserve(reserve);
|
||||
a->alignment = MA_DEFAULT_ALIGNMENT;
|
||||
MA_INIT_HOOK(a);
|
||||
}
|
||||
|
||||
MA_API void MA_Init(MA_Arena *a) {
|
||||
MA_InitEx(a, MA_DEFAULT_RESERVE_SIZE);
|
||||
}
|
||||
|
||||
MA_API void MA_MakeSureInitialized(MA_Arena *a) {
|
||||
if (a->memory.data == 0) {
|
||||
MA_Init(a);
|
||||
}
|
||||
}
|
||||
|
||||
MA_API MA_Arena *MA_Bootstrap(void) {
|
||||
MA_Arena bootstrap_arena = {0};
|
||||
MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena);
|
||||
*arena = bootstrap_arena;
|
||||
arena->allocator.obj = arena;
|
||||
return arena;
|
||||
}
|
||||
|
||||
MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size) {
|
||||
arena->memory.data = (uint8_t *)buffer;
|
||||
arena->memory.commit = size;
|
||||
arena->memory.reserve = size;
|
||||
arena->alignment = MA_DEFAULT_ALIGNMENT;
|
||||
MA_INIT_HOOK(arena);
|
||||
}
|
||||
|
||||
MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) {
|
||||
MA_Arena arena;
|
||||
MA_MemoryZero(&arena, sizeof(arena));
|
||||
MA_InitFromBuffer(&arena, buffer, size);
|
||||
return arena;
|
||||
}
|
||||
|
||||
MA_API char *MA_PushStringCopy(MA_Arena *arena, char *p, size_t size) {
|
||||
char *copy_buffer = (char *)MA_PushSizeNonZeroed(arena, size + 1);
|
||||
MA_MemoryCopy(copy_buffer, p, size);
|
||||
copy_buffer[size] = 0;
|
||||
return copy_buffer;
|
||||
}
|
||||
|
||||
MA_API void *MA_PushCopy(MA_Arena *arena, void *p, size_t size) {
|
||||
void *copy_buffer = MA_PushSizeNonZeroed(arena, size);
|
||||
MA_MemoryCopy(copy_buffer, p, size);
|
||||
return copy_buffer;
|
||||
}
|
||||
|
||||
MA_API bool MA_IsPointerInside(MA_Arena *arena, void *p) {
|
||||
uintptr_t pointer = (uintptr_t)p;
|
||||
uintptr_t start = (uintptr_t)arena->memory.data;
|
||||
uintptr_t stop = start + (uintptr_t)arena->len;
|
||||
bool result = pointer >= start && pointer < stop;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API MA_Arena MA_PushArena(MA_Arena *arena, size_t size) {
|
||||
MA_Arena result;
|
||||
MA_MemoryZero(&result, sizeof(result));
|
||||
result.memory.data = MA_PushArrayNonZeroed(arena, uint8_t, size);
|
||||
result.memory.commit = size;
|
||||
result.memory.reserve = size;
|
||||
result.alignment = arena->alignment;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API MA_Checkpoint MA_Save(MA_Arena *arena) {
|
||||
MA_Checkpoint result;
|
||||
result.pos = arena->len;
|
||||
result.arena = arena;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API void MA_Load(MA_Checkpoint checkpoint) {
|
||||
MA_PopToPos(checkpoint.arena, checkpoint.pos);
|
||||
}
|
||||
|
||||
MA_API void *M_AllocNonZeroed(M_Allocator allocator, size_t size) {
|
||||
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
MA_API void *M_Alloc(M_Allocator allocator, size_t size) {
|
||||
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size);
|
||||
MA_MemoryZero(p, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
MA_API void *M_AllocCopy(M_Allocator allocator, void *p, size_t size) {
|
||||
void *copy_buffer = M_AllocNonZeroed(allocator, size);
|
||||
MA_MemoryCopy(copy_buffer, p, size);
|
||||
return copy_buffer;
|
||||
}
|
||||
|
||||
MA_API void M_Dealloc(M_Allocator allocator, void *p) {
|
||||
allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0);
|
||||
}
|
||||
|
||||
MA_FN void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size) {
|
||||
if (kind == M_AllocatorOp_Allocate) {
|
||||
return MA_CMalloc(size);
|
||||
}
|
||||
|
||||
if (kind == M_AllocatorOp_Deallocate) {
|
||||
MA_CFree(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_ASSERT("MA_Arena invalid codepath");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_API void *MA_AllocatorProc(M_Allocator allocator, M_AllocatorOp kind, void *p, size_t size) {
|
||||
if (kind == M_AllocatorOp_Allocate) {
|
||||
return MA_PushSizeNonZeroed((MA_Arena *)allocator.obj, size);
|
||||
}
|
||||
|
||||
if (kind == M_AllocatorOp_Deallocate) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_ASSERT("MA_Arena invalid codepath");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MA_API M_Allocator M_GetSystemAllocator(void) {
|
||||
M_Allocator allocator;
|
||||
allocator.obj = 0;
|
||||
allocator.p = M_ClibAllocatorProc;
|
||||
return allocator;
|
||||
}
|
||||
|
||||
#ifndef MA_DISABLE_SCRATCH
|
||||
MA_THREAD_LOCAL MA_Arena MA_ScratchArenaPool[4];
|
||||
|
||||
MA_API MA_Checkpoint MA_GetScratchEx(MA_Arena **conflicts, int conflict_count) {
|
||||
MA_Arena *unoccupied = 0;
|
||||
for (int i = 0; i < MA_LENGTHOF(MA_ScratchArenaPool); i += 1) {
|
||||
MA_Arena *from_pool = MA_ScratchArenaPool + i;
|
||||
unoccupied = from_pool;
|
||||
for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) {
|
||||
MA_Arena *from_conflict = conflicts[conflict_i];
|
||||
if (from_pool == from_conflict) {
|
||||
unoccupied = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unoccupied) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MA_ASSERT(unoccupied);
|
||||
MA_Checkpoint result = MA_Save(unoccupied);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API MA_Checkpoint MA_GetScratch(void) {
|
||||
MA_Checkpoint result = MA_Save(MA_ScratchArenaPool + 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API MA_Checkpoint MA_GetScratch1(MA_Arena *conflict) {
|
||||
MA_Arena *conflicts[] = {conflict};
|
||||
return MA_GetScratchEx(conflicts, 1);
|
||||
}
|
||||
#endif // MA_DISABLE_SCRATCH
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
const size_t MV__WIN32_PAGE_SIZE = 4096;
|
||||
|
||||
MA_API MV_Memory MV_Reserve(size_t size) {
|
||||
MV_Memory result;
|
||||
MA_MemoryZero(&result, sizeof(result));
|
||||
size_t adjusted_size = MA_AlignUp(size, MV__WIN32_PAGE_SIZE);
|
||||
result.data = (uint8_t *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE);
|
||||
MA_ASSERT(result.data && "Failed to reserve virtual memory");
|
||||
result.reserve = adjusted_size;
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
|
||||
uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__WIN32_PAGE_SIZE);
|
||||
if (pointer) {
|
||||
void *result = VirtualAlloc(pointer, commit, MEM_COMMIT, PAGE_READWRITE);
|
||||
MA_ASSERT(result && "Failed to commit more memory");
|
||||
if (result) {
|
||||
m->commit += commit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MA_API void MV_Deallocate(MV_Memory *m) {
|
||||
BOOL result = VirtualFree(m->data, 0, MEM_RELEASE);
|
||||
MA_ASSERT(result != 0 && "Failed to release MV_Memory");
|
||||
}
|
||||
|
||||
MA_API bool MV_DecommitPos(MV_Memory *m, size_t pos) {
|
||||
size_t aligned = MA_AlignDown(pos, MV__WIN32_PAGE_SIZE);
|
||||
size_t adjusted_pos = MA_CLAMP_TOP(aligned, m->commit);
|
||||
size_t size_to_decommit = m->commit - adjusted_pos;
|
||||
if (size_to_decommit) {
|
||||
uint8_t *base_address = m->data + adjusted_pos;
|
||||
BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT);
|
||||
if (result) {
|
||||
m->commit -= size_to_decommit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#elif __unix__ // _WIN32
|
||||
#include <sys/mman.h>
|
||||
#define MV__UNIX_PAGE_SIZE 4096
|
||||
MA_API MV_Memory MV_Reserve(size_t size) {
|
||||
MV_Memory result = {};
|
||||
size_t size_aligned = MA_AlignUp(size, MV__UNIX_PAGE_SIZE);
|
||||
result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
MA_ASSERT(result.data && "Failed to reserve memory using mmap!!");
|
||||
if (result.data) {
|
||||
result.reserve = size_aligned;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
|
||||
uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__UNIX_PAGE_SIZE);
|
||||
if (pointer) {
|
||||
int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE);
|
||||
MA_ASSERT(mprotect_result == 0 && "Failed to commit more memory using mmap");
|
||||
if (mprotect_result == 0) {
|
||||
m->commit += commit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MA_API void MV_Deallocate(MV_Memory *m) {
|
||||
int result = munmap(m->data, m->reserve);
|
||||
MA_ASSERT(result == 0 && "Failed to release virtual memory using munmap");
|
||||
}
|
||||
#else
|
||||
#error "unhandled arena platform"
|
||||
#endif // __unix__
|
||||
#endif // MA_IMPL
|
||||
@@ -0,0 +1,23 @@
|
||||
@echo off
|
||||
call ../misc/compile_setup.bat
|
||||
|
||||
set DEBUG=-Od -RTC1 -D_DEBUG -MTd -fsanitize=address
|
||||
set RELEASE=-O2 -MT -DNDEBUG -GL
|
||||
set COMMON=-MP -FC -Z7 -GF -Gm- -Oi -Zo -EHa- -GR-
|
||||
set WRN=-WX -W3 -wd4200 -diagnostics:column -nologo -D_CRT_SECURE_NO_WARNINGS
|
||||
set LINK_DEBUG=-NODEFAULTLIB:LIBCMT
|
||||
set LINK_RELEASE=-opt:ref -opt:icf -ltcg
|
||||
|
||||
set DEBUG_LINE=%DEBUG% %WRN% %COMMON% -link -incremental:no %LINK_DEBUG%
|
||||
set RELEASE_LINE=%RELEASE% %WRN% %COMMON% -link -incremental:no %LINK_RELEASE%
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cl.exe -Fe:cpp_debug.exe ../test/main.cpp %DEBUG_LINE%
|
||||
cl.exe -Fe:cpp_release.exe ../test/main.cpp %RELEASE_LINE%
|
||||
cl.exe -Fe:c_debug.exe ../test/main.c %DEBUG_LINE%
|
||||
cl.exe -Fe:main_core_as_header.exe ../core.c ../test/main_core_as_header.cpp %DEBUG_LINE%
|
||||
cd ..
|
||||
|
||||
rem rtc1 - runtime error checks
|
||||
rem gl - whole program optimizations
|
||||
@@ -1,34 +1,30 @@
|
||||
#include "core.h"
|
||||
#define S8_IMPLEMENTATION
|
||||
|
||||
#define IO_IMPLEMENTATION
|
||||
#define IO_VSNPRINTF stbsp_vsnprintf
|
||||
#define IO_SNPRINTF stbsp_snprintf
|
||||
#include "io.h"
|
||||
|
||||
#define STB_SPRINTF_IMPLEMENTATION
|
||||
#include "stb_sprintf.h"
|
||||
|
||||
#define MA_IMPLEMENTATION
|
||||
#define IO_VSNPRINTF stbsp_vsnprintf
|
||||
#define IO_SNPRINTF stbsp_snprintf
|
||||
#include "io.c"
|
||||
|
||||
#define MA_ASSERT(x) IO_Assert(x)
|
||||
#include "arena.h"
|
||||
#include "arena.c"
|
||||
|
||||
#define RE_IMPLEMENTATION
|
||||
#define RE_ASSERT(x) IO_Assert(x)
|
||||
#include "regex.h"
|
||||
#include "regex.c"
|
||||
|
||||
#define UTF_IMPLEMENTATION
|
||||
#include "unicode.h"
|
||||
#include "unicode.c"
|
||||
|
||||
#define S8_VSNPRINTF stbsp_vsnprintf
|
||||
#define S8_ALLOCATE(allocator, size) MA_PushSize(allocator, size)
|
||||
#define S8_ASSERT(x) IO_Assert(x)
|
||||
#define S8_MemoryCopy MA_MemoryCopy
|
||||
#include "string.h"
|
||||
#include "string.c"
|
||||
|
||||
#define MU_IMPLEMENTATION
|
||||
#define MU_ASSERT IO_Assert
|
||||
#include "multimedia.h"
|
||||
|
||||
#include "hash.c"
|
||||
#include "load_library.c"
|
||||
|
||||
#include "filesystem.c"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifdef __cplusplus
|
||||
#include "defer.hpp"
|
||||
#define TABLE_ASSERT IO_Assert
|
||||
#define TABLE_ALLOCATOR_TYPE M_Allocator
|
||||
#define TABLE_SET_DEFAULT_ALLOCATOR \
|
||||
if (!allocator.p) allocator = M_GetSystemAllocator();
|
||||
#include "table.hpp"
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
#include "io.h"
|
||||
|
||||
#ifndef IO_SNPRINTF
|
||||
#include <stdio.h>
|
||||
#define IO_SNPRINTF snprintf
|
||||
#endif
|
||||
|
||||
#ifndef IO_VSNPRINTF
|
||||
#include <stdio.h>
|
||||
#define IO_VSNPRINTF vsnprintf
|
||||
#endif
|
||||
|
||||
#ifndef IO_ALLOCATE
|
||||
#include <stdlib.h>
|
||||
#define IO_ALLOCATE(x) malloc(x)
|
||||
#define IO_FREE(x) free(x)
|
||||
#endif
|
||||
|
||||
#ifndef IO_StaticFunc
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define IO_StaticFunc __attribute__((unused)) static
|
||||
#else
|
||||
#define IO_StaticFunc static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
IO_StaticFunc int IO_Strlen(char *string) {
|
||||
int len = 0;
|
||||
while (*string++ != 0) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
void (*IO_User_OutputMessage)(char *str, int len);
|
||||
|
||||
IO_API bool IO__FatalErrorf(const char *file, int line, const char *msg, ...) {
|
||||
va_list args1;
|
||||
va_list args2;
|
||||
char buff[2048];
|
||||
|
||||
va_start(args1, msg);
|
||||
va_copy(args2, args1);
|
||||
int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *new_buffer = 0;
|
||||
char *user_message = buff;
|
||||
if (size >= sizeof(buff)) {
|
||||
size += 4;
|
||||
new_buffer = (char *)IO_ALLOCATE(size);
|
||||
IO_VSNPRINTF(new_buffer, size, msg, args1);
|
||||
user_message = new_buffer;
|
||||
}
|
||||
va_end(args1);
|
||||
|
||||
IO_ErrorResult ret = IO_ErrorResult_Continue;
|
||||
{
|
||||
char buff2[2048];
|
||||
char *result = buff2;
|
||||
char *b = 0;
|
||||
int size = IO_SNPRINTF(buff2, sizeof(buff2), "%s(%d): error: %s \n", file, line, user_message);
|
||||
if (size >= sizeof(buff2)) {
|
||||
size += 4;
|
||||
b = (char *)IO_ALLOCATE(size);
|
||||
size = IO_SNPRINTF(b, size, "%s(%d): error: %s \n", file, line, user_message);
|
||||
result = b;
|
||||
}
|
||||
|
||||
ret = IO_OutputError(result, size);
|
||||
if (ret == IO_ErrorResult_Exit) {
|
||||
IO_Exit(1);
|
||||
}
|
||||
|
||||
if (b) {
|
||||
IO_FREE(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (new_buffer) {
|
||||
IO_FREE(new_buffer);
|
||||
}
|
||||
|
||||
return ret == IO_ErrorResult_Break;
|
||||
}
|
||||
|
||||
IO_API void IO_Printf(const char *msg, ...) {
|
||||
va_list args1;
|
||||
va_list args2;
|
||||
char buff[2048];
|
||||
|
||||
va_start(args1, msg);
|
||||
va_copy(args2, args1);
|
||||
int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *new_buffer = 0;
|
||||
char *result = buff;
|
||||
if (size >= sizeof(buff)) {
|
||||
size += 4;
|
||||
new_buffer = (char *)IO_ALLOCATE(size);
|
||||
IO_VSNPRINTF(new_buffer, size, msg, args1);
|
||||
result = new_buffer;
|
||||
}
|
||||
va_end(args1);
|
||||
|
||||
if (IO_User_OutputMessage) {
|
||||
IO_User_OutputMessage(result, size);
|
||||
}
|
||||
else {
|
||||
IO_OutputMessage(result, size);
|
||||
}
|
||||
|
||||
if (new_buffer) {
|
||||
IO_FREE(new_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
IO_API bool IO__FatalError(char *msg) {
|
||||
int len = IO_Strlen(msg);
|
||||
IO_ErrorResult result = IO_OutputError(msg, len);
|
||||
if (result == IO_ErrorResult_Exit) {
|
||||
IO_Exit(1);
|
||||
}
|
||||
return result == IO_ErrorResult_Break;
|
||||
}
|
||||
|
||||
IO_API void IO_Print(char *msg) {
|
||||
int len = IO_Strlen(msg);
|
||||
if (IO_User_OutputMessage) {
|
||||
IO_User_OutputMessage(msg, len);
|
||||
}
|
||||
else {
|
||||
IO_OutputMessage(msg, len);
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
#pragma comment(lib, "user32")
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
IO_API bool IO_IsDebuggerPresent(void) {
|
||||
return IsDebuggerPresent();
|
||||
}
|
||||
|
||||
IO_API void IO_OutputMessage(char *str, int len) {
|
||||
if (IsDebuggerPresent()) {
|
||||
OutputDebugStringA(str);
|
||||
}
|
||||
printf("%.*s", len, str);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
|
||||
IO_ErrorResult result = IO_ErrorResult_Continue;
|
||||
IO_OutputMessage(str, len);
|
||||
|
||||
char *msg = str;
|
||||
if (str[len] != 0) {
|
||||
msg = (char *)IO_ALLOCATE(len + 1);
|
||||
for (int i = 0; i < len; i += 1) msg[i] = str[i];
|
||||
msg[len] = 0;
|
||||
}
|
||||
|
||||
OutputDebugStringA(msg);
|
||||
if (!IsDebuggerPresent()) {
|
||||
|
||||
// Limit size of error output message
|
||||
char tmp = 0;
|
||||
if (len > 4096) {
|
||||
tmp = str[4096];
|
||||
str[4096] = 0;
|
||||
}
|
||||
|
||||
MessageBoxA(0, msg, "Error!", 0);
|
||||
|
||||
if (tmp != 0) {
|
||||
str[4096] = tmp;
|
||||
}
|
||||
|
||||
result = IO_ErrorResult_Exit;
|
||||
}
|
||||
else {
|
||||
result = IO_ErrorResult_Break;
|
||||
}
|
||||
|
||||
if (msg != str) {
|
||||
IO_FREE(msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
IO_API void IO_Exit(int error_code) {
|
||||
ExitProcess(error_code);
|
||||
}
|
||||
#else // _WIN32 else // LIBC
|
||||
#include <stdio.h>
|
||||
|
||||
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
|
||||
fprintf(stderr, "%.*s", len, str);
|
||||
return IO_ErrorResult_Exit;
|
||||
}
|
||||
|
||||
IO_API void IO_OutputMessage(char *str, int len) {
|
||||
fprintf(stdout, "%.*s", len, str);
|
||||
}
|
||||
|
||||
IO_API void IO_Exit(int error_code) {
|
||||
exit(error_code);
|
||||
}
|
||||
|
||||
IO_API bool IO_IsDebuggerPresent(void) {
|
||||
return false;
|
||||
}
|
||||
#endif // LIBC
|
||||
@@ -2,6 +2,7 @@
|
||||
#define IO_HEADER
|
||||
#include <stdbool.h>
|
||||
typedef enum IO_ErrorResult IO_ErrorResult;
|
||||
|
||||
#ifndef IO_API
|
||||
#ifdef __cplusplus
|
||||
#define IO_API extern "C"
|
||||
@@ -73,226 +74,3 @@ IO_API IO_ErrorResult IO_OutputError(char *str, int len);
|
||||
IO_API void IO_Exit(int error_code);
|
||||
IO_API bool IO_IsDebuggerPresent(void);
|
||||
#endif
|
||||
|
||||
#ifdef IO_IMPLEMENTATION
|
||||
#ifndef IO_SNPRINTF
|
||||
#include <stdio.h>
|
||||
#define IO_SNPRINTF snprintf
|
||||
#endif
|
||||
|
||||
#ifndef IO_VSNPRINTF
|
||||
#include <stdio.h>
|
||||
#define IO_VSNPRINTF vsnprintf
|
||||
#endif
|
||||
|
||||
#ifndef IO_ALLOCATE
|
||||
#include <stdlib.h>
|
||||
#define IO_ALLOCATE(x) malloc(x)
|
||||
#define IO_FREE(x) free(x)
|
||||
#endif
|
||||
|
||||
#ifndef IO_FN
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define IO_FN __attribute__((unused)) static
|
||||
#else
|
||||
#define IO_FN static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
IO_FN int IO_Strlen(char *string) {
|
||||
int len = 0;
|
||||
while (*string++ != 0) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
void (*IO_User_OutputMessage)(char *str, int len);
|
||||
|
||||
IO_API bool IO__FatalErrorf(const char *file, int line, const char *msg, ...) {
|
||||
va_list args1;
|
||||
va_list args2;
|
||||
char buff[2048];
|
||||
|
||||
va_start(args1, msg);
|
||||
va_copy(args2, args1);
|
||||
int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *new_buffer = 0;
|
||||
char *user_message = buff;
|
||||
if (size >= sizeof(buff)) {
|
||||
size += 4;
|
||||
new_buffer = (char *)IO_ALLOCATE(size);
|
||||
IO_VSNPRINTF(new_buffer, size, msg, args1);
|
||||
user_message = new_buffer;
|
||||
}
|
||||
va_end(args1);
|
||||
|
||||
IO_ErrorResult ret = IO_ErrorResult_Continue;
|
||||
{
|
||||
char buff2[2048];
|
||||
char *result = buff2;
|
||||
char *b = 0;
|
||||
int size = IO_SNPRINTF(buff2, sizeof(buff2), "%s(%d): error: %s \n", file, line, user_message);
|
||||
if (size >= sizeof(buff2)) {
|
||||
size += 4;
|
||||
b = (char *)IO_ALLOCATE(size);
|
||||
size = IO_SNPRINTF(b, size, "%s(%d): error: %s \n", file, line, user_message);
|
||||
result = b;
|
||||
}
|
||||
|
||||
ret = IO_OutputError(result, size);
|
||||
if (ret == IO_ErrorResult_Exit) {
|
||||
IO_Exit(1);
|
||||
}
|
||||
|
||||
if (b) {
|
||||
IO_FREE(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (new_buffer) {
|
||||
IO_FREE(new_buffer);
|
||||
}
|
||||
|
||||
return ret == IO_ErrorResult_Break;
|
||||
}
|
||||
|
||||
IO_API void IO_Printf(const char *msg, ...) {
|
||||
va_list args1;
|
||||
va_list args2;
|
||||
char buff[2048];
|
||||
|
||||
va_start(args1, msg);
|
||||
va_copy(args2, args1);
|
||||
int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *new_buffer = 0;
|
||||
char *result = buff;
|
||||
if (size >= sizeof(buff)) {
|
||||
size += 4;
|
||||
new_buffer = (char *)IO_ALLOCATE(size);
|
||||
IO_VSNPRINTF(new_buffer, size, msg, args1);
|
||||
result = new_buffer;
|
||||
}
|
||||
va_end(args1);
|
||||
|
||||
if (IO_User_OutputMessage) {
|
||||
IO_User_OutputMessage(result, size);
|
||||
}
|
||||
else {
|
||||
IO_OutputMessage(result, size);
|
||||
}
|
||||
|
||||
if (new_buffer) {
|
||||
IO_FREE(new_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
IO_API bool IO__FatalError(char *msg) {
|
||||
int len = IO_Strlen(msg);
|
||||
IO_ErrorResult result = IO_OutputError(msg, len);
|
||||
if (result == IO_ErrorResult_Exit) {
|
||||
IO_Exit(1);
|
||||
}
|
||||
return result == IO_ErrorResult_Break;
|
||||
}
|
||||
|
||||
IO_API void IO_Print(char *msg) {
|
||||
int len = IO_Strlen(msg);
|
||||
if (IO_User_OutputMessage) {
|
||||
IO_User_OutputMessage(msg, len);
|
||||
}
|
||||
else {
|
||||
IO_OutputMessage(msg, len);
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
#pragma comment(lib, "user32")
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
IO_API bool IO_IsDebuggerPresent(void) {
|
||||
return IsDebuggerPresent();
|
||||
}
|
||||
|
||||
IO_API void IO_OutputMessage(char *str, int len) {
|
||||
if (IsDebuggerPresent()) {
|
||||
OutputDebugStringA(str);
|
||||
}
|
||||
printf("%.*s", len, str);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
|
||||
IO_ErrorResult result = IO_ErrorResult_Continue;
|
||||
IO_OutputMessage(str, len);
|
||||
|
||||
char *msg = str;
|
||||
if (str[len] != 0) {
|
||||
msg = (char *)IO_ALLOCATE(len + 1);
|
||||
for (int i = 0; i < len; i += 1) msg[i] = str[i];
|
||||
msg[len] = 0;
|
||||
}
|
||||
|
||||
OutputDebugStringA(msg);
|
||||
if (!IsDebuggerPresent()) {
|
||||
|
||||
// Limit size of error output message
|
||||
char tmp = 0;
|
||||
if (len > 4096) {
|
||||
tmp = str[4096];
|
||||
str[4096] = 0;
|
||||
}
|
||||
|
||||
MessageBoxA(0, msg, "Error!", 0);
|
||||
|
||||
if (tmp != 0) {
|
||||
str[4096] = tmp;
|
||||
}
|
||||
|
||||
result = IO_ErrorResult_Exit;
|
||||
}
|
||||
else {
|
||||
result = IO_ErrorResult_Break;
|
||||
}
|
||||
|
||||
if (msg != str) {
|
||||
IO_FREE(msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
IO_API void IO_Exit(int error_code) {
|
||||
ExitProcess(error_code);
|
||||
}
|
||||
#else // _WIN32 else // LIBC
|
||||
#include <stdio.h>
|
||||
|
||||
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
|
||||
fprintf(stderr, "%.*s", len, str);
|
||||
return IO_ErrorResult_Exit;
|
||||
}
|
||||
|
||||
IO_API void IO_OutputMessage(char *str, int len) {
|
||||
fprintf(stdout, "%.*s", len, str);
|
||||
}
|
||||
|
||||
IO_API void IO_Exit(int error_code) {
|
||||
exit(error_code);
|
||||
}
|
||||
|
||||
IO_API bool IO_IsDebuggerPresent(void) {
|
||||
return false;
|
||||
}
|
||||
#endif // LIBC
|
||||
#endif // IO_IMPLEMENTATION
|
||||
+1740
File diff suppressed because it is too large
Load Diff
-1740
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,558 @@
|
||||
#include "regex.h"
|
||||
|
||||
#ifndef RE_ASSERT
|
||||
#include <assert.h>
|
||||
#define RE_ASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
#ifndef RE_STRICT_ASSERT
|
||||
#define RE_STRICT_ASSERT RE_ASSERT
|
||||
#endif
|
||||
|
||||
#ifndef RE_MemoryZero
|
||||
#include <string.h>
|
||||
#define RE_MemoryZero(p, size) memset(p, 0, size)
|
||||
#endif
|
||||
|
||||
typedef struct RE__Arena {
|
||||
char *buff;
|
||||
RE_Int len;
|
||||
RE_Int cap;
|
||||
} RE_Arena;
|
||||
|
||||
struct RE_String {
|
||||
char *str;
|
||||
RE_Int len;
|
||||
};
|
||||
|
||||
struct RE_Utf32Result {
|
||||
uint32_t out_str;
|
||||
int advance;
|
||||
int error;
|
||||
};
|
||||
static RE_Regex RE_NullRegex;
|
||||
static char RE_NullChar;
|
||||
|
||||
struct RE_Parser {
|
||||
RE_String string;
|
||||
RE_Int i;
|
||||
RE_Regex *first;
|
||||
RE_Regex *last;
|
||||
};
|
||||
RE_API RE_Regex *RE1_ParseEx(RE_Arena *arena, char *string);
|
||||
RE_API RE_Regex *RE2_ParseEx(RE_Arena *arena, char *string, RE_Int len);
|
||||
|
||||
RE_StaticFunc void *RE_PushSize(RE_Arena *arena, RE_Int size) {
|
||||
if (arena->len + size > arena->cap) {
|
||||
RE_ASSERT(!"RE_Regex: Not enough memory passed for this regex");
|
||||
}
|
||||
void *result = arena->buff + arena->len;
|
||||
arena->len += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Arena RE_ArenaFromBuffer(char *buff, RE_Int size) {
|
||||
RE_Arena result;
|
||||
result.len = 0;
|
||||
result.cap = size;
|
||||
result.buff = buff;
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_String RE_Skip(RE_String string, RE_Int len) {
|
||||
if (len > string.len) len = string.len;
|
||||
RE_Int remain = string.len - len;
|
||||
RE_String result;
|
||||
result.str = string.str + len;
|
||||
result.len = remain;
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Int RE_StringLength(char *string) {
|
||||
RE_Int len = 0;
|
||||
while (*string++ != 0) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Utf32Result RE_ConvertUTF8ToUTF32(char *c, int max_advance) {
|
||||
RE_Utf32Result result;
|
||||
RE_MemoryZero(&result, sizeof(result));
|
||||
|
||||
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||
if (max_advance >= 1) {
|
||||
result.out_str = c[0];
|
||||
result.advance = 1;
|
||||
}
|
||||
else result.error = 1;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xe0) == 0xc0) {
|
||||
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||
result.advance = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf0) == 0xe0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||
if (max_advance >= 3) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||
result.advance = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf8) == 0xf0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||
if (max_advance >= 4) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||
result.advance = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define RE_DLL_QUEUE_REMOVE(first, last, node) \
|
||||
do { \
|
||||
if ((first) == (last)) { \
|
||||
(first) = (last) = 0; \
|
||||
} \
|
||||
else if ((last) == (node)) { \
|
||||
(last) = (last)->prev; \
|
||||
(last)->next = 0; \
|
||||
} \
|
||||
else if ((first) == (node)) { \
|
||||
(first) = (first)->next; \
|
||||
(first)->prev = 0; \
|
||||
} \
|
||||
else { \
|
||||
(node)->prev->next = (node)->next; \
|
||||
(node)->next->prev = (node)->prev; \
|
||||
} \
|
||||
if (node) (node)->prev = 0; \
|
||||
} while (0)
|
||||
|
||||
#define RE_DLL_QUEUE_ADD(f, l, node) \
|
||||
do { \
|
||||
if ((f) == 0) { \
|
||||
(f) = (l) = (node); \
|
||||
(node)->prev = 0; \
|
||||
(node)->next = 0; \
|
||||
} \
|
||||
else { \
|
||||
(l)->next = (node); \
|
||||
(node)->prev = (l); \
|
||||
(node)->next = 0; \
|
||||
(l) = (node); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
RE_StaticFunc char *RE_GetP(RE_Parser *P) {
|
||||
if (P->i >= P->string.len) return &RE_NullChar;
|
||||
return P->string.str + P->i;
|
||||
}
|
||||
|
||||
RE_StaticFunc char RE_Get(RE_Parser *P) {
|
||||
if (P->i >= P->string.len) return 0;
|
||||
return P->string.str[P->i];
|
||||
}
|
||||
|
||||
RE_StaticFunc char RE_Get1(RE_Parser *P) {
|
||||
if ((P->i + 1) >= P->string.len || P->i >= P->string.len) return 0;
|
||||
return P->string.str[P->i + 1];
|
||||
}
|
||||
|
||||
RE_StaticFunc void RE_Advance(RE_Parser *P) {
|
||||
if (P->i >= P->string.len) return;
|
||||
P->i += 1;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Regex *RE_ParseSingle(RE_Parser *P, RE_Arena *arena, RE_Regex **first, RE_Regex **last) {
|
||||
RE_Regex *regex = (RE_Regex *)RE_PushSize(arena, sizeof(RE_Regex));
|
||||
RE_MemoryZero(regex, sizeof(*regex));
|
||||
char *c = RE_GetP(P);
|
||||
RE_Int size_left = P->string.len - P->i;
|
||||
RE_Advance(P);
|
||||
switch (*c) {
|
||||
case ')': RE_STRICT_ASSERT(regex->kind != RE_MATCH_NULL && "Invalid regex syntax, ')' appeared without matching '('"); break;
|
||||
case '\0': RE_STRICT_ASSERT(regex->kind != RE_MATCH_NULL && "Invalid regex syntax, reached end of string obruptly"); break;
|
||||
case '.': regex->kind = RE_MATCH_ANY; break;
|
||||
case '^': regex->kind = RE_MATCH_FRONT; break;
|
||||
case '$': regex->kind = RE_MATCH_BACK; break;
|
||||
|
||||
case '*': {
|
||||
if (*last) {
|
||||
regex->kind = RE_MATCH_ZERO_OR_MORE;
|
||||
RE_Regex *prev = *last;
|
||||
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||
regex->child = prev;
|
||||
}
|
||||
else {
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, '*' is not attached to anything");
|
||||
}
|
||||
} break;
|
||||
|
||||
case '+': {
|
||||
if (*last) {
|
||||
regex->kind = RE_MATCH_ONE_OR_MORE;
|
||||
RE_Regex *prev = *last;
|
||||
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||
regex->child = prev;
|
||||
}
|
||||
else {
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, '+' is not attached to anything");
|
||||
}
|
||||
} break;
|
||||
|
||||
case '?': {
|
||||
if (*last) {
|
||||
regex->kind = RE_MATCH_ZERO_OR_ONE;
|
||||
RE_Regex *prev = *last;
|
||||
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||
regex->child = prev;
|
||||
}
|
||||
else {
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, '?' is not attached to anything");
|
||||
}
|
||||
} break;
|
||||
|
||||
case '[': {
|
||||
regex->kind = RE_MATCH_SELECTED;
|
||||
if (RE_Get(P) == '^') {
|
||||
regex->kind = RE_MATCH_NOT_SELECTED;
|
||||
RE_Advance(P);
|
||||
}
|
||||
while (RE_Get(P) != 0 && RE_Get(P) != ']') {
|
||||
RE_Regex *r = RE_ParseSingle(P, arena, ®ex->group.first, ®ex->group.last);
|
||||
if (r->kind == RE_MATCH_NULL) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
break;
|
||||
}
|
||||
if (r->kind == RE_MATCH_WORD && RE_Get(P) == '-') {
|
||||
char word = RE_Get1(P);
|
||||
if (word >= '!' && word <= '~') {
|
||||
RE_Advance(P);
|
||||
RE_Regex *right = RE_ParseSingle(P, arena, 0, 0);
|
||||
if (right->kind == RE_MATCH_NULL) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
break;
|
||||
}
|
||||
RE_ASSERT(right->kind == RE_MATCH_WORD);
|
||||
RE_ASSERT(right->word == word);
|
||||
r->word_min = word > r->word ? r->word : word;
|
||||
r->word_max = word > r->word ? word : r->word;
|
||||
r->kind = RE_MATCH_RANGE;
|
||||
}
|
||||
}
|
||||
RE_DLL_QUEUE_ADD(regex->group.first, regex->group.last, r);
|
||||
}
|
||||
RE_Advance(P);
|
||||
} break;
|
||||
|
||||
case '(': {
|
||||
regex->kind = RE_MATCH_GROUP;
|
||||
while (RE_Get(P) != 0 && RE_Get(P) != ')') {
|
||||
RE_Regex *r = RE_ParseSingle(P, arena, ®ex->group.first, ®ex->group.last);
|
||||
if (r->kind == RE_MATCH_NULL) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
break;
|
||||
}
|
||||
RE_DLL_QUEUE_ADD(regex->group.first, regex->group.last, r);
|
||||
}
|
||||
RE_Advance(P);
|
||||
} break;
|
||||
|
||||
case '|': {
|
||||
if (*last) {
|
||||
regex->kind = RE_MATCH_OR;
|
||||
RE_Regex *left = *last;
|
||||
RE_Regex *right = RE_ParseSingle(P, arena, first, last);
|
||||
if (right->kind == RE_MATCH_NULL) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, '|' appeared but it's right option is invalid");
|
||||
}
|
||||
else {
|
||||
RE_DLL_QUEUE_REMOVE(*first, *last, left);
|
||||
regex->left = left;
|
||||
regex->right = right;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case '\\': {
|
||||
regex->kind = RE_MATCH_WORD;
|
||||
regex->word = RE_Get(P);
|
||||
switch (regex->word) {
|
||||
case 'n': regex->word = '\n'; break;
|
||||
case 't': regex->word = '\t'; break;
|
||||
case 'r': regex->word = '\r'; break;
|
||||
case 'w': regex->kind = RE_MATCH_ANY_WORD; break;
|
||||
case 'd': regex->kind = RE_MATCH_ANY_DIGIT; break;
|
||||
case 's': regex->kind = RE_MATCH_ANY_WHITESPACE; break;
|
||||
case '\0': {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, escape '\\' followed by end of string");
|
||||
} break;
|
||||
}
|
||||
RE_Advance(P);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
regex->kind = RE_MATCH_WORD;
|
||||
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(c, (int)size_left);
|
||||
if (decode.error) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, string is an invalid utf8");
|
||||
}
|
||||
else {
|
||||
regex->word32 = decode.out_str;
|
||||
for (int i = 0; i < decode.advance - 1; i += 1)
|
||||
RE_Advance(P);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Int RE_MatchSingle(RE_Regex *regex, RE_String string) {
|
||||
switch (regex->kind) {
|
||||
case RE_MATCH_ZERO_OR_MORE: {
|
||||
RE_Int result = 0;
|
||||
for (; string.len;) {
|
||||
// @idea
|
||||
// In this case (asd)*(asd) we just quit with 0
|
||||
// when we meet asd
|
||||
// Maybe this should be collapsed in parsing stage/
|
||||
// asd should be combined with *asd etc. cause
|
||||
// now it's a bit weird but I dont know why you would
|
||||
// type that in the first place
|
||||
if (RE_MatchSingle(regex->next, string) != -1) break;
|
||||
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||
if (index == -1) break;
|
||||
string = RE_Skip(string, index);
|
||||
result += index;
|
||||
}
|
||||
return result;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ONE_OR_MORE: {
|
||||
RE_Int result = 0;
|
||||
for (; string.len;) {
|
||||
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||
if (index == -1) break;
|
||||
string = RE_Skip(string, index);
|
||||
result += index;
|
||||
}
|
||||
|
||||
if (result == 0) return -1;
|
||||
return result;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_OR: {
|
||||
RE_Int right = RE_MatchSingle(regex->right, string);
|
||||
RE_Int left = RE_MatchSingle(regex->left, string);
|
||||
if (left > right) return left;
|
||||
else return right;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_GROUP: {
|
||||
RE_Int result = 0;
|
||||
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||
if (string.len == 0) return -1;
|
||||
RE_Int index = RE_MatchSingle(it, string);
|
||||
if (index == -1) return -1;
|
||||
result += index;
|
||||
string = RE_Skip(string, index);
|
||||
}
|
||||
return result;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_NOT_SELECTED: {
|
||||
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||
RE_Int index = RE_MatchSingle(it, string);
|
||||
if (index != -1) return -1;
|
||||
}
|
||||
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(string.str, (int)string.len);
|
||||
if (decode.error) return -1;
|
||||
return decode.advance;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_SELECTED: {
|
||||
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||
RE_Int index = RE_MatchSingle(it, string);
|
||||
if (index != -1) return index;
|
||||
}
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_RANGE: {
|
||||
if (string.str[0] >= regex->word_min && string.str[0] <= regex->word_max)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
case RE_MATCH_ANY_WORD: {
|
||||
if ((string.str[0] >= 'a' && string.str[0] <= 'z') || (string.str[0] >= 'A' && string.str[0] <= 'Z'))
|
||||
return 1;
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ANY_DIGIT: {
|
||||
if (string.str[0] >= '0' && string.str[0] <= '9')
|
||||
return 1;
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ANY_WHITESPACE: {
|
||||
if (string.str[0] == ' ' || string.str[0] == '\n' || string.str[0] == '\t' || string.str[0] == '\r')
|
||||
return 1;
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ANY: {
|
||||
if (string.str[0] != '\n') {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ZERO_OR_ONE: {
|
||||
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||
if (index == -1) index = 0;
|
||||
return index;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_WORD: {
|
||||
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(string.str, (int)string.len);
|
||||
if (decode.error) return -1;
|
||||
if (decode.out_str == regex->word32) return decode.advance;
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_BACK:
|
||||
case RE_MATCH_NULL: return -1;
|
||||
|
||||
default: RE_ASSERT(!"Invalid codepath");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
RE_API bool RE1_AreEqual(char *regex, char *string) {
|
||||
char buff[4096];
|
||||
RE_Regex *re = RE1_Parse(buff, sizeof(buff), regex);
|
||||
bool result = RE3_AreEqual(re, string, RE_StringLength(string));
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_API bool RE2_AreEqual(RE_Regex *regex, char *string) {
|
||||
return RE3_AreEqual(regex, string, RE_StringLength(string));
|
||||
}
|
||||
|
||||
RE_API bool RE3_AreEqual(RE_Regex *regex, char *string, RE_Int len) {
|
||||
RE_Int result = RE3_MatchFront(regex, string, len, string);
|
||||
return result == len ? true : false;
|
||||
}
|
||||
|
||||
RE_API RE_Match RE1_Find(char *regex, char *string) {
|
||||
char buff[4096];
|
||||
RE_Regex *re = RE1_Parse(buff, sizeof(buff), regex);
|
||||
RE_Match result = RE2_Find(re, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_API RE_Match RE2_Find(RE_Regex *regex, char *string) {
|
||||
return RE3_Find(regex, string, RE_StringLength(string));
|
||||
}
|
||||
|
||||
RE_API RE_Match RE3_Find(RE_Regex *regex, char *string, RE_Int len) {
|
||||
RE_Match result;
|
||||
for (RE_Int i = 0; i < len; i += 1) {
|
||||
result.size = RE3_MatchFront(regex, string + i, len - i, string);
|
||||
if (result.size != -1) {
|
||||
result.pos = i;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.size = 0;
|
||||
result.pos = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_API RE_Match RE2_FindAgain(RE_Regex *regex, char *string, RE_Match prev_match) {
|
||||
return RE2_Find(regex, string + prev_match.pos);
|
||||
}
|
||||
|
||||
RE_API RE_Match RE3_FindAgain(RE_Regex *regex, char *string, RE_Int len, RE_Match prev_match) {
|
||||
return RE3_Find(regex, string + prev_match.pos, len - prev_match.pos);
|
||||
}
|
||||
|
||||
RE_API RE_Int RE3_MatchFront(RE_Regex *regex, char *string, RE_Int len, char *string_front) {
|
||||
RE_String re_string;
|
||||
re_string.str = string;
|
||||
re_string.len = len;
|
||||
RE_Int submatch_len = 0;
|
||||
for (RE_Regex *it = regex; it; it = it->next) {
|
||||
if (it->kind == RE_MATCH_FRONT) {
|
||||
if (re_string.str == string_front)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
if (it->kind == RE_MATCH_BACK) {
|
||||
if (re_string.len == 0)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
|
||||
RE_Int index = RE_MatchSingle(it, re_string);
|
||||
if (index == -1) return -1;
|
||||
re_string = RE_Skip(re_string, index);
|
||||
submatch_len += index;
|
||||
}
|
||||
return submatch_len;
|
||||
}
|
||||
|
||||
RE_API RE_Regex *RE1_ParseEx(RE_Arena *arena, char *string) {
|
||||
return RE2_ParseEx(arena, string, RE_StringLength(string));
|
||||
}
|
||||
|
||||
RE_API RE_Regex *RE2_ParseEx(RE_Arena *arena, char *string, RE_Int len) {
|
||||
RE_Parser P;
|
||||
RE_MemoryZero(&P, sizeof(P));
|
||||
P.string.str = string;
|
||||
P.string.len = len;
|
||||
|
||||
for (; P.i < P.string.len;) {
|
||||
RE_Regex *regex = RE_ParseSingle(&P, arena, &P.first, &P.last);
|
||||
RE_DLL_QUEUE_ADD(P.first, P.last, regex);
|
||||
if (regex->kind == RE_MATCH_NULL) {
|
||||
P.first = &RE_NullRegex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return P.first;
|
||||
}
|
||||
|
||||
RE_API RE_Regex *RE1_Parse(char *buff, RE_Int buffsize, char *string) {
|
||||
RE_Arena arena = RE_ArenaFromBuffer(buff, buffsize);
|
||||
RE_Regex *result = RE1_ParseEx(&arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_API RE_Regex *RE2_Parse(char *buff, RE_Int buffsize, char *string, RE_Int len) {
|
||||
RE_Arena arena = RE_ArenaFromBuffer(buff, buffsize);
|
||||
RE_Regex *result = RE2_ParseEx(&arena, string, len);
|
||||
return result;
|
||||
}
|
||||
@@ -95,562 +95,3 @@ RE_API RE_Regex *RE1_Parse(char *buff, RE_Int buffsize, char *string);
|
||||
RE_API RE_Regex *RE2_Parse(char *buff, RE_Int buffsize, char *string, RE_Int len);
|
||||
|
||||
#endif // RE_HEADER
|
||||
#ifdef RE_IMPLEMENTATION
|
||||
#ifndef RE_ASSERT
|
||||
#include <assert.h>
|
||||
#define RE_ASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
#ifndef RE_STRICT_ASSERT
|
||||
#define RE_STRICT_ASSERT RE_ASSERT
|
||||
#endif
|
||||
|
||||
#ifndef RE_MemoryZero
|
||||
#include <string.h>
|
||||
#define RE_MemoryZero(p, size) memset(p, 0, size)
|
||||
#endif
|
||||
|
||||
typedef struct RE__Arena {
|
||||
char *buff;
|
||||
RE_Int len;
|
||||
RE_Int cap;
|
||||
} RE_Arena;
|
||||
|
||||
struct RE_String {
|
||||
char *str;
|
||||
RE_Int len;
|
||||
};
|
||||
|
||||
struct RE_Utf32Result {
|
||||
uint32_t out_str;
|
||||
int advance;
|
||||
int error;
|
||||
};
|
||||
static RE_Regex RE_NullRegex;
|
||||
static char RE_NullChar;
|
||||
|
||||
struct RE_Parser {
|
||||
RE_String string;
|
||||
RE_Int i;
|
||||
RE_Regex *first;
|
||||
RE_Regex *last;
|
||||
};
|
||||
RE_API RE_Regex *RE1_ParseEx(RE_Arena *arena, char *string);
|
||||
RE_API RE_Regex *RE2_ParseEx(RE_Arena *arena, char *string, RE_Int len);
|
||||
|
||||
RE_StaticFunc void *RE_PushSize(RE_Arena *arena, RE_Int size) {
|
||||
if (arena->len + size > arena->cap) {
|
||||
RE_ASSERT(!"RE_Regex: Not enough memory passed for this regex");
|
||||
}
|
||||
void *result = arena->buff + arena->len;
|
||||
arena->len += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Arena RE_ArenaFromBuffer(char *buff, RE_Int size) {
|
||||
RE_Arena result;
|
||||
result.len = 0;
|
||||
result.cap = size;
|
||||
result.buff = buff;
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_String RE_Skip(RE_String string, RE_Int len) {
|
||||
if (len > string.len) len = string.len;
|
||||
RE_Int remain = string.len - len;
|
||||
RE_String result;
|
||||
result.str = string.str + len;
|
||||
result.len = remain;
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Int RE_StringLength(char *string) {
|
||||
RE_Int len = 0;
|
||||
while (*string++ != 0) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Utf32Result RE_ConvertUTF8ToUTF32(char *c, int max_advance) {
|
||||
RE_Utf32Result result;
|
||||
RE_MemoryZero(&result, sizeof(result));
|
||||
|
||||
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||
if (max_advance >= 1) {
|
||||
result.out_str = c[0];
|
||||
result.advance = 1;
|
||||
}
|
||||
else result.error = 1;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xe0) == 0xc0) {
|
||||
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||
result.advance = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf0) == 0xe0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||
if (max_advance >= 3) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||
result.advance = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf8) == 0xf0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||
if (max_advance >= 4) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||
result.advance = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define RE_DLL_QUEUE_REMOVE(first, last, node) \
|
||||
do { \
|
||||
if ((first) == (last)) { \
|
||||
(first) = (last) = 0; \
|
||||
} \
|
||||
else if ((last) == (node)) { \
|
||||
(last) = (last)->prev; \
|
||||
(last)->next = 0; \
|
||||
} \
|
||||
else if ((first) == (node)) { \
|
||||
(first) = (first)->next; \
|
||||
(first)->prev = 0; \
|
||||
} \
|
||||
else { \
|
||||
(node)->prev->next = (node)->next; \
|
||||
(node)->next->prev = (node)->prev; \
|
||||
} \
|
||||
if (node) (node)->prev = 0; \
|
||||
} while (0)
|
||||
|
||||
#define RE_DLL_QUEUE_ADD(f, l, node) \
|
||||
do { \
|
||||
if ((f) == 0) { \
|
||||
(f) = (l) = (node); \
|
||||
(node)->prev = 0; \
|
||||
(node)->next = 0; \
|
||||
} \
|
||||
else { \
|
||||
(l)->next = (node); \
|
||||
(node)->prev = (l); \
|
||||
(node)->next = 0; \
|
||||
(l) = (node); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
RE_StaticFunc char *RE_GetP(RE_Parser *P) {
|
||||
if (P->i >= P->string.len) return &RE_NullChar;
|
||||
return P->string.str + P->i;
|
||||
}
|
||||
|
||||
RE_StaticFunc char RE_Get(RE_Parser *P) {
|
||||
if (P->i >= P->string.len) return 0;
|
||||
return P->string.str[P->i];
|
||||
}
|
||||
|
||||
RE_StaticFunc char RE_Get1(RE_Parser *P) {
|
||||
if ((P->i + 1) >= P->string.len || P->i >= P->string.len) return 0;
|
||||
return P->string.str[P->i + 1];
|
||||
}
|
||||
|
||||
RE_StaticFunc void RE_Advance(RE_Parser *P) {
|
||||
if (P->i >= P->string.len) return;
|
||||
P->i += 1;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Regex *RE_ParseSingle(RE_Parser *P, RE_Arena *arena, RE_Regex **first, RE_Regex **last) {
|
||||
RE_Regex *regex = (RE_Regex *)RE_PushSize(arena, sizeof(RE_Regex));
|
||||
RE_MemoryZero(regex, sizeof(*regex));
|
||||
char *c = RE_GetP(P);
|
||||
RE_Int size_left = P->string.len - P->i;
|
||||
RE_Advance(P);
|
||||
switch (*c) {
|
||||
case ')': RE_STRICT_ASSERT(regex->kind != RE_MATCH_NULL && "Invalid regex syntax, ')' appeared without matching '('"); break;
|
||||
case '\0': RE_STRICT_ASSERT(regex->kind != RE_MATCH_NULL && "Invalid regex syntax, reached end of string obruptly"); break;
|
||||
case '.': regex->kind = RE_MATCH_ANY; break;
|
||||
case '^': regex->kind = RE_MATCH_FRONT; break;
|
||||
case '$': regex->kind = RE_MATCH_BACK; break;
|
||||
|
||||
case '*': {
|
||||
if (*last) {
|
||||
regex->kind = RE_MATCH_ZERO_OR_MORE;
|
||||
RE_Regex *prev = *last;
|
||||
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||
regex->child = prev;
|
||||
}
|
||||
else {
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, '*' is not attached to anything");
|
||||
}
|
||||
} break;
|
||||
|
||||
case '+': {
|
||||
if (*last) {
|
||||
regex->kind = RE_MATCH_ONE_OR_MORE;
|
||||
RE_Regex *prev = *last;
|
||||
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||
regex->child = prev;
|
||||
}
|
||||
else {
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, '+' is not attached to anything");
|
||||
}
|
||||
} break;
|
||||
|
||||
case '?': {
|
||||
if (*last) {
|
||||
regex->kind = RE_MATCH_ZERO_OR_ONE;
|
||||
RE_Regex *prev = *last;
|
||||
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
|
||||
regex->child = prev;
|
||||
}
|
||||
else {
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, '?' is not attached to anything");
|
||||
}
|
||||
} break;
|
||||
|
||||
case '[': {
|
||||
regex->kind = RE_MATCH_SELECTED;
|
||||
if (RE_Get(P) == '^') {
|
||||
regex->kind = RE_MATCH_NOT_SELECTED;
|
||||
RE_Advance(P);
|
||||
}
|
||||
while (RE_Get(P) != 0 && RE_Get(P) != ']') {
|
||||
RE_Regex *r = RE_ParseSingle(P, arena, ®ex->group.first, ®ex->group.last);
|
||||
if (r->kind == RE_MATCH_NULL) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
break;
|
||||
}
|
||||
if (r->kind == RE_MATCH_WORD && RE_Get(P) == '-') {
|
||||
char word = RE_Get1(P);
|
||||
if (word >= '!' && word <= '~') {
|
||||
RE_Advance(P);
|
||||
RE_Regex *right = RE_ParseSingle(P, arena, 0, 0);
|
||||
if (right->kind == RE_MATCH_NULL) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
break;
|
||||
}
|
||||
RE_ASSERT(right->kind == RE_MATCH_WORD);
|
||||
RE_ASSERT(right->word == word);
|
||||
r->word_min = word > r->word ? r->word : word;
|
||||
r->word_max = word > r->word ? word : r->word;
|
||||
r->kind = RE_MATCH_RANGE;
|
||||
}
|
||||
}
|
||||
RE_DLL_QUEUE_ADD(regex->group.first, regex->group.last, r);
|
||||
}
|
||||
RE_Advance(P);
|
||||
} break;
|
||||
|
||||
case '(': {
|
||||
regex->kind = RE_MATCH_GROUP;
|
||||
while (RE_Get(P) != 0 && RE_Get(P) != ')') {
|
||||
RE_Regex *r = RE_ParseSingle(P, arena, ®ex->group.first, ®ex->group.last);
|
||||
if (r->kind == RE_MATCH_NULL) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
break;
|
||||
}
|
||||
RE_DLL_QUEUE_ADD(regex->group.first, regex->group.last, r);
|
||||
}
|
||||
RE_Advance(P);
|
||||
} break;
|
||||
|
||||
case '|': {
|
||||
if (*last) {
|
||||
regex->kind = RE_MATCH_OR;
|
||||
RE_Regex *left = *last;
|
||||
RE_Regex *right = RE_ParseSingle(P, arena, first, last);
|
||||
if (right->kind == RE_MATCH_NULL) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, '|' appeared but it's right option is invalid");
|
||||
}
|
||||
else {
|
||||
RE_DLL_QUEUE_REMOVE(*first, *last, left);
|
||||
regex->left = left;
|
||||
regex->right = right;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case '\\': {
|
||||
regex->kind = RE_MATCH_WORD;
|
||||
regex->word = RE_Get(P);
|
||||
switch (regex->word) {
|
||||
case 'n': regex->word = '\n'; break;
|
||||
case 't': regex->word = '\t'; break;
|
||||
case 'r': regex->word = '\r'; break;
|
||||
case 'w': regex->kind = RE_MATCH_ANY_WORD; break;
|
||||
case 'd': regex->kind = RE_MATCH_ANY_DIGIT; break;
|
||||
case 's': regex->kind = RE_MATCH_ANY_WHITESPACE; break;
|
||||
case '\0': {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, escape '\\' followed by end of string");
|
||||
} break;
|
||||
}
|
||||
RE_Advance(P);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
regex->kind = RE_MATCH_WORD;
|
||||
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(c, (int)size_left);
|
||||
if (decode.error) {
|
||||
regex->kind = RE_MATCH_NULL;
|
||||
RE_STRICT_ASSERT(!"Invalid regex syntax, string is an invalid utf8");
|
||||
}
|
||||
else {
|
||||
regex->word32 = decode.out_str;
|
||||
for (int i = 0; i < decode.advance - 1; i += 1)
|
||||
RE_Advance(P);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
RE_StaticFunc RE_Int RE_MatchSingle(RE_Regex *regex, RE_String string) {
|
||||
switch (regex->kind) {
|
||||
case RE_MATCH_ZERO_OR_MORE: {
|
||||
RE_Int result = 0;
|
||||
for (; string.len;) {
|
||||
// @idea
|
||||
// In this case (asd)*(asd) we just quit with 0
|
||||
// when we meet asd
|
||||
// Maybe this should be collapsed in parsing stage/
|
||||
// asd should be combined with *asd etc. cause
|
||||
// now it's a bit weird but I dont know why you would
|
||||
// type that in the first place
|
||||
if (RE_MatchSingle(regex->next, string) != -1) break;
|
||||
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||
if (index == -1) break;
|
||||
string = RE_Skip(string, index);
|
||||
result += index;
|
||||
}
|
||||
return result;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ONE_OR_MORE: {
|
||||
RE_Int result = 0;
|
||||
for (; string.len;) {
|
||||
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||
if (index == -1) break;
|
||||
string = RE_Skip(string, index);
|
||||
result += index;
|
||||
}
|
||||
|
||||
if (result == 0) return -1;
|
||||
return result;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_OR: {
|
||||
RE_Int right = RE_MatchSingle(regex->right, string);
|
||||
RE_Int left = RE_MatchSingle(regex->left, string);
|
||||
if (left > right) return left;
|
||||
else return right;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_GROUP: {
|
||||
RE_Int result = 0;
|
||||
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||
if (string.len == 0) return -1;
|
||||
RE_Int index = RE_MatchSingle(it, string);
|
||||
if (index == -1) return -1;
|
||||
result += index;
|
||||
string = RE_Skip(string, index);
|
||||
}
|
||||
return result;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_NOT_SELECTED: {
|
||||
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||
RE_Int index = RE_MatchSingle(it, string);
|
||||
if (index != -1) return -1;
|
||||
}
|
||||
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(string.str, (int)string.len);
|
||||
if (decode.error) return -1;
|
||||
return decode.advance;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_SELECTED: {
|
||||
for (RE_Regex *it = regex->group.first; it; it = it->next) {
|
||||
RE_Int index = RE_MatchSingle(it, string);
|
||||
if (index != -1) return index;
|
||||
}
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_RANGE: {
|
||||
if (string.str[0] >= regex->word_min && string.str[0] <= regex->word_max)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
case RE_MATCH_ANY_WORD: {
|
||||
if ((string.str[0] >= 'a' && string.str[0] <= 'z') || (string.str[0] >= 'A' && string.str[0] <= 'Z'))
|
||||
return 1;
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ANY_DIGIT: {
|
||||
if (string.str[0] >= '0' && string.str[0] <= '9')
|
||||
return 1;
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ANY_WHITESPACE: {
|
||||
if (string.str[0] == ' ' || string.str[0] == '\n' || string.str[0] == '\t' || string.str[0] == '\r')
|
||||
return 1;
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ANY: {
|
||||
if (string.str[0] != '\n') {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_ZERO_OR_ONE: {
|
||||
RE_Int index = RE_MatchSingle(regex->child, string);
|
||||
if (index == -1) index = 0;
|
||||
return index;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_WORD: {
|
||||
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(string.str, (int)string.len);
|
||||
if (decode.error) return -1;
|
||||
if (decode.out_str == regex->word32) return decode.advance;
|
||||
return -1;
|
||||
} break;
|
||||
|
||||
case RE_MATCH_BACK:
|
||||
case RE_MATCH_NULL: return -1;
|
||||
|
||||
default: RE_ASSERT(!"Invalid codepath");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
RE_API bool RE1_AreEqual(char *regex, char *string) {
|
||||
char buff[4096];
|
||||
RE_Regex *re = RE1_Parse(buff, sizeof(buff), regex);
|
||||
bool result = RE3_AreEqual(re, string, RE_StringLength(string));
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_API bool RE2_AreEqual(RE_Regex *regex, char *string) {
|
||||
return RE3_AreEqual(regex, string, RE_StringLength(string));
|
||||
}
|
||||
|
||||
RE_API bool RE3_AreEqual(RE_Regex *regex, char *string, RE_Int len) {
|
||||
RE_Int result = RE3_MatchFront(regex, string, len, string);
|
||||
return result == len ? true : false;
|
||||
}
|
||||
|
||||
RE_API RE_Match RE1_Find(char *regex, char *string) {
|
||||
char buff[4096];
|
||||
RE_Regex *re = RE1_Parse(buff, sizeof(buff), regex);
|
||||
RE_Match result = RE2_Find(re, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_API RE_Match RE2_Find(RE_Regex *regex, char *string) {
|
||||
return RE3_Find(regex, string, RE_StringLength(string));
|
||||
}
|
||||
|
||||
RE_API RE_Match RE3_Find(RE_Regex *regex, char *string, RE_Int len) {
|
||||
RE_Match result;
|
||||
for (RE_Int i = 0; i < len; i += 1) {
|
||||
result.size = RE3_MatchFront(regex, string + i, len - i, string);
|
||||
if (result.size != -1) {
|
||||
result.pos = i;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.size = 0;
|
||||
result.pos = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_API RE_Match RE2_FindAgain(RE_Regex *regex, char *string, RE_Match prev_match) {
|
||||
return RE2_Find(regex, string + prev_match.pos);
|
||||
}
|
||||
|
||||
RE_API RE_Match RE3_FindAgain(RE_Regex *regex, char *string, RE_Int len, RE_Match prev_match) {
|
||||
return RE3_Find(regex, string + prev_match.pos, len - prev_match.pos);
|
||||
}
|
||||
|
||||
RE_API RE_Int RE3_MatchFront(RE_Regex *regex, char *string, RE_Int len, char *string_front) {
|
||||
RE_String re_string;
|
||||
re_string.str = string;
|
||||
re_string.len = len;
|
||||
RE_Int submatch_len = 0;
|
||||
for (RE_Regex *it = regex; it; it = it->next) {
|
||||
if (it->kind == RE_MATCH_FRONT) {
|
||||
if (re_string.str == string_front)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
if (it->kind == RE_MATCH_BACK) {
|
||||
if (re_string.len == 0)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
|
||||
RE_Int index = RE_MatchSingle(it, re_string);
|
||||
if (index == -1) return -1;
|
||||
re_string = RE_Skip(re_string, index);
|
||||
submatch_len += index;
|
||||
}
|
||||
return submatch_len;
|
||||
}
|
||||
|
||||
RE_API RE_Regex *RE1_ParseEx(RE_Arena *arena, char *string) {
|
||||
return RE2_ParseEx(arena, string, RE_StringLength(string));
|
||||
}
|
||||
|
||||
RE_API RE_Regex *RE2_ParseEx(RE_Arena *arena, char *string, RE_Int len) {
|
||||
RE_Parser P;
|
||||
RE_MemoryZero(&P, sizeof(P));
|
||||
P.string.str = string;
|
||||
P.string.len = len;
|
||||
|
||||
for (; P.i < P.string.len;) {
|
||||
RE_Regex *regex = RE_ParseSingle(&P, arena, &P.first, &P.last);
|
||||
RE_DLL_QUEUE_ADD(P.first, P.last, regex);
|
||||
if (regex->kind == RE_MATCH_NULL) {
|
||||
P.first = &RE_NullRegex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return P.first;
|
||||
}
|
||||
|
||||
RE_API RE_Regex *RE1_Parse(char *buff, RE_Int buffsize, char *string) {
|
||||
RE_Arena arena = RE_ArenaFromBuffer(buff, buffsize);
|
||||
RE_Regex *result = RE1_ParseEx(&arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
RE_API RE_Regex *RE2_Parse(char *buff, RE_Int buffsize, char *string, RE_Int len) {
|
||||
RE_Arena arena = RE_ArenaFromBuffer(buff, buffsize);
|
||||
RE_Regex *result = RE2_ParseEx(&arena, string, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,493 @@
|
||||
#include "string.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifndef S8_VSNPRINTF
|
||||
#include <stdio.h>
|
||||
#define S8_VSNPRINTF vsnprintf
|
||||
#endif
|
||||
|
||||
#ifndef S8_ALLOCATE
|
||||
#include <stdlib.h>
|
||||
#define S8_ALLOCATE(allocator, size) malloc(size)
|
||||
#endif
|
||||
|
||||
#ifndef S8_ASSERT
|
||||
#include <assert.h>
|
||||
#define S8_ASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
#ifndef S8_MemoryCopy
|
||||
#include <string.h>
|
||||
#define S8_MemoryCopy(dst, src, s) memcpy(dst, src, s)
|
||||
#endif
|
||||
|
||||
#ifndef S8_StaticFunc
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define S8_StaticFunc __attribute__((unused)) static
|
||||
#else
|
||||
#define S8_StaticFunc static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
S8_StaticFunc int64_t S8__ClampTop(int64_t val, int64_t max) {
|
||||
if (val > max) val = max;
|
||||
return val;
|
||||
}
|
||||
|
||||
S8_API char CHAR_ToLowerCase(char a) {
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
S8_API char CHAR_ToUpperCase(char a) {
|
||||
if (a >= 'a' && a <= 'z') a -= 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsWhitespace(char w) {
|
||||
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsAlphabetic(char a) {
|
||||
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsIdent(char a) {
|
||||
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsDigit(char a) {
|
||||
bool result = a >= '0' && a <= '9';
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsAlphanumeric(char a) {
|
||||
bool result = CHAR_IsDigit(a) || CHAR_IsAlphabetic(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case) {
|
||||
if (a.len != b.len) return false;
|
||||
for (int64_t i = 0; i < a.len; i++) {
|
||||
char A = a.str[i];
|
||||
char B = b.str[i];
|
||||
if (ignore_case & S8_IGNORE_CASE) {
|
||||
A = CHAR_ToLowerCase(A);
|
||||
B = CHAR_ToLowerCase(B);
|
||||
}
|
||||
if (A != B)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case) {
|
||||
S8_String a_end = S8_GetPostfix(a, end.len);
|
||||
bool result = S8_AreEqual(end, a_end, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case) {
|
||||
S8_String a_start = S8_GetPrefix(a, start.len);
|
||||
bool result = S8_AreEqual(start, a_start, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Make(char *str, int64_t len) {
|
||||
S8_String result;
|
||||
result.str = (char *)str;
|
||||
result.len = len;
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string) {
|
||||
char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (string.len + 1));
|
||||
S8_MemoryCopy(copy, string.str, string.len);
|
||||
copy[string.len] = 0;
|
||||
S8_String result = S8_Make(copy, string.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API void S8_NormalizePath(S8_String s) {
|
||||
for (int64_t i = 0; i < s.len; i++) {
|
||||
if (s.str[i] == '\\')
|
||||
s.str[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Chop(S8_String string, int64_t len) {
|
||||
len = S8__ClampTop(len, string.len);
|
||||
S8_String result = S8_Make(string.str, string.len - len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Skip(S8_String string, int64_t len) {
|
||||
len = S8__ClampTop(len, string.len);
|
||||
int64_t remain = string.len - len;
|
||||
S8_String result = S8_Make(string.str + len, remain);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool S8_IsPointerInside(S8_String string, char *p) {
|
||||
uintptr_t pointer = (uintptr_t)p;
|
||||
uintptr_t start = (uintptr_t)string.str;
|
||||
uintptr_t stop = start + (uintptr_t)string.len;
|
||||
bool result = pointer >= start && pointer < stop;
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_SkipToP(S8_String string, char *p) {
|
||||
if (S8_IsPointerInside(string, p)) {
|
||||
S8_String result = S8_Make(p, p - string.str);
|
||||
return result;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_SkipPast(S8_String string, S8_String a) {
|
||||
if (S8_IsPointerInside(string, a.str)) {
|
||||
S8_String on_p = S8_Make(a.str, a.str - string.str);
|
||||
S8_String result = S8_Skip(on_p, a.len);
|
||||
return result;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_GetPostfix(S8_String string, int64_t len) {
|
||||
len = S8__ClampTop(len, string.len);
|
||||
int64_t remain_len = string.len - len;
|
||||
S8_String result = S8_Make(string.str + remain_len, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_GetPrefix(S8_String string, int64_t len) {
|
||||
len = S8__ClampTop(len, string.len);
|
||||
S8_String result = S8_Make(string.str, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index) {
|
||||
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
|
||||
if (first_index < 0) first_index = string.len + first_index;
|
||||
S8_ASSERT(first_index < one_past_last_index && "S8_Slice, first_index is bigger then one_past_last_index");
|
||||
S8_ASSERT(string.len > 0 && "Slicing string of length 0! Might be an error!");
|
||||
S8_String result = string;
|
||||
if (string.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = S8__ClampTop(first_index, string.len - 1);
|
||||
one_past_last_index = S8__ClampTop(one_past_last_index, string.len);
|
||||
result.str += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
}
|
||||
else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Trim(S8_String string) {
|
||||
if (string.len == 0)
|
||||
return string;
|
||||
|
||||
int64_t whitespace_begin = 0;
|
||||
for (; whitespace_begin < string.len; whitespace_begin++) {
|
||||
if (!CHAR_IsWhitespace(string.str[whitespace_begin])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != whitespace_begin; whitespace_end--) {
|
||||
if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (whitespace_begin == whitespace_end) {
|
||||
string.len = 0;
|
||||
}
|
||||
else {
|
||||
string = S8_Slice(string, whitespace_begin, whitespace_end);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_TrimEnd(S8_String string) {
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != 0; whitespace_end--) {
|
||||
if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
S8_String result = S8_GetPrefix(string, whitespace_end);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s) {
|
||||
S8_String copy = S8_Copy(allocator, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = CHAR_ToLowerCase(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s) {
|
||||
S8_String copy = S8_Copy(allocator, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = CHAR_ToUpperCase(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
S8_API bool S8_Find(S8_String string, S8_String find, unsigned flags, int64_t *index_out) {
|
||||
bool result = false;
|
||||
if (flags & S8_MATCH_FIND_LAST) {
|
||||
for (int64_t i = string.len; i != 0; i--) {
|
||||
int64_t index = i - 1;
|
||||
S8_String substring = S8_Slice(string, index, index + find.len);
|
||||
if (S8_AreEqual(substring, find, flags)) {
|
||||
if (index_out)
|
||||
*index_out = index;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int64_t i = 0; i < string.len; i++) {
|
||||
S8_String substring = S8_Slice(string, i, i + find.len);
|
||||
if (S8_AreEqual(substring, find, flags)) {
|
||||
if (index_out)
|
||||
*index_out = i;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, unsigned flags) {
|
||||
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
int64_t index = 0;
|
||||
while (S8_Find(string, find, flags, &index)) {
|
||||
S8_String before_match = S8_Make(string.str, index);
|
||||
S8_AddNode(allocator, &result, before_match);
|
||||
if (flags & S8_SPLIT_INCLUSIVE) {
|
||||
S8_String match = S8_Make(string.str + index, find.len);
|
||||
S8_AddNode(allocator, &result, match);
|
||||
}
|
||||
string = S8_Skip(string, index + find.len);
|
||||
}
|
||||
if (string.len) S8_AddNode(allocator, &result, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator) {
|
||||
if (list.node_count == 0) return S8_MakeEmpty();
|
||||
if (list.char_count == 0) return S8_MakeEmpty();
|
||||
// S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||
int64_t base_size = (list.char_count + 1);
|
||||
int64_t sep_size = (list.node_count - 1) * separator.len;
|
||||
int64_t size = base_size + sep_size;
|
||||
char *buff = (char *)S8_ALLOCATE(allocator, sizeof(char) * size);
|
||||
S8_String string = S8_Make(buff, 0);
|
||||
for (S8_Node *it = list.first; it; it = it->next) {
|
||||
S8_ASSERT(string.len + it->string.len <= size);
|
||||
S8_MemoryCopy(string.str + string.len, it->string.str, it->string.len);
|
||||
string.len += it->string.len;
|
||||
if (it != list.last) {
|
||||
S8_MemoryCopy(string.str + string.len, separator.str, separator.len);
|
||||
string.len += separator.len;
|
||||
}
|
||||
}
|
||||
S8_ASSERT(string.len == size - 1);
|
||||
string.str[size] = 0;
|
||||
return string;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list) {
|
||||
return S8_MergeWithSeparator(allocator, list, S8_Lit(""));
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, unsigned flags) {
|
||||
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||
S8_List list = S8_Split(allocator, string, replace, flags | S8_SPLIT_INCLUSIVE);
|
||||
for (S8_Node *it = list.first; it; it = it->next) {
|
||||
if (S8_AreEqual(it->string, replace, flags)) {
|
||||
S8_ReplaceNodeString(&list, it, with);
|
||||
}
|
||||
}
|
||||
S8_String result = S8_Merge(allocator, list);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, unsigned flags) { // @untested
|
||||
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
int64_t index = 0;
|
||||
while (S8_Find(string, find, flags, &index)) {
|
||||
S8_String match = S8_Make(string.str + index, find.len);
|
||||
S8_AddNode(allocator, &result, match);
|
||||
string = S8_Skip(string, index + find.len);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ChopLastSlash(S8_String s) {
|
||||
S8_String result = s;
|
||||
S8_Find(s, S8_Lit("/"), S8_MATCH_FIND_LAST, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ChopLastPeriod(S8_String s) {
|
||||
S8_String result = s;
|
||||
S8_Find(s, S8_Lit("."), S8_MATCH_FIND_LAST, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_SkipToLastSlash(S8_String s) {
|
||||
int64_t pos;
|
||||
S8_String result = s;
|
||||
if (S8_Find(s, S8_Lit("/"), S8_MATCH_FIND_LAST, &pos)) {
|
||||
result = S8_Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_SkipToLastPeriod(S8_String s) {
|
||||
int64_t pos;
|
||||
S8_String result = s;
|
||||
if (S8_Find(s, S8_Lit("."), S8_MATCH_FIND_LAST, &pos)) {
|
||||
result = S8_Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API int64_t S8_Length(char *string) {
|
||||
int64_t len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
S8_API int64_t S8_WideLength(wchar_t *string) {
|
||||
int64_t len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_MakeFromChar(char *string) {
|
||||
S8_String result;
|
||||
result.str = (char *)string;
|
||||
result.len = S8_Length(string);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_MakeEmpty(void) {
|
||||
return S8_Make(0, 0);
|
||||
}
|
||||
|
||||
S8_API S8_List S8_MakeEmptyList(void) {
|
||||
S8_List result;
|
||||
result.first = 0;
|
||||
result.last = 0;
|
||||
result.char_count = 0;
|
||||
result.node_count = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1) {
|
||||
va_list args2;
|
||||
va_copy(args2, args1);
|
||||
int64_t len = S8_VSNPRINTF(0, 0, str, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *result = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1));
|
||||
S8_VSNPRINTF(result, (int)(len + 1), str, args1);
|
||||
S8_String res = S8_Make(result, len);
|
||||
return res;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) {
|
||||
S8_FORMAT(allocator, str, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string) {
|
||||
S8_Node *result = (S8_Node *)S8_ALLOCATE(allocator, sizeof(S8_Node));
|
||||
result->string = string;
|
||||
result->next = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string) {
|
||||
list->char_count -= node->string.len;
|
||||
list->char_count += new_string.len;
|
||||
node->string = new_string;
|
||||
}
|
||||
|
||||
S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node) {
|
||||
if (list->first) {
|
||||
list->last->next = node;
|
||||
list->last = list->last->next;
|
||||
}
|
||||
else {
|
||||
list->first = list->last = node;
|
||||
}
|
||||
list->node_count += 1;
|
||||
list->char_count += node->string.len;
|
||||
}
|
||||
|
||||
S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count) {
|
||||
for (int i = 0; i < count; i += 1) {
|
||||
S8_String s = S8_MakeFromChar(array[i]);
|
||||
S8_AddNode(allocator, list, s);
|
||||
}
|
||||
}
|
||||
|
||||
S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count) {
|
||||
for (int i = 0; i < count; i += 1) {
|
||||
S8_AddF(allocator, list, "%s%s", prefix, array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a) {
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
S8_AddNode(allocator, &result, a);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a) {
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b) {
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||
for (S8_Node *it = b.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string) {
|
||||
S8_Node *node = S8_CreateNode(allocator, string);
|
||||
S8_AddExistingNode(list, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) {
|
||||
S8_FORMAT(allocator, str, result);
|
||||
S8_AddNode(allocator, list, result);
|
||||
return result;
|
||||
}
|
||||
@@ -3,16 +3,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Preprocessor Input: ALLOCATOR_TYPE
|
||||
|
||||
#ifndef S8_FN
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define S8_FN __attribute__((unused)) static
|
||||
#else
|
||||
#define S8_FN static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef S8_API
|
||||
#ifdef __cplusplus
|
||||
#define S8_API extern "C"
|
||||
@@ -127,490 +117,3 @@ S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String stri
|
||||
S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...);
|
||||
|
||||
#endif // S8_HEADER
|
||||
#ifdef S8_IMPLEMENTATION
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifndef S8_VSNPRINTF
|
||||
#include <stdio.h>
|
||||
#define S8_VSNPRINTF vsnprintf
|
||||
#endif
|
||||
|
||||
#ifndef S8_ALLOCATE
|
||||
#include <stdlib.h>
|
||||
#define S8_ALLOCATE(allocator, size) malloc(size)
|
||||
#endif
|
||||
|
||||
#ifndef S8_ASSERT
|
||||
#include <assert.h>
|
||||
#define S8_ASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
#ifndef S8_MemoryCopy
|
||||
#include <string.h>
|
||||
#define S8_MemoryCopy(dst, src, s) memcpy(dst, src, s)
|
||||
#endif
|
||||
|
||||
S8_FN int64_t S8__ClampTop(int64_t val, int64_t max) {
|
||||
if (val > max) val = max;
|
||||
return val;
|
||||
}
|
||||
|
||||
S8_API char CHAR_ToLowerCase(char a) {
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
S8_API char CHAR_ToUpperCase(char a) {
|
||||
if (a >= 'a' && a <= 'z') a -= 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsWhitespace(char w) {
|
||||
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsAlphabetic(char a) {
|
||||
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsIdent(char a) {
|
||||
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsDigit(char a) {
|
||||
bool result = a >= '0' && a <= '9';
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool CHAR_IsAlphanumeric(char a) {
|
||||
bool result = CHAR_IsDigit(a) || CHAR_IsAlphabetic(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case) {
|
||||
if (a.len != b.len) return false;
|
||||
for (int64_t i = 0; i < a.len; i++) {
|
||||
char A = a.str[i];
|
||||
char B = b.str[i];
|
||||
if (ignore_case & S8_IGNORE_CASE) {
|
||||
A = CHAR_ToLowerCase(A);
|
||||
B = CHAR_ToLowerCase(B);
|
||||
}
|
||||
if (A != B)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case) {
|
||||
S8_String a_end = S8_GetPostfix(a, end.len);
|
||||
bool result = S8_AreEqual(end, a_end, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case) {
|
||||
S8_String a_start = S8_GetPrefix(a, start.len);
|
||||
bool result = S8_AreEqual(start, a_start, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Make(char *str, int64_t len) {
|
||||
S8_String result;
|
||||
result.str = (char *)str;
|
||||
result.len = len;
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string) {
|
||||
char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (string.len + 1));
|
||||
S8_MemoryCopy(copy, string.str, string.len);
|
||||
copy[string.len] = 0;
|
||||
S8_String result = S8_Make(copy, string.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API void S8_NormalizePath(S8_String s) {
|
||||
for (int64_t i = 0; i < s.len; i++) {
|
||||
if (s.str[i] == '\\')
|
||||
s.str[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Chop(S8_String string, int64_t len) {
|
||||
len = S8__ClampTop(len, string.len);
|
||||
S8_String result = S8_Make(string.str, string.len - len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Skip(S8_String string, int64_t len) {
|
||||
len = S8__ClampTop(len, string.len);
|
||||
int64_t remain = string.len - len;
|
||||
S8_String result = S8_Make(string.str + len, remain);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API bool S8_IsPointerInside(S8_String string, char *p) {
|
||||
uintptr_t pointer = (uintptr_t)p;
|
||||
uintptr_t start = (uintptr_t)string.str;
|
||||
uintptr_t stop = start + (uintptr_t)string.len;
|
||||
bool result = pointer >= start && pointer < stop;
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_SkipToP(S8_String string, char *p) {
|
||||
if (S8_IsPointerInside(string, p)) {
|
||||
S8_String result = S8_Make(p, p - string.str);
|
||||
return result;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_SkipPast(S8_String string, S8_String a) {
|
||||
if (S8_IsPointerInside(string, a.str)) {
|
||||
S8_String on_p = S8_Make(a.str, a.str - string.str);
|
||||
S8_String result = S8_Skip(on_p, a.len);
|
||||
return result;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_GetPostfix(S8_String string, int64_t len) {
|
||||
len = S8__ClampTop(len, string.len);
|
||||
int64_t remain_len = string.len - len;
|
||||
S8_String result = S8_Make(string.str + remain_len, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_GetPrefix(S8_String string, int64_t len) {
|
||||
len = S8__ClampTop(len, string.len);
|
||||
S8_String result = S8_Make(string.str, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index) {
|
||||
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
|
||||
if (first_index < 0) first_index = string.len + first_index;
|
||||
S8_ASSERT(first_index < one_past_last_index && "S8_Slice, first_index is bigger then one_past_last_index");
|
||||
S8_ASSERT(string.len > 0 && "Slicing string of length 0! Might be an error!");
|
||||
S8_String result = string;
|
||||
if (string.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = S8__ClampTop(first_index, string.len - 1);
|
||||
one_past_last_index = S8__ClampTop(one_past_last_index, string.len);
|
||||
result.str += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
}
|
||||
else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Trim(S8_String string) {
|
||||
if (string.len == 0)
|
||||
return string;
|
||||
|
||||
int64_t whitespace_begin = 0;
|
||||
for (; whitespace_begin < string.len; whitespace_begin++) {
|
||||
if (!CHAR_IsWhitespace(string.str[whitespace_begin])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != whitespace_begin; whitespace_end--) {
|
||||
if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (whitespace_begin == whitespace_end) {
|
||||
string.len = 0;
|
||||
}
|
||||
else {
|
||||
string = S8_Slice(string, whitespace_begin, whitespace_end);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_TrimEnd(S8_String string) {
|
||||
int64_t whitespace_end = string.len;
|
||||
for (; whitespace_end != 0; whitespace_end--) {
|
||||
if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
S8_String result = S8_GetPrefix(string, whitespace_end);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s) {
|
||||
S8_String copy = S8_Copy(allocator, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = CHAR_ToLowerCase(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s) {
|
||||
S8_String copy = S8_Copy(allocator, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
copy.str[i] = CHAR_ToUpperCase(copy.str[i]);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
S8_API bool S8_Find(S8_String string, S8_String find, unsigned flags, int64_t *index_out) {
|
||||
bool result = false;
|
||||
if (flags & S8_MATCH_FIND_LAST) {
|
||||
for (int64_t i = string.len; i != 0; i--) {
|
||||
int64_t index = i - 1;
|
||||
S8_String substring = S8_Slice(string, index, index + find.len);
|
||||
if (S8_AreEqual(substring, find, flags)) {
|
||||
if (index_out)
|
||||
*index_out = index;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int64_t i = 0; i < string.len; i++) {
|
||||
S8_String substring = S8_Slice(string, i, i + find.len);
|
||||
if (S8_AreEqual(substring, find, flags)) {
|
||||
if (index_out)
|
||||
*index_out = i;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, unsigned flags) {
|
||||
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
int64_t index = 0;
|
||||
while (S8_Find(string, find, flags, &index)) {
|
||||
S8_String before_match = S8_Make(string.str, index);
|
||||
S8_AddNode(allocator, &result, before_match);
|
||||
if (flags & S8_SPLIT_INCLUSIVE) {
|
||||
S8_String match = S8_Make(string.str + index, find.len);
|
||||
S8_AddNode(allocator, &result, match);
|
||||
}
|
||||
string = S8_Skip(string, index + find.len);
|
||||
}
|
||||
if (string.len) S8_AddNode(allocator, &result, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator) {
|
||||
if (list.node_count == 0) return S8_MakeEmpty();
|
||||
if (list.char_count == 0) return S8_MakeEmpty();
|
||||
// S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||
int64_t base_size = (list.char_count + 1);
|
||||
int64_t sep_size = (list.node_count - 1) * separator.len;
|
||||
int64_t size = base_size + sep_size;
|
||||
char *buff = (char *)S8_ALLOCATE(allocator, sizeof(char) * size);
|
||||
S8_String string = S8_Make(buff, 0);
|
||||
for (S8_Node *it = list.first; it; it = it->next) {
|
||||
S8_ASSERT(string.len + it->string.len <= size);
|
||||
S8_MemoryCopy(string.str + string.len, it->string.str, it->string.len);
|
||||
string.len += it->string.len;
|
||||
if (it != list.last) {
|
||||
S8_MemoryCopy(string.str + string.len, separator.str, separator.len);
|
||||
string.len += separator.len;
|
||||
}
|
||||
}
|
||||
S8_ASSERT(string.len == size - 1);
|
||||
string.str[size] = 0;
|
||||
return string;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list) {
|
||||
return S8_MergeWithSeparator(allocator, list, S8_Lit(""));
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, unsigned flags) {
|
||||
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||
S8_List list = S8_Split(allocator, string, replace, flags | S8_SPLIT_INCLUSIVE);
|
||||
for (S8_Node *it = list.first; it; it = it->next) {
|
||||
if (S8_AreEqual(it->string, replace, flags)) {
|
||||
S8_ReplaceNodeString(&list, it, with);
|
||||
}
|
||||
}
|
||||
S8_String result = S8_Merge(allocator, list);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, unsigned flags) { // @untested
|
||||
S8_ASSERT((flags & S8_MATCH_FIND_LAST) == 0);
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
int64_t index = 0;
|
||||
while (S8_Find(string, find, flags, &index)) {
|
||||
S8_String match = S8_Make(string.str + index, find.len);
|
||||
S8_AddNode(allocator, &result, match);
|
||||
string = S8_Skip(string, index + find.len);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ChopLastSlash(S8_String s) {
|
||||
S8_String result = s;
|
||||
S8_Find(s, S8_Lit("/"), S8_MATCH_FIND_LAST, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_ChopLastPeriod(S8_String s) {
|
||||
S8_String result = s;
|
||||
S8_Find(s, S8_Lit("."), S8_MATCH_FIND_LAST, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_SkipToLastSlash(S8_String s) {
|
||||
int64_t pos;
|
||||
S8_String result = s;
|
||||
if (S8_Find(s, S8_Lit("/"), S8_MATCH_FIND_LAST, &pos)) {
|
||||
result = S8_Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_SkipToLastPeriod(S8_String s) {
|
||||
int64_t pos;
|
||||
S8_String result = s;
|
||||
if (S8_Find(s, S8_Lit("."), S8_MATCH_FIND_LAST, &pos)) {
|
||||
result = S8_Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API int64_t S8_Length(char *string) {
|
||||
int64_t len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
S8_API int64_t S8_WideLength(wchar_t *string) {
|
||||
int64_t len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_MakeFromChar(char *string) {
|
||||
S8_String result;
|
||||
result.str = (char *)string;
|
||||
result.len = S8_Length(string);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_MakeEmpty(void) {
|
||||
return S8_Make(0, 0);
|
||||
}
|
||||
|
||||
S8_API S8_List S8_MakeEmptyList(void) {
|
||||
S8_List result;
|
||||
result.first = 0;
|
||||
result.last = 0;
|
||||
result.char_count = 0;
|
||||
result.node_count = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1) {
|
||||
va_list args2;
|
||||
va_copy(args2, args1);
|
||||
int64_t len = S8_VSNPRINTF(0, 0, str, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *result = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1));
|
||||
S8_VSNPRINTF(result, (int)(len + 1), str, args1);
|
||||
S8_String res = S8_Make(result, len);
|
||||
return res;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) {
|
||||
S8_FORMAT(allocator, str, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string) {
|
||||
S8_Node *result = (S8_Node *)S8_ALLOCATE(allocator, sizeof(S8_Node));
|
||||
result->string = string;
|
||||
result->next = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string) {
|
||||
list->char_count -= node->string.len;
|
||||
list->char_count += new_string.len;
|
||||
node->string = new_string;
|
||||
}
|
||||
|
||||
S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node) {
|
||||
if (list->first) {
|
||||
list->last->next = node;
|
||||
list->last = list->last->next;
|
||||
}
|
||||
else {
|
||||
list->first = list->last = node;
|
||||
}
|
||||
list->node_count += 1;
|
||||
list->char_count += node->string.len;
|
||||
}
|
||||
|
||||
S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count) {
|
||||
for (int i = 0; i < count; i += 1) {
|
||||
S8_String s = S8_MakeFromChar(array[i]);
|
||||
S8_AddNode(allocator, list, s);
|
||||
}
|
||||
}
|
||||
|
||||
S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count) {
|
||||
for (int i = 0; i < count; i += 1) {
|
||||
S8_AddF(allocator, list, "%s%s", prefix, array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a) {
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
S8_AddNode(allocator, &result, a);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a) {
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b) {
|
||||
S8_List result = S8_MakeEmptyList();
|
||||
for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||
for (S8_Node *it = b.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
|
||||
return result;
|
||||
}
|
||||
|
||||
S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string) {
|
||||
S8_Node *node = S8_CreateNode(allocator, string);
|
||||
S8_AddExistingNode(list, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) {
|
||||
S8_FORMAT(allocator, str, result);
|
||||
S8_AddNode(allocator, list, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // S8_IMPLEMENTATION
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "../core.c"
|
||||
int main() {
|
||||
MA_Arena arena = {0};
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "../core.c"
|
||||
|
||||
int main() {
|
||||
MA_Arena arena = {};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "../core.h"
|
||||
|
||||
int main() {
|
||||
MA_Arena arena = {};
|
||||
int *a = MA_PushStruct(&arena, int);
|
||||
*a = 10;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
#include "unicode.h"
|
||||
|
||||
#ifndef UTF__MemoryZero
|
||||
#include <string.h>
|
||||
#define UTF__MemoryZero(p, size) memset(p, 0, size)
|
||||
#endif
|
||||
|
||||
UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) {
|
||||
UTF32_Result result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
if (max_advance >= 1) {
|
||||
result.advance = 1;
|
||||
result.out_str = c[0];
|
||||
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = 0x10000;
|
||||
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
|
||||
result.advance = 2;
|
||||
}
|
||||
else
|
||||
result.error = 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint) {
|
||||
UTF8_Result result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
|
||||
if (codepoint <= 0x7F) {
|
||||
result.len = 1;
|
||||
result.out_str[0] = (char)codepoint;
|
||||
}
|
||||
else if (codepoint <= 0x7FF) {
|
||||
result.len = 2;
|
||||
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
|
||||
result.out_str[1] = 0x80 | (0x3f & codepoint);
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) { // 16 bit word
|
||||
result.len = 3;
|
||||
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
}
|
||||
else if (codepoint <= 0x10FFFF) { // 21 bit word
|
||||
result.len = 4;
|
||||
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance) {
|
||||
UTF32_Result result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
|
||||
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||
if (max_advance >= 1) {
|
||||
result.out_str = c[0];
|
||||
result.advance = 1;
|
||||
}
|
||||
else result.error = 1;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xe0) == 0xc0) {
|
||||
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||
result.advance = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf0) == 0xe0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||
if (max_advance >= 3) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||
result.advance = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf8) == 0xf0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||
if (max_advance >= 4) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||
result.advance = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint) {
|
||||
UTF16_Result result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
if (codepoint < 0x10000) {
|
||||
result.out_str[0] = (uint16_t)codepoint;
|
||||
result.out_str[1] = 0;
|
||||
result.len = 1;
|
||||
}
|
||||
else if (codepoint <= 0x10FFFF) {
|
||||
uint32_t code = (codepoint - 0x10000);
|
||||
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
|
||||
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
|
||||
result.len = 2;
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define UTF__HANDLE_DECODE_ERROR(question_mark) \
|
||||
{ \
|
||||
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
|
||||
break; \
|
||||
}
|
||||
|
||||
UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen && in[i];) {
|
||||
UTF32_Result decode = UTF_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
UTF8_Result encode = UTF_ConvertUTF32ToUTF8(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR('?');
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR('?');
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen;) {
|
||||
UTF32_Result decode = UTF_ConvertUTF8ToUTF32(in + i, (int)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
UTF16_Result encode = UTF_ConvertUTF32ToUTF16(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR(0x003f);
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR(0x003f);
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
UTF_API void UTF8_Advance(UTF8_Iter *iter) {
|
||||
iter->i += iter->utf8_codepoint_byte_size;
|
||||
UTF32_Result r = UTF_ConvertUTF8ToUTF32(iter->str + iter->i, iter->len - iter->i);
|
||||
if (r.error) {
|
||||
iter->item = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
iter->utf8_codepoint_byte_size = r.advance;
|
||||
iter->item = r.out_str;
|
||||
}
|
||||
|
||||
UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len) {
|
||||
UTF8_Iter result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
result.str = str;
|
||||
result.len = len;
|
||||
if (len) UTF8_Advance(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF_API UTF8_Iter UTF8_Iterate(char *str) {
|
||||
int length = 0;
|
||||
while (str[length]) length += 1;
|
||||
return UTF8_IterateEx(str, length);
|
||||
}
|
||||
@@ -6,14 +6,6 @@ typedef struct UTF8_Result UTF8_Result;
|
||||
typedef struct UTF16_Result UTF16_Result;
|
||||
typedef struct UTF8_Iter UTF8_Iter;
|
||||
|
||||
#ifndef UTF_StaticFunc
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define UTF_StaticFunc __attribute__((unused)) static
|
||||
#else
|
||||
#define UTF_StaticFunc static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef UTF_API
|
||||
#ifdef __cplusplus
|
||||
#define UTF_API extern "C"
|
||||
@@ -59,222 +51,4 @@ UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len);
|
||||
UTF_API UTF8_Iter UTF8_Iterate(char *str);
|
||||
|
||||
#define UTF8_For(name, str, len) for (UTF8_Iter name = UTF8_IterateEx(str, (int)len); name.item; UTF8_Advance(&name))
|
||||
|
||||
#endif // UTF_HEADER
|
||||
#ifdef UTF_IMPLEMENTATION
|
||||
|
||||
UTF_StaticFunc int UTF__StringLength(char *string) {
|
||||
int len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
UTF_StaticFunc void UTF__MemoryZero(void *p, size_t size) {
|
||||
uint8_t *p8 = (uint8_t *)p;
|
||||
while (size--) *p8++ = 0;
|
||||
}
|
||||
|
||||
UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) {
|
||||
UTF32_Result result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
if (max_advance >= 1) {
|
||||
result.advance = 1;
|
||||
result.out_str = c[0];
|
||||
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = 0x10000;
|
||||
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
|
||||
result.advance = 2;
|
||||
}
|
||||
else
|
||||
result.error = 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint) {
|
||||
UTF8_Result result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
|
||||
if (codepoint <= 0x7F) {
|
||||
result.len = 1;
|
||||
result.out_str[0] = (char)codepoint;
|
||||
}
|
||||
else if (codepoint <= 0x7FF) {
|
||||
result.len = 2;
|
||||
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
|
||||
result.out_str[1] = 0x80 | (0x3f & codepoint);
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) { // 16 bit word
|
||||
result.len = 3;
|
||||
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
}
|
||||
else if (codepoint <= 0x10FFFF) { // 21 bit word
|
||||
result.len = 4;
|
||||
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance) {
|
||||
UTF32_Result result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
|
||||
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||
if (max_advance >= 1) {
|
||||
result.out_str = c[0];
|
||||
result.advance = 1;
|
||||
}
|
||||
else result.error = 1;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xe0) == 0xc0) {
|
||||
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||
result.advance = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
else result.error = 2;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf0) == 0xe0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||
if (max_advance >= 3) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||
result.advance = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
else result.error = 3;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf8) == 0xf0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||
if (max_advance >= 4) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||
result.advance = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint) {
|
||||
UTF16_Result result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
if (codepoint < 0x10000) {
|
||||
result.out_str[0] = (uint16_t)codepoint;
|
||||
result.out_str[1] = 0;
|
||||
result.len = 1;
|
||||
}
|
||||
else if (codepoint <= 0x10FFFF) {
|
||||
uint32_t code = (codepoint - 0x10000);
|
||||
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
|
||||
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
|
||||
result.len = 2;
|
||||
}
|
||||
else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define UTF__HANDLE_DECODE_ERROR(question_mark) \
|
||||
{ \
|
||||
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
|
||||
break; \
|
||||
}
|
||||
|
||||
UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen && in[i];) {
|
||||
UTF32_Result decode = UTF_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
UTF8_Result encode = UTF_ConvertUTF32ToUTF8(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR('?');
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR('?');
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen;) {
|
||||
UTF32_Result decode = UTF_ConvertUTF8ToUTF32(in + i, (int)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
UTF16_Result encode = UTF_ConvertUTF32ToUTF16(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR(0x003f);
|
||||
}
|
||||
else UTF__HANDLE_DECODE_ERROR(0x003f);
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
UTF_API void UTF8_Advance(UTF8_Iter *iter) {
|
||||
iter->i += iter->utf8_codepoint_byte_size;
|
||||
UTF32_Result r = UTF_ConvertUTF8ToUTF32(iter->str + iter->i, iter->len - iter->i);
|
||||
if (r.error) {
|
||||
iter->item = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
iter->utf8_codepoint_byte_size = r.advance;
|
||||
iter->item = r.out_str;
|
||||
}
|
||||
|
||||
UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len) {
|
||||
UTF8_Iter result;
|
||||
UTF__MemoryZero(&result, sizeof(result));
|
||||
result.str = str;
|
||||
result.len = len;
|
||||
if (len) UTF8_Advance(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF_API UTF8_Iter UTF8_Iterate(char *str) {
|
||||
return UTF8_IterateEx(str, UTF__StringLength(str));
|
||||
}
|
||||
|
||||
#endif // UTF_IMPLEMENTATION
|
||||
Reference in New Issue
Block a user