From b24104c99fb76f351611120a742d5bafdd3ecbbf Mon Sep 17 00:00:00 2001 From: Sandu Liviu Catalin Date: Tue, 22 Mar 2016 02:07:34 +0200 Subject: [PATCH] Revised the standard string library to have cleaner code. Implemented the Explode/Implode functions that work with a single delimiter. --- source/Library/String.cpp | 305 +++++++++++++++++++++++++++++++------- 1 file changed, 253 insertions(+), 52 deletions(-) diff --git a/source/Library/String.cpp b/source/Library/String.cpp index cb180709..4a391d7d 100644 --- a/source/Library/String.cpp +++ b/source/Library/String.cpp @@ -23,24 +23,28 @@ CSStr LeftStr(CSStr t, SQChar f, Uint32 w) { // Is the specified width valid? if (!w) - return _SC(""); + { + return _SC(""); // Default to an empty string! + } // Allocate a buffer with the requested width Buffer b(w + 1); // + null terminator // Is the specified string valid? - if (!t || *t == 0) + if (!t || *t == '\0') + { // Insert only the fill character - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); + } else { // Calculate the string length const Uint32 n = strlen(t); // Insert only the fill character first - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); // Overwrite with the specified string - strncpy(b.Data(), t, n); + std::strncpy(b.Data(), t, n); } // End the resulted string - b.At(w) = 0; + b.At(w) = '\0'; // Return the resulted string return b.Get< SQChar >(); } @@ -49,27 +53,33 @@ CSStr LeftStr(CSStr t, SQChar f, Uint32 w, Uint32 o) { // Is the specified width valid? if (!w) - return _SC(""); + { + return _SC(""); // Default to an empty string! + } // Is the specified offset within width range? else if (o > w) + { STHROWF("Offset is out of bounds"); + } // Allocate a buffer with the requested width Buffer b(w + 1); // + null terminator // Is the specified string valid? - if (!t || *t == 0) + if (!t || *t == '\0') + { // Insert only the fill character - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); + } else { // Clculate the string length const Uint32 n = strlen(t); // Insert the fill character first - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); // Overwrite with the specified string - strncpy(b.Data() + o, t, w - n); + std::strncpy(b.Data() + o, t, w - n); } // End the resulted string - b.At(w) = 0; + b.At(w) = '\0'; // Return the resulted string return b.Get(); } @@ -79,27 +89,35 @@ CSStr RightStr(CSStr t, SQChar f, Uint32 w) { // Is the specified width valid? if (!w) - return _SC(""); + { + return _SC(""); // Default to an empty string! + } // Allocate a buffer with the requested width Buffer b(w + 1); // + null terminator // Is the specified string valid? - if (!t || *t == 0) + if (!t || *t == '\0') + { // Insert only the fill character - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); + } else { // Calculate the string length const Uint32 n = strlen(t); // Insert the fill character first - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); // Overwrite with the specified string if (n >= w) - strncpy(b.Data(), t, w); + { + std::strncpy(b.Data(), t, w); + } else - strncpy(b.Data() + (w - n), t, n); + { + std::strncpy(b.Data() + (w - n), t, n); + } } // End the resulted string - b.At(w) = 0; + b.At(w) = '\0'; // Return the resulted string return b.Get< SQChar >(); } @@ -108,30 +126,40 @@ CSStr RightStr(CSStr t, SQChar f, Uint32 w, Uint32 o) { // Is the specified width valid? if (!w) - return _SC(""); + { + return _SC(""); // Default to an empty string! + } // Is the specified offset within width range? else if (o > w) + { STHROWF("Offset is out of bounds"); + } // Allocate a buffer with the requested width Buffer b(w + 1); // + null terminator // Is the specified string valid? - if (!t || *t == 0) + if (!t || *t == '\0') + { // Insert only the fill character - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); + } else { // Calculate the string length const Uint32 n = strlen(t); // Insert the fill character first - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); // Overwrite with the specified string if (n >= w || (n + o) >= w) - strncpy(b.Data(), t, w - o); + { + std::strncpy(b.Data(), t, w - o); + } else - strncpy(b.Data() + ((w - n) - o), t, n); + { + std::strncpy(b.Data() + ((w - n) - o), t, n); + } } // End the resulted string - b.At(w) = 0; + b.At(w) = '\0'; // Return the resulted string return b.Get< SQChar >(); } @@ -141,24 +169,28 @@ CSStr CenterStr(CSStr t, SQChar f, Uint32 w) { // Is the specified width valid? if (!w) - return _SC(""); + { + return _SC(""); // Default to an empty string! + } // Allocate a buffer with the requested width Buffer b(w + 1); // + null terminator // Is the specified string valid? - if (!t || *t == 0) + if (!t || *t == '\0') + { // Insert only the fill character - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); + } else { // Calculate the string length const Uint32 n = strlen(t); // Insert only the fill character first - memset(b.Data(), f, w); + std::memset(b.Data(), f, w); // Overwrite with the specified string - strncpy(b.Data() + ((w/2) - (n/2)), t, n); + std::strncpy(b.Data() + ((w/2) - (n/2)), t, n); } // End the resulted string - b.At(w) = 0; + b.At(w) = '\0'; // Return the resulted string return b.Get< SQChar >(); } @@ -167,10 +199,12 @@ CSStr CenterStr(CSStr t, SQChar f, Uint32 w) CSStr StrJustAlphaNum(CSStr str) { // See if we actually have something to search for - if(!str || *str == 0) - return _SC(""); + if(!str || *str == '\0') + { + return _SC(""); // Default to an empty string! + } // Calculate the string length - Uint32 size = strlen(str); + Uint32 size = std::strlen(str); // Obtain a temporary buffer Buffer b(size + 1); // + null terminator // Resulted string size @@ -181,12 +215,14 @@ CSStr StrJustAlphaNum(CSStr str) while ((c = *(str++)) != '\0') { // Is this an alpha-numeric character? - if (isalnum(c) != 0) + if (std::isalnum(c) != 0) + { // Save it and move to the next one b.At(n++) = c; + } } // End the resulted string - b.At(n) = 0; + b.At(n) = '\0'; // Return the string return b.Get< SQChar >(); } @@ -195,10 +231,12 @@ CSStr StrJustAlphaNum(CSStr str) CSStr StrToLowercase(CSStr str) { // See if we actually have something to search for - if(!str || *str == 0) - return _SC(""); + if(!str || *str == '\0') + { + return _SC(""); // Default to an empty string! + } // Calculate the string length - Uint32 size = strlen(str); + Uint32 size = std::strlen(str); // Obtain a temporary buffer Buffer b(size + 1); // + null terminator // Resulted string size @@ -207,10 +245,12 @@ CSStr StrToLowercase(CSStr str) SQChar c = 0; // Process characters while ((c = *(str++)) != '\0') + { // Convert it and move to the next one - b.At(n++) = tolower(c); + b.At(n++) = std::tolower(c); + } // End the resulted string - b.At(n) = 0; + b.At(n) = '\0'; // Return the string return b.Get< SQChar >(); } @@ -219,10 +259,12 @@ CSStr StrToLowercase(CSStr str) CSStr StrToUppercase(CSStr str) { // See if we actually have something to search for - if(!str || *str == 0) - return _SC(""); + if(!str || *str == '\0') + { + return _SC(""); // Default to an empty string! + } // Calculate the string length - Uint32 size = strlen(str); // + null terminator + Uint32 size = std::strlen(str); // + null terminator // Obtain a temporary buffer Buffer b(size + 1); // + null terminator // Resulted string size @@ -230,11 +272,164 @@ CSStr StrToUppercase(CSStr str) // Currently processed character SQChar c = 0; // Process characters - while ((c = *(str++)) != 0) + while ((c = *(str++)) != '\0') + { // Convert it and move to the next one - b.At(n++) = toupper(c); + b.At(n++) = std::toupper(c); + } // End the resulted string - b.At(n) = 0; + b.At(n) = '\0'; + // Return the string + return b.Get< SQChar >(); +} + +// ------------------------------------------------------------------------------------------------ +static bool OnlyDelimiter(CSStr str, SQChar chr) +{ + while (*str != '\0') + { + // Is this different from the delimiter? + if (*(str++) != chr) + { + // Another character was found + return false; + } + } + // No other character was found + return true; +} + +// ------------------------------------------------------------------------------------------------ +static Array StrExplode(CSStr str, SQChar chr, bool empty) +{ + // See if we actually have something to explode + if(!str || *str == '\0') + { + // Default to an empty array + return Array(DefaultVM::Get(), 0); + } + // Don't modify the specified string pointer + CSStr itr = str, last = str; + // The number of delimiter occurrences + Uint32 num = 0; + // Pre-count how many delimiters of this type exist + while (*itr != '\0') + { + // Is this our delimiter? + if (*(itr++) == chr) + { + // Are we allowed to include empty elements? + if (empty || (itr - last) > 1) + { + // Increase the count + ++num; + } + // Update the last delimiter position + last = itr; + } + } + // Were there no delimiters found and can we include empty elements? + if (num == 0 && !empty && (str[1] == '\0' || OnlyDelimiter(str, chr))) + { + // Default to an empty array + return Array(DefaultVM::Get(), 0); + } + // Have we found any delimiters? + else if (num == 0) + { + // Create an array with just one element + Array arr(DefaultVM::Get(), 1); + // Test against strings with only delimiters + if (str[1] == '\0' || OnlyDelimiter(str, chr)) + { + arr.SetValue(0, _SC("")); // Add an empty string + } + else + { + arr.SetValue(0, str); // Add the whole string + } + // Return the resulted array + return arr; + } + // Is there anything after the last delimiter? + if (itr != last && *last != chr) + { + ++num; // Add it to the counter + } + // Pre-allocate an array with the number of found delimiters + Array arr(DefaultVM::Get(), num); + // Remember the initial stack size + StackGuard sg; + // Push the array object on the stack + sq_pushobject(DefaultVM::Get(), arr.GetObject()); + // Don't modify the specified string pointer + itr = str, last = str; + // Reset the counter and use it as the element index + num = 0; + // Process the string again, this time slicing the actual elements + while (*itr != '\0') + { + // Is this our delimiter? + if (*itr++ == chr) + { + // Are we allowed to include empty elements? + if (empty || (itr - last) > 1) + { + // Push the element index on the stack and advance to the next one + sq_pushinteger(DefaultVM::Get(), num++); + // Push the string portion on the stack + sq_pushstring(DefaultVM::Get(), last, itr - last - 1); + // Assign the string onto the + sq_set(DefaultVM::Get(), -3); + } + // Update the last delimiter position + last = itr; + } + } + // Is there anything after the last delimiter? + if (itr != last && *last != chr) + { + // Push the element index on the stack + sq_pushinteger(DefaultVM::Get(), num); + // Add the remaining string as an element + sq_pushstring(DefaultVM::Get(), last, itr - last); + // Assign the string onto the + sq_set(DefaultVM::Get(), -3); + } + // Return the resulted array and let the stack guard handle the cleanup + return arr; +} + +// ------------------------------------------------------------------------------------------------ +static CSStr StrImplode(Array & arr, SQChar chr) +{ + // Determine array size + const Int32 length = static_cast< Int32 >(arr.Length()); + // Is there anything to implode? + if (length <= 0) + { + return _SC(""); // Default to an empty string + } + // Obtain a temporary buffer + Buffer b(length * 32); + // Process array elements + for (SQInteger i = 0; i < length; ++i) + { + // Retrieve the element value as string + SharedPtr< String > str = arr.GetValue< String >(i); + // Was there any value retrieved? + if (!!str) + { + // Append the value to the buffer + b.AppendS(str->data(), str->size()); + } + // Append the delimiter + b.Push(chr); + } + // Move the cursor back one element + b.Retreat(1); + // Set that as the null character + b.Cursor() = '\0'; // Return the string return b.Get< SQChar >(); } @@ -250,9 +445,11 @@ static CSStr FromArray(Array & arr) arr.GetArray< Int32 >(b.Get< Int32 >(), length); // Overwrite integers with characters for (Int32 n = 0; n < length; ++n) + { b.At(n) = static_cast< SQChar >(b.At< Int32 >(n)); + } // Terminate the resulted string - b.At(length) = 0; + b.At(length) = '\0'; // Return the string return b.Get< SQChar >(); } @@ -260,13 +457,15 @@ static CSStr FromArray(Array & arr) // ------------------------------------------------------------------------------------------------ static SQInteger StdPrintF(HSQUIRRELVM vm) { - CStr msg = NULL; + CStr msg = nullptr; SQInteger length = 0; // Attempt to run the specified format and save the result const SQRESULT res = sqstd_format(vm, 2, &length, &msg); // Validate the result for errors and propagate them to the VM if(SQ_FAILED(res)) - return res; + { + return res; // Return the error! + } // Send the resulted string to console as a user message LogUsr("%s", msg); // This function doesn't return anything @@ -286,7 +485,9 @@ void Register_String(HSQUIRRELVM vm) .Func(_SC("Center"), &CenterStr) .Func(_SC("JustAlphaNum"), &StrJustAlphaNum) .Func(_SC("Lowercase"), &StrToLowercase) - .Func(_SC("Uppercase"), &StrToUppercase); + .Func(_SC("Uppercase"), &StrToUppercase) + .Func(_SC("Explode"), &StrExplode) + .Func(_SC("Implode"), &StrImplode); RootTable(vm).Bind(_SC("SqStr"), strns); RootTable(vm).SquirrelFunc(_SC("printf"), &StdPrintF);