236 lines
6.3 KiB
C
236 lines
6.3 KiB
C
#include "tstring.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
int GetGlyphByteWidth(char glyph);
|
|
void TStringCopyGlyph(const char* glyph, int length, TString* string);
|
|
int ResizeTStringCapacity(int newTextLength, TString* string);
|
|
|
|
struct __tstring {
|
|
char* Characters;
|
|
int Length;
|
|
int ByteCount;
|
|
int Capacity;
|
|
};
|
|
|
|
TString* TStringCreate(void) {
|
|
TString* string = malloc(sizeof(TString));
|
|
|
|
if (!string) {
|
|
fprintf(stderr, "Failed to malloc space for TString. %s.\n", strerror(errno));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
string->Characters = calloc(DEFAULT_TSTRING_BYTE_BUFFER, sizeof(char));
|
|
|
|
if (!string->Characters) {
|
|
fprintf(stderr, "Failed to malloc space for TString character array. %s.\n", strerror(errno));
|
|
|
|
free(string);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
string->Capacity = DEFAULT_TSTRING_BYTE_BUFFER;
|
|
string->ByteCount = 0;
|
|
string->Length = 0;
|
|
|
|
return string;
|
|
}
|
|
|
|
int TStringLength(const TString* string) {
|
|
if (!string) return -1;
|
|
|
|
return string->Length;
|
|
}
|
|
|
|
int ResizeTStringCapacity(int newTextLength, TString* string) {
|
|
if (!string) return 0;
|
|
if (string->ByteCount + newTextLength < string->Capacity) return 1;
|
|
|
|
//TODO: This is a bad assumption considering the additional length could overflow without a check.
|
|
char* buffer = realloc(string->Characters, string->Capacity * 2);
|
|
|
|
if (!buffer) {
|
|
fprintf(stderr, "Failed to realloc space for text insertion. %s.\n", strerror(errno));
|
|
|
|
return 0;
|
|
}
|
|
|
|
string->Characters = buffer;
|
|
string->Capacity *= 2;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void TStringPop(char buffer[8], TString* string) {
|
|
if (!string) return;
|
|
|
|
memset(buffer, '\0', 8);
|
|
|
|
unsigned int offset = 0;
|
|
|
|
for (int i = string->ByteCount; i >= 0; i--) {
|
|
unsigned int width = GetGlyphByteWidth(string->Characters[i]);
|
|
char c = string->Characters[i];
|
|
if (c > 0x7F) continue;
|
|
|
|
if (width) {
|
|
if (buffer) memcpy(buffer, &string->Characters[i], width);
|
|
memset(&string->Characters[i], '\0', width);
|
|
string->Length--;
|
|
string->ByteCount -= width;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TStringInsertText(const char* text, int index, TString* string) {
|
|
if (!text || !string) return;
|
|
if (index < 0 || index > string->Length) index = string->Length;
|
|
|
|
unsigned int insertionTextLength = strlen(text);
|
|
unsigned int startByte = 0;
|
|
unsigned int endByte = string->ByteCount + insertionTextLength;
|
|
|
|
if (!ResizeTStringCapacity(insertionTextLength, string)) return;
|
|
|
|
char buffer[256] = { 0 };
|
|
|
|
for (int glyphCount = 0; glyphCount != index; glyphCount++) startByte += GetGlyphByteWidth(string->Characters[startByte]);
|
|
for (unsigned int offset = 0; offset < insertionTextLength; string->Length++) offset += GetGlyphByteWidth(text[offset]);
|
|
|
|
unsigned int fullBuffers = (string->ByteCount - startByte) / sizeof buffer;
|
|
unsigned int remainder = (string->ByteCount - startByte) - fullBuffers * sizeof buffer;
|
|
|
|
for (int i = fullBuffers; i > 0; i--) {
|
|
memcpy(buffer, &string->Characters[string->ByteCount - i * sizeof buffer], sizeof buffer);
|
|
memcpy(&string->Characters[endByte - sizeof buffer * i], buffer, sizeof buffer);
|
|
}
|
|
|
|
if (remainder > 0) {
|
|
memcpy(buffer, &string->Characters[startByte], remainder);
|
|
memcpy(&string->Characters[insertionTextLength + startByte], buffer, remainder);
|
|
}
|
|
|
|
memcpy(&string->Characters[startByte], text, insertionTextLength);
|
|
|
|
string->ByteCount += insertionTextLength;
|
|
}
|
|
|
|
void TStringAppendText(const char* text, TString* string) {
|
|
if (!text || !string) return;
|
|
|
|
int totalLength = strlen(text);
|
|
int glyphLength = GetGlyphByteWidth(text[0]);
|
|
|
|
//If the two lengths are the same then that means we have a single character.
|
|
//Simply append it to the list and return.
|
|
if (totalLength == glyphLength) {
|
|
TStringCopyGlyph(text, glyphLength, string);
|
|
|
|
return;
|
|
}
|
|
|
|
//If we've made it here, than that means we have a full on string of characters.
|
|
for(int i = 0; i < totalLength;) {
|
|
TStringCopyGlyph(&text[i], glyphLength, string);
|
|
|
|
i += glyphLength;
|
|
glyphLength = GetGlyphByteWidth(text[i]);
|
|
}
|
|
}
|
|
|
|
char* TStringGetString(const TString* string) {
|
|
if (!string) return NULL;
|
|
|
|
char* text = calloc(string->ByteCount + 1, sizeof(char));
|
|
|
|
if (!text) {
|
|
fprintf(stderr, "Failed to calloc memory for TString text. %s.\n", strerror(errno));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(text, string->Characters, string->ByteCount);
|
|
|
|
return text;
|
|
}
|
|
|
|
void TStringGetGlyph(int index, char buffer[8], const TString* string) {
|
|
if (!buffer || !string) return;
|
|
|
|
memset(buffer, '\0', sizeof *buffer);
|
|
|
|
int glyphCount = 0;
|
|
|
|
for(int i = 0; i < string->ByteCount;) {
|
|
int glyphWidth = GetGlyphByteWidth(string->Characters[i]);
|
|
|
|
if (glyphCount == index) {
|
|
memcpy(buffer, &string->Characters[i], glyphWidth);
|
|
return;
|
|
}
|
|
|
|
glyphCount++;
|
|
i += glyphWidth;
|
|
}
|
|
}
|
|
|
|
void TStringFree(TString* string) {
|
|
if (!string) return;
|
|
|
|
if (string->Characters) free(string->Characters);
|
|
|
|
free(string);
|
|
}
|
|
|
|
int GetGlyphByteWidth(char glyph) {
|
|
if (!glyph) return 0;
|
|
|
|
if ((glyph & 0x80) == 0) {
|
|
return 1;
|
|
}
|
|
else if ((glyph & 0xE0) == 0xC0) {
|
|
return 2;
|
|
}
|
|
else if ((glyph & 0xF0) == 0xE0) {
|
|
return 3;
|
|
}
|
|
else if ((glyph & 0xF8) == 0xF0) {
|
|
return 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void TStringCopyGlyph(const char* glyph, int length, TString* string) {
|
|
if (!glyph || !string) return;
|
|
if (length < 1) return;
|
|
|
|
if (!ResizeTStringCapacity(length, string)) return;
|
|
|
|
memcpy(&string->Characters[string->ByteCount], glyph, length);
|
|
|
|
string->ByteCount += length;
|
|
string->Length++;
|
|
}
|
|
|
|
void TStringPrintDetails(const TString* string) {
|
|
if (!string) return;
|
|
|
|
printf("Length: %d Size: %d byte(s) Capacity: %d bytes\n", string->Length, string->ByteCount, string->Capacity);
|
|
|
|
if (string->Characters) {
|
|
char* text = TStringGetString(string);
|
|
printf("%s\n", text);
|
|
free(text);
|
|
}
|
|
|
|
printf("Bytes:");
|
|
for(int i = 0; i < string->Capacity; i++) printf(" %02X", string->Characters[i] & 0xFF);
|
|
printf("\n");
|
|
} |