#include "tstring.h" #include #include #include #include void TStringCopyGlyph(const char* glyph, int length, TString* string); int ResizeTStringCapacity(int newTextLength, TString* string); int GetGlyphByteWidth(char glyph); 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 additionalLength, TString* string) { if (!string) return 0; if (string->ByteCount + additionalLength < string->Capacity) return 1; unsigned int newCapacity = string->Capacity; while (additionalLength > newCapacity) newCapacity = string->Capacity * 2; 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; if (string->ByteCount == 0) return; for (int i = string->ByteCount; i >= 0; i--) { unsigned int width = GetGlyphByteWidth(string->Characters[i]); if (width) { //If we've been provided a buffer, then populate it with the glyph we're removing. if (buffer) { memset(buffer, '\0', 8); 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) { //ANDing 0x80 (1000 0000) should result in 0 if this code point is 1 byte long. if ((glyph & 0x80) == 0) return 1; //ANDing 0xE0 (1110 0000) will result in 0xC0 (1100 0000) if this code point is 2 bytes long. if ((glyph & 0xE0) == 0xC0) return 2; //ANDing 0xF0 (1111 0000) will result in 0xE0 (1110 0000) if this code point is 3 bytes long. if ((glyph & 0xF0) == 0xE0) return 3; //ANDing 0xE0 (1111 1000) will result in 0xF0 (1111 0000) if this code point is 4 bytes long. if ((glyph & 0xF8) == 0xF0) return 4; //Else we either have a continuation point (pattern: 10XX XXXX) or its just a junk value //as far as we're concerned. 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, int statsOnly) { if (!string) return; printf("Stats: Length %d; Size %d byte(s); Capacity: %d bytes\n", string->Length, string->ByteCount, string->Capacity); if (statsOnly) return; printf("Stored String:\n"); if (string->Characters) { char* text = TStringGetString(string); printf("%s\n", text); free(text); } printf("\nBytes:\n"); for(int i = 0; i < string->Capacity; i++) { if (i != 0 && i % 80 == 0) printf("\n"); printf("%02X ", string->Characters[i] & 0xFF); } printf("\n"); }