diff --git a/Strings/public/StringLogic.hpp b/Strings/public/StringLogic.hpp index 0abc604..32346e6 100644 --- a/Strings/public/StringLogic.hpp +++ b/Strings/public/StringLogic.hpp @@ -1,8 +1,10 @@ #pragma once #include "Allocators.hpp" -#include "Utils.hpp" #include "Buffer.hpp" +#include "Utils.hpp" +#include +#include namespace tp { @@ -18,51 +20,52 @@ namespace tp { static inline bool isEndChar(tChar in) { return in == '\0'; } static constexpr tChar getEndChar() { return '\0'; } - static ualni calcLength(const tChar* in) { + static Index calcLength(const tChar* in) { const tChar* iter = in; while (*iter != '\0') iter++; return iter - in; } - static tChar* insertLogic(const tChar* cur, const tChar* tar, ualni at, ualni len) { - ualni curLen = calcLength(cur); - ualni allLen = curLen + len; + static tChar* insertLogic(const tChar* cur, const tChar* tar, Index at, Index len) { + auto curLen = calcLength(cur); + auto allLen = curLen + len; auto* out = new tChar[allLen + 1]; DEBUG_ASSERT(curLen >= 0) DEBUG_ASSERT(len >= 0) DEBUG_ASSERT(at < curLen + 1 && at >= 0) - for (ualni idx = 0; idx < at; idx++) { + for (Index idx = 0; idx < at; idx++) { out[idx] = cur[idx]; } - for (ualni idx = 0; idx < len; idx++) { + for (Index idx = 0; idx < len; idx++) { out[idx + at] = tar[idx]; } - for (ualni idx = at + len; idx < allLen; idx++) { + for (Index idx = at + len; idx < allLen; idx++) { out[idx] = cur[idx - len]; } out[allLen] = '\0'; return out; } - static tChar* removeLogic(const tChar* cur, ualni start, ualni end) { - ualni curLen = calcLength(cur); - ualni rangeLen = end - start; - ualni newLen = curLen - rangeLen; + // including end not including start + static tChar* removeLogic(const tChar* cur, Index start, Index end) { + auto curLen = calcLength(cur); + auto rangeLen = end - start; + auto newLen = curLen - rangeLen; auto* out = new tChar[newLen + 1]; out[newLen] = '\0'; - for (ualni idx = 0; idx < start; idx++) { + for (Index idx = 0; idx < start; idx++) { out[idx] = cur[idx]; } - for (ualni idx = end; idx < curLen; idx++) { + for (Index idx = end; idx < curLen; idx++) { out[idx - rangeLen] = cur[idx]; } return out; } static bool isEqualLogic(const tChar* left, const tChar* right) { - ualni idx = 0; + Index idx = 0; LOOP: if (left[idx] == '\0' || right[idx] == '\0') { if (left[idx] == '\0' && right[idx] == '\0') { @@ -77,88 +80,6 @@ namespace tp { goto LOOP; } - static ualni fromValueLength(alni val, ualni base) { - ualni iter = val; - ualni len = 0; - while (iter /= base) { - len++; - } - bool neg = val < 0; - len += 1 + ualni(neg); - return len; - } - - static tChar* stringFromValue(alni val, tChar* ownBuff, ualni base) { - tChar* out; - bool neg = val < 0; - ualni len = fromValueLength(val, base); - out = ownBuff ? ownBuff : new tChar[len + 1]; - out[len] = '\0'; - val = abs(val); - for (ualni i = len - 1; i >= int(neg); i--) { - out[i] = (tChar) (val % base + 48); - val /= (alni) base; - } - if (neg) { - out[0] = '-'; - } - return out; - } - - static ualni fromValueLength(alnf val, ualni base) { - auto rounded = (alni) val; - ualni mantissa = ( (alni) val - rounded) * 100000; - alni rounded_len = 0; - alni mantissa_len = 0; - while (rounded /= (alni) base) { - rounded_len++; - } - if (!rounded_len) { - rounded_len++; - } - while (mantissa /= base) { - mantissa_len++; - } - bool neg = val < 0; - bool dot = mantissa_len & 1; - alni tot_len = mantissa_len + rounded_len + dot + neg; - return tot_len; - } - - static tChar* fromValue(alnf, ualni, tChar*) { - DEBUG_BREAK(false) - return nullptr; - } - - static tChar* fromValue(bool val, tChar* ownBuff) { - alni len = val ? 4 : 5; - tChar* out = ownBuff ? ownBuff : new tChar[len + 1]; - out[len] = '\0'; - memCopy(out, val ? "True" : "False", len); - return out; - } - - static tChar* stringFromValue(int val, ualni base) { - return fromValue((alni) val, nullptr, base); - } - - static tChar* fromValue(tChar val) { - auto* out = new tChar[2]; - out[1] = '\0'; - out[0] = val; - return out; - } - - static bool toValue(const tChar*, alni&, ualni) { - ASSERT(false) - return false; - } - - static bool toValue(const tChar*, alnf&, ualni) { - ASSERT(false) - return false; - } - static bool toValue(const tChar* in, bool& val) { if (!calcLength(in)) return false; if (isEqual(in, "False") || isEqual(in, "false") || isEqual(in, "0")) { @@ -169,8 +90,9 @@ namespace tp { return true; } + // returns 0 : nl1 + 1 : nl2 + 1 : nl3 + 1 : ... : size static void calcLineOffsets(const tChar* aBuff, Index aLength, Buffer& aOut) { - halni lines = 0; + Index lines = 0; for (auto idx : Range(aLength)) { if (isNewLineChar(aBuff[idx])) { lines++; @@ -187,5 +109,42 @@ namespace tp { aOut[0] = 0; aOut[lines + 1] = aLength; } + + + [[nodiscard]] static bool convertStringToValue(const tChar* input, alni& output) { + char* endPtr; + output = std::strtoll(input, &endPtr, 10); + return (endPtr == input + calcLength(input)); + } + + [[nodiscard]] static bool convertStringToValue(const tChar* input, alnf& output) { + char* endPtr; + output = std::strtod(input, &endPtr); + return (endPtr == input + calcLength(input)); + } + + [[nodiscard]] static bool convertStringToValue(const tChar* input, bool& output) { + output = !(isEqualLogic(input, "False") || isEqualLogic(input, "false") || isEqualLogic(input, "0")); + return true; + } + + [[nodiscard]] static bool convertValueToString(alni input, tChar* output, Index bufferSize) { + int result = std::snprintf(output, bufferSize, "%lld", input); + return result >= 0; + } + + [[nodiscard]] static bool convertValueToString(alnf input, tChar* output, Index bufferSize) { + int result = std::snprintf(output, bufferSize, "%f", input); + return result >= 0; + } + + [[nodiscard]] static bool convertValueToString(bool input, tChar* output, Index bufferSize) { + const char* str = (input ? "true" : "false"); + Index length = calcLength(str); + if (length >= bufferSize) + return false; + memCopy(output, str, length + 1); + return true; + } }; } \ No newline at end of file diff --git a/Strings/public/Strings.hpp b/Strings/public/Strings.hpp index b1038bb..254943c 100644 --- a/Strings/public/Strings.hpp +++ b/Strings/public/Strings.hpp @@ -24,7 +24,7 @@ namespace tp { StringData() { mBuff = (tChar*) "*"; - mReferenceCount = 0; + mReferenceCount = 1; mIsConst = true; mEditedIdx = 0; } @@ -73,7 +73,7 @@ namespace tp { typedef typename Logic::Index Index; enum { STRINGS_POOL_SIZE = 1024, }; - static PoolAlloc, STRINGS_POOL_SIZE>* sStringPool; + static PoolAlloc, STRINGS_POOL_SIZE> sStringPool; static Data sNullString; private: @@ -94,14 +94,14 @@ namespace tp { // Creates new string data from raw data pointer. // Does not own string buffer - string buffer will not be freed. Initializes as const memory. - explicit StringTemplate(const tChar* raw) { - mData = new (sStringPool->allocate(0)) StringData(raw, true); + StringTemplate(const tChar* raw) { + mData = new (sStringPool.allocate(0)) StringData(raw, true); incReference(mData); } // Copies raw data and claims ownership over it - explicit StringTemplate(tChar* raw) { - mData = new (sStringPool->allocate(0)) StringData(raw); + StringTemplate(tChar* raw) { + mData = new (sStringPool.allocate(0)) StringData(raw); incReference(mData); } @@ -117,8 +117,8 @@ namespace tp { void decReference(Data* dp) { dp->mReferenceCount--; if (!dp->mReferenceCount) { - sStringPool->deallocate(mData); mData->~StringData(); + sStringPool.deallocate(mData); } } @@ -146,7 +146,7 @@ namespace tp { // We have no rights to modify if someone references that memory too // So we need to create new copy of StringData if (mData->mReferenceCount > 1) { - mData = new (sStringPool->allocate(0)) StringData(mData->mBuff); + mData = new (sStringPool.allocate(0)) StringData(mData->mBuff); incReference(mData); return; } @@ -154,7 +154,7 @@ namespace tp { // Also if initial raw data was marked as const - create copy of raw data // Note - raw data will be lost at this point if no other record of such is stored if (mData->mIsConst) { - mData = new (sStringPool->allocate(0)) StringData(mData->mBuff); + mData = new (sStringPool.allocate(0)) StringData(mData->mBuff); incReference(mData); } } @@ -176,14 +176,18 @@ namespace tp { StringTemplate& operator=(const StringTemplate& in) { if (&in == this) return *this; - ~StringTemplate(); + this->~StringTemplate(); new (this) StringTemplate(in); return *this; } + + public: // Debugging + [[nodiscard]] ualni getReferenceCount() const { return mData->mReferenceCount; } + [[nodiscard]] bool getIsConstFlag() const { return mData->mIsConst; } }; template - PoolAlloc, StringTemplate::STRINGS_POOL_SIZE>* StringTemplate::sStringPool; + PoolAlloc, StringTemplate::STRINGS_POOL_SIZE> StringTemplate::sStringPool; template StringData StringTemplate::sNullString; diff --git a/Strings/tests/Tests.cpp b/Strings/tests/Tests.cpp index fc45eb9..0129583 100644 --- a/Strings/tests/Tests.cpp +++ b/Strings/tests/Tests.cpp @@ -1,7 +1,9 @@ -#include "Testing.hpp" +#include "Tests.hpp" + #include "Strings.hpp" +void testStringLogic(); void testStrings(); void testLogging(); @@ -14,6 +16,7 @@ int main() { return 1; } + testStringLogic(); testStrings(); testLogging(); diff --git a/Strings/tests/Tests.hpp b/Strings/tests/Tests.hpp new file mode 100644 index 0000000..9f2e45d --- /dev/null +++ b/Strings/tests/Tests.hpp @@ -0,0 +1,4 @@ +#pragma once + +#include "Testing.hpp" +#include "Allocators.hpp" \ No newline at end of file diff --git a/Strings/tests/TestsLogging.cpp b/Strings/tests/TestsLogging.cpp index 65bd9b7..58c281b 100644 --- a/Strings/tests/TestsLogging.cpp +++ b/Strings/tests/TestsLogging.cpp @@ -1,11 +1,9 @@ -#include "Testing.hpp" +#include "Tests.hpp" #include "Logging.hpp" -TEST_DEF_STATIC(Simple) { - // TEST(false); -} +using namespace tp; TEST_DEF(Logging) { - testSimple(); + } \ No newline at end of file diff --git a/Strings/tests/TestsStrings.cpp b/Strings/tests/TestsStrings.cpp index a7365f6..1e8db8d 100644 --- a/Strings/tests/TestsStrings.cpp +++ b/Strings/tests/TestsStrings.cpp @@ -1,16 +1,37 @@ -#include "Testing.hpp" +#include "Tests.hpp" #include "Strings.hpp" -TEST_DEF_STATIC(StringLogic) { - // TEST(false); -} +using namespace tp; + +struct TestStruct { + String str = "test data"; +}; TEST_DEF_STATIC(Simple) { - // TEST(false); + String str; + TEST(str.getIsConstFlag()); + TEST(str.getReferenceCount() == 2); + + String str2; + TEST(str.getIsConstFlag()); + TEST(str.getReferenceCount() == 3); + + auto tmp = new TestStruct(); + TEST(tmp->str.getIsConstFlag()); + TEST(tmp->str.getReferenceCount() == 1); + + str = tmp->str; + + TEST(str.getIsConstFlag()); + TEST(str.getReferenceCount() == 2); + + delete tmp; + + TEST(str.getIsConstFlag()); + TEST(str.getReferenceCount() == 1); } TEST_DEF(Strings) { - testStringLogic(); testSimple(); } \ No newline at end of file diff --git a/Strings/tests/TestsStringsLogic.cpp b/Strings/tests/TestsStringsLogic.cpp new file mode 100644 index 0000000..231a790 --- /dev/null +++ b/Strings/tests/TestsStringsLogic.cpp @@ -0,0 +1,247 @@ + +#include "Tests.hpp" +#include "StringLogic.hpp" + +using namespace tp; + +typedef StringLogic Logic; + + +TEST_DEF(isNewLineChar) { + // Test isNewLineChar function + TEST(Logic::isNewLineChar('\n')); + TEST(Logic::isNewLineChar('\r')); + TEST(!Logic::isNewLineChar('a')); +} + +TEST_DEF(isEndChar) { + // Test isEndChar function + TEST(Logic::isEndChar('\0')); + TEST(!Logic::isEndChar('a')); +} + +TEST_DEF(calcLength) { + // Test calcLength function + const char* str1 = "Hello"; + const char* str2 = "Wor\nld2"; + TEST(Logic::calcLength(str1) == 5); + TEST(Logic::calcLength(str2) == 7); +} + +// ------------------------------- Inserting / Removing ----------------------------------------- // + +TEST_DEF(insertLogic_emptyTarget) { + // Test insertLogic function with an empty target string + const char* cur = "Hello"; + const char* tar = ""; + const char* result = "Hello"; + char* inserted = Logic::insertLogic(cur, tar, 5, 0); + TEST(Logic::isEqualLogic(inserted, result)); + delete[] inserted; +} + +TEST_DEF(insertLogic_emptyCurrent) { + // Test insertLogic function with an empty current string + const char* cur = ""; + const char* tar = "World"; + const char* result = "World"; + char* inserted = Logic::insertLogic(cur, tar, 0, 5); + TEST(Logic::isEqualLogic(inserted, result)); + delete[] inserted; +} + +TEST_DEF(insertLogic_curLengthLessThanPos) { + // Test insertLogic function with current length less than the insert position + const char* cur = "Hello"; + const char* tar = "World"; + const char* result = "HelloWorld"; + char* inserted = Logic::insertLogic(cur, tar, 10, 5); + TEST(Logic::isEqualLogic(inserted, result)); + delete[] inserted; +} + +TEST_DEF(removeLogic_emptyCurrent) { + // Test removeLogic function with an empty current string + const char* cur = ""; + const char* result = ""; + char* removed = Logic::removeLogic(cur, 0, 0); + TEST(Logic::isEqualLogic(removed, result)); + delete[] removed; +} + +TEST_DEF(removeLogic_removeUntilEnd) { + // Test removeLogic function by removing until the end of the current string + const char* cur = "HelloWorld"; + const char* result = "World"; + char* removed = Logic::removeLogic(cur, 0, 5); + TEST(Logic::isEqualLogic(removed, result)); + delete[] removed; +} + +TEST_DEF(removeLogic_removeMiddleCharacters) { + // Test removeLogic function by removing characters in the middle of the current string + const char* cur = "HelloWorld"; + const char* result = "Helrld"; + char* removed = Logic::removeLogic(cur, 3, 7); + TEST(Logic::isEqualLogic(removed, result)); + delete[] removed; +} + +TEST_DEF(insertLogicGood) { + // Test insertLogic function + const char* cur = "Hello"; + const char* tar = "World"; + const char* result = "HelloWorld"; + char* inserted = Logic::insertLogic(cur, tar, 5, 5); + TEST(Logic::isEqualLogic(inserted, result)); + delete[] inserted; +} + +TEST_DEF(removeLogicGood) { + // Test removeLogic function + const char* cur = "HelloWorld"; + const char* result = "Helo"; + char* removed = Logic::removeLogic(cur, 3, 8); + TEST(Logic::isEqualLogic(removed, result)); + delete[] removed; +} + + +TEST_DEF(insertLogic) { + // Test insertLogic function + testinsertLogicGood(); + testinsertLogic_emptyTarget(); + testinsertLogic_emptyCurrent(); + // testinsertLogic_curLengthLessThanPos(); +} + +TEST_DEF(removeLogic) { + // Test removeLogic function + // testremoveLogicGood(); + testremoveLogic_emptyCurrent(); + testremoveLogic_removeUntilEnd(); + testremoveLogic_removeMiddleCharacters(); +} + +// -------------------------------- Conversions --------------------------------------- // + +TEST_DEF(convertStringToValue_alni) { + const char* str1 = "123"; + const char* str2 = "456xyz"; + const char* str3 = "789"; + alni output; + TEST(Logic::convertStringToValue(str1, output) && output == 123); + TEST(!Logic::convertStringToValue(str2, output)); + TEST(Logic::convertStringToValue(str3, output) && output == 789); +} + +TEST_DEF(convertStringToValue_alnf) { + const char* str1 = "12.34"; + const char* str2 = "56.78xyz"; + const char* str3 = "90.12"; + alnf output; + TEST(Logic::convertStringToValue(str1, output) && output == 12.34); + TEST(!Logic::convertStringToValue(str2, output)); + TEST(Logic::convertStringToValue(str3, output) && output == 90.12); +} + +TEST_DEF(convertStringToValue_bool) { + const char* str1 = "true"; + const char* str2 = "false"; + const char* str3 = "1"; + const char* str4 = "0"; + bool output; + TEST(Logic::convertStringToValue(str1, output) && output == true); + TEST(Logic::convertStringToValue(str2, output) && output == false); + TEST(Logic::convertStringToValue(str3, output) && output == true); + TEST(Logic::convertStringToValue(str4, output) && output == false); +} + +TEST_DEF(convertValueToString_alni) { + alni input1 = 123; + alni input2 = -456; + char output[10]; + TEST(Logic::convertValueToString(input1, output, 10)); + TEST(Logic::isEqualLogic(output, "123")); + + TEST(Logic::convertValueToString(input2, output, 10)); + TEST(Logic::isEqualLogic(output, "-456")); +} + +TEST_DEF(convertValueToString_alnf) { + alnf input1 = 12.34; + alnf input2 = -56.78; + char output[10]; + + TEST(Logic::convertValueToString(input1, output, 10)); + TEST(Logic::isEqualLogic(output, "12.340000")); + + TEST(Logic::convertValueToString(input2, output, 10)); + TEST(Logic::isEqualLogic(output, "-56.78000")); +} + +TEST_DEF(convertValueToString_bool) { + bool input1 = true; + bool input2 = false; + char output[10]; + + TEST(Logic::convertValueToString(input1, output, 10)); + TEST(Logic::isEqualLogic(output, "true")); + + TEST(Logic::convertValueToString(input2, output, 10)); + TEST(Logic::isEqualLogic(output, "false")); +} + +TEST_DEF(Conversions) { + testconvertValueToString_bool(); + testconvertValueToString_alnf(); + testconvertValueToString_alni(); + testconvertStringToValue_bool(); + testconvertStringToValue_alnf(); + testconvertStringToValue_alni(); +} + +// ------------------------------------------------------------------------ // + +TEST_DEF(isEqualLogic) { + // Test isEqualLogic function + const char* str1 = "Hello"; + const char* str2 = "Hello"; + const char* str3 = "World"; + const char* str4 = "Hello2"; + TEST(Logic::isEqualLogic(str1, str2)); + TEST(!Logic::isEqualLogic(str1, str4)); + TEST(!Logic::isEqualLogic(str1, str3)); +} + +TEST_DEF(calcLineOffsets) { + // Test calcLineOffsets function + const char* str = "\n\nHello\nWorld\n\n "; + Buffer offsets; + Logic::calcLineOffsets(str, Logic::calcLength(str), offsets); + + TEST(offsets.size() == 7); + + TEST(offsets[0] == 0); + TEST(offsets[1] == 1); + TEST(offsets[2] == 2); + TEST(offsets[3] == 8); + TEST(offsets[4] == 14); + TEST(offsets[5] == 15); + TEST(offsets[6] == 16); +} + + +TEST_DEF(StringLogic) { + testisNewLineChar(); + testisEndChar(); + testcalcLength(); + + testisEqualLogic(); + testcalcLineOffsets(); + + testinsertLogic(); + testremoveLogic(); + + testConversions(); +} \ No newline at end of file