/// <summary> /// Replace all instances of a character in a string with a replacement character. /// </summary> /// <param name="str">The string to search through (if NULL, an empty string is returned).</param> /// <param name="pattern">The pattern to search for.</param> /// <param name="replacement">A replacement character for each instance of the pattern.</param> /// <returns>A new string where all instances of the pattern character have been replaced by the given replacement character.</returns> String String_ReplaceChar(const String str, Byte pattern, Byte replacement) { const struct StringInt *s; struct StringInt *newString; Byte *text, *newText, *dest; const Byte *src; Int length; Byte ch; Int i; if (String_IsNullOrEmpty(str)) return String_Empty; s = (const struct StringInt *)str; text = s->text; length = s->length; newString = GC_MALLOC_STRUCT(struct StringInt); if (newString == NULL) Smile_Abort_OutOfMemory(); newText = GC_MALLOC_TEXT(length); if (newText == NULL) Smile_Abort_OutOfMemory(); newString->length = length; newString->text = newText; src = text; dest = newText; for (i = 0; i < length; i++) { *dest++ = ((ch = *src++) == pattern ? replacement : ch); } *dest = '\0'; return (String)newString; }
/// <summary> /// Construct a new String instance by concatenating a byte onto the end of a string. /// </summary> /// <param name="str">The string to concatenate.</param> /// <param name="ch">The byte to append to it.</param> /// <returns>The new String instance.</returns> String String_ConcatByte(const String str, Byte ch) { struct StringInt *result, *s1; Byte *newText; Int length; s1 = (struct StringInt *)str; if (s1 == NULL || s1->length == 0) return String_CreateRepeat(ch, 1); length = s1->length + 1; result = GC_MALLOC_STRUCT(struct StringInt); if (result == NULL) Smile_Abort_OutOfMemory(); newText = GC_MALLOC_TEXT(length); if (newText == NULL) Smile_Abort_OutOfMemory(); result->length = length; result->text = newText; MemCpy(newText, s1->text, s1->length); newText[length-1] = ch; newText[length] = '\0'; return (String)result; }
/// <summary> /// Construct a new String instance by concatenating exactly two strings together. /// </summary> /// <param name="str">The first string to concatenate.</param> /// <param name="other">The second string to concatenate.</param> /// <returns>The new String instance.</returns> String String_Concat(const String str, const String other) { struct StringInt *result, *s1, *s2; Byte *newText; Int length; s1 = (struct StringInt *)str; s2 = (struct StringInt *)other; if (s1 == NULL || s1->length == 0) return s2 != NULL ? (String)s2 : String_Empty; if (s2 == NULL || s2->length == 0) return (String)s1; length = s1->length + s2->length; result = GC_MALLOC_STRUCT(struct StringInt); if (result == NULL) Smile_Abort_OutOfMemory(); newText = GC_MALLOC_TEXT(length); if (newText == NULL) Smile_Abort_OutOfMemory(); result->length = length; result->text = newText; MemCpy(newText, s1->text, s1->length); MemCpy(newText + s1->length, s2->text, s2->length); newText[length] = '\0'; return (String)result; }
/// <summary> /// Run all the tests in the given test suite, and return a new collection of results for it. /// </summary> /// <param name="name">The name of the test suite.</param> /// <param name="funcs">An array of the functions in the test suite to be run.</param> /// <param name="numFuncs">The number of functions to run.</param> /// <returns>A new collection of results that describes the overall state of this test suite.</returns> TestSuiteResults *RunTestSuiteInternal(const char *name, TestFunc *funcs, int numFuncs) { int numSuccesses, numFailures, succeeded, i; TestSuiteResults *results; if (!IsTestSuiteRequested(name)) { results = GC_MALLOC_STRUCT(TestSuiteResults); if (results == NULL) Smile_Abort_OutOfMemory(); results->numFailures = 0; results->numSuccesses = 0; return results; } if (!QuietMode) { printf("\x1B[0;1;37m Test suite %s: \x1B[0m\n", name); fflush(stdout); } numSuccesses = 0, numFailures = 0; for (i = 0; i < numFuncs; i++, funcs++) { succeeded = (*funcs)(); if (succeeded) numSuccesses++; else numFailures++; } if (!QuietMode) printf("\n"); results = GC_MALLOC_STRUCT(TestSuiteResults); if (results == NULL) Smile_Abort_OutOfMemory(); results->numFailures = numFailures; results->numSuccesses = numSuccesses; return results; }
/// <summary> /// Construct a new String instance by concatenating many other strings together, with a glue string between them. /// </summary> /// <param name="glue">The glue string to insert between successive string instances. This will not be /// added to the start and end of the resulting string: Only between strings.</param> /// <param name="strs">The array of string instances to join to create the new string.</param> /// <param name="numStrs">The number of string instances in the array.</param> /// <returns>The new String instance.</returns> String String_Join(const String glue, const String *strs, Int numStrs) { Int length, dest, i; struct StringInt *str; Byte *newText; const Byte *glueText; Int glueLength; if (numStrs <= 0) return String_Empty; length = 0; for (i = 0; i < numStrs; i++) { length += ((const struct StringInt *)(strs[i]))->length; } length += (numStrs - 1) * ((const struct StringInt *)glue)->length; if (length <= 0) return String_Empty; str = GC_MALLOC_STRUCT(struct StringInt); if (str == NULL) Smile_Abort_OutOfMemory(); newText = GC_MALLOC_TEXT(length); if (newText == NULL) Smile_Abort_OutOfMemory(); str->length = length; str->text = newText; glueText = ((const struct StringInt *)glue)->text; glueLength = ((const struct StringInt *)glue)->length; MemCpy(newText, ((const struct StringInt *)(strs[0]))->text, ((const struct StringInt *)(strs[0]))->length); dest = ((const struct StringInt *)(strs[0]))->length; for (i = 1; i < numStrs; i++) { MemCpy(newText + dest, glueText, glueLength); dest += glueLength; MemCpy(newText + dest, ((const struct StringInt *)(strs[i]))->text, ((const struct StringInt *)(strs[i]))->length); dest += ((const struct StringInt *)(strs[i]))->length; } newText[length] = '\0'; return (String)str; }
/// <summary> /// Construct a new String instance of the given length, but whose bytes are as yet uninitialized (garbage). /// </summary> /// <param name="length">The length of bytes for the new string (not including the terminating nul character).</param> /// <returns>The new String instance.</returns> String String_CreateInternal(Int length) { struct StringInt *str; Byte *newText; if (length <= 0) return String_Empty; str = GC_MALLOC_STRUCT(struct StringInt); if (str == NULL) Smile_Abort_OutOfMemory(); newText = GC_MALLOC_TEXT(length); if (newText == NULL) Smile_Abort_OutOfMemory(); str->length = length; str->text = newText; newText[length] = '\0'; return (String)str; }
/// <summary> /// Construct a new String instance containing the given character repeated for the given count. /// </summary> /// <param name="b">The source character to use to construct the new string.</param> /// <param name="repeatCount">The number of times to repeat that character when constructing the new string.</param> /// <returns>The new String instance.</returns> String String_CreateRepeat(Byte b, Int repeatCount) { struct StringInt *str; Byte *newText; if (repeatCount <= 0) return String_Empty; str = GC_MALLOC_STRUCT(struct StringInt); if (str == NULL) Smile_Abort_OutOfMemory(); newText = GC_MALLOC_TEXT(repeatCount); if (newText == NULL) Smile_Abort_OutOfMemory(); str->length = repeatCount; str->text = newText; MemSet(newText, b, repeatCount); newText[repeatCount] = '\0'; return (String)str; }
/// <summary> /// Construct a new String instance by concatenating many other strings together. /// </summary> /// <param name="strs">The array of string instances to join to create the new string.</param> /// <param name="numStrs">The number of string instances in the array.</param> /// <returns>The new String instance.</returns> String String_ConcatMany(const String *strs, Int numStrs) { Int length, dest, i; struct StringInt *str; const struct StringInt *temp; Byte *newText; if (numStrs <= 0) return String_Empty; length = 0; for (i = 0; i < numStrs; i++) { length += ((const struct StringInt *)(strs[i]))->length; } if (length <= 0) return String_Empty; str = GC_MALLOC_STRUCT(struct StringInt); if (str == NULL) Smile_Abort_OutOfMemory(); newText = GC_MALLOC_TEXT(length); if (newText == NULL) Smile_Abort_OutOfMemory(); str->length = length; str->text = newText; dest = 0; for (i = 0; i < numStrs; i++) { temp = ((const struct StringInt *)(strs[i])); MemCpy(newText + dest, temp->text, temp->length); dest += temp->length; } newText[length] = '\0'; return (String)str; }
/// <summary> /// Cause the current test to fail, aborting all successive execution. /// </summary> /// <param name="message">The message to display to the user explaining why this test failed.</param> /// <param name="file">The file in which the failure occurred.</param> /// <param name="line">The line at which the failure occurred.</param> /// <returns>Never returns.</returns> int FailTestWithLineInternal(const char *message, const char *file, int line) { char *safeMessage; // Save the message so the test runner can find it (and save it in case it gets stomped on). safeMessage = GC_MALLOC_ATOMIC(strlen(message) + 1); if (safeMessage == NULL) Smile_Abort_OutOfMemory(); strcpy(safeMessage, message); TestFailureMessage = safeMessage; TestFailureFile = file; TestFailureLine = line; // Long-jump back to abort the test. We pass '1', which is what will be returned by the // original setjmp() call. longjmp(TestJmpBuf, 1); return 0; // Never reached. }