162 lines
5.2 KiB
C
162 lines
5.2 KiB
C
#include <c_StringBuffer.h>
|
|||
|
|
#include <c_Memory.h>
|
||
|
|
|
||
|
|
#define DEFAULT_INIT_CAPACITY 16
|
||
|
|
|
||
|
|
C_STATIC_FORCE_INLINE
|
||
|
|
c_err_t c_StringBuffer_EnsureCapacity(c_StringBuffer_t* self, c_size_t required_len) {
|
||
|
|
c_size_t needed_capacity = self->size + required_len + 1; // +1 for trailing '\0'
|
||
|
|
if (needed_capacity <= self->capacity) {
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
c_size_t new_capacity = self->capacity == 0 ? DEFAULT_INIT_CAPACITY : self->capacity;
|
||
|
|
while (new_capacity < needed_capacity) {
|
||
|
|
new_capacity *= 2; // Exponential doubling strategy
|
||
|
|
}
|
||
|
|
|
||
|
|
char* new_buffer = (char*)C_ALLOC(new_capacity);
|
||
|
|
if (!new_buffer) {
|
||
|
|
return C_ERR_NOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (self->buffer && self->size > 0) {
|
||
|
|
memcpy(new_buffer, self->buffer, self->size);
|
||
|
|
}
|
||
|
|
new_buffer[self->size] = '\0';
|
||
|
|
|
||
|
|
C_FREE(self->buffer);
|
||
|
|
self->buffer = new_buffer;
|
||
|
|
self->capacity = new_capacity;
|
||
|
|
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
||
|
|
/* */
|
||
|
|
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_Init(c_StringBuffer_t* self, c_size_t capacity) {
|
||
|
|
if (!self) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
self->size = 0;
|
||
|
|
self->capacity = capacity > 0 ? capacity : DEFAULT_INIT_CAPACITY; // Enforce minimum initial allocation
|
||
|
|
self->buffer = (char*)C_ALLOC(self->capacity);
|
||
|
|
if (!self->buffer) {
|
||
|
|
self->capacity = 0;
|
||
|
|
return C_ERR_NOMEM;
|
||
|
|
}
|
||
|
|
self->buffer[0] = '\0';
|
||
|
|
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
void c_StringBuffer_Destroy(c_StringBuffer_t* self) {
|
||
|
|
if (!self) return;
|
||
|
|
if (self->buffer) {
|
||
|
|
C_FREE(self->buffer);
|
||
|
|
}
|
||
|
|
self->size = 0;
|
||
|
|
self->capacity = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_Append(c_StringBuffer_t* self, const char* string, c_size_t length) {
|
||
|
|
if (!self || !self->buffer || !string || length == 0) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
c_err_t err = c_StringBuffer_EnsureCapacity(self, length);
|
||
|
|
if (err != C_ERR_OK) return err;
|
||
|
|
|
||
|
|
memcpy(self->buffer + self->size, string, length);
|
||
|
|
self->size += length;
|
||
|
|
self->buffer[self->size] = '\0';
|
||
|
|
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_Prepend(c_StringBuffer_t* self, const char* string, c_size_t length) {
|
||
|
|
return c_StringBuffer_InsertAt(self, 0, string, length);
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_InsertAt(c_StringBuffer_t* self, c_size_t index, const char* string, c_size_t length) {
|
||
|
|
if (!self || !self->buffer || !string || length == 0 || index > self->size) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
c_err_t err = c_StringBuffer_EnsureCapacity(self, length);
|
||
|
|
if (err != C_ERR_OK) return err;
|
||
|
|
|
||
|
|
// Shift memory to the right using memmove to prevent overlapping issues
|
||
|
|
memmove(self->buffer + index + length, self->buffer + index, self->size - index);
|
||
|
|
memcpy(self->buffer + index, string, length);
|
||
|
|
|
||
|
|
self->size += length;
|
||
|
|
self->buffer[self->size] = '\0';
|
||
|
|
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_RemoveAt(c_StringBuffer_t* self, c_size_t index, c_size_t length) {
|
||
|
|
if (!self || !self->buffer || length == 0 || index >= self->size) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
// Clamp length if it attempts to read past the end of the current buffer
|
||
|
|
if (index + length > self->size) {
|
||
|
|
length = self->size - index;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Shift trailing memory to the left to close the character gap
|
||
|
|
memmove(self->buffer + index, self->buffer + index + length, self->size - (index + length));
|
||
|
|
self->size -= length;
|
||
|
|
self->buffer[self->size] = '\0';
|
||
|
|
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
void c_StringBuffer_Clear(c_StringBuffer_t* self) {
|
||
|
|
if (!self || !self->buffer) return;
|
||
|
|
self->size = 0;
|
||
|
|
self->buffer[0] = '\0';
|
||
|
|
}
|
||
|
|
|
||
|
|
/* --- Explicit String-Wrapper Interfaces --- */
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_AppendStr(c_StringBuffer_t* self, const char* string) {
|
||
|
|
if (!string) return C_ERR_PARAM;
|
||
|
|
return c_StringBuffer_Append(self, string, strlen(string));
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_PrependStr(c_StringBuffer_t* self, const char* string) {
|
||
|
|
if (!string) return C_ERR_PARAM;
|
||
|
|
return c_StringBuffer_Prepend(self, string, strlen(string));
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_InsertStrAt(c_StringBuffer_t* self, const char* string, c_size_t index) {
|
||
|
|
if (!string) return C_ERR_PARAM;
|
||
|
|
return c_StringBuffer_InsertAt(self, index, string, strlen(string));
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_StringBuffer_CopyTo(c_StringBuffer_t* self, c_size_t index, c_size_t length, char* buffer, c_size_t buffer_length) {
|
||
|
|
// 1. Guard against invalid pointers, empty destinations, or index out-of-bounds
|
||
|
|
if (!self || !self->buffer || !buffer || buffer_length == 0 || index > self->size) {
|
||
|
|
return C_ERR_PARAM;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Clamp requested copy length if it exceeds the remaining data payload bounds
|
||
|
|
if (index + length > self->size) {
|
||
|
|
length = self->size - index;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. Enforce destination buffer capacity threshold checks
|
||
|
|
// The requested segment requires at least (length + 1) bytes for safe null-termination
|
||
|
|
if (length >= buffer_length) {
|
||
|
|
return C_ERR_PARAM; // Destination buffer is too small to store the segment safely
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. Perform the raw memory copy if there are valid characters to process
|
||
|
|
if (length > 0) {
|
||
|
|
memcpy(buffer, self->buffer + index, length);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 5. Always apply a deterministic trailing null terminator
|
||
|
|
buffer[length] = '\0';
|
||
|
|
|
||
|
|
return C_ERR_OK;
|
||
|
|
}
|