1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 08:47:17 +01:00
SqMod/source/Library/String.cpp
Sandu Liviu Catalin b24104c99f Revised the standard string library to have cleaner code.
Implemented the Explode/Implode functions that work with a single delimiter.
2016-03-22 02:07:34 +02:00

497 lines
15 KiB
C++

// ------------------------------------------------------------------------------------------------
#include "Library/String.hpp"
#include "Base/Shared.hpp"
#include "Base/Buffer.hpp"
// ------------------------------------------------------------------------------------------------
#include <sqstdstring.h>
// ------------------------------------------------------------------------------------------------
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
// ------------------------------------------------------------------------------------------------
#include <algorithm>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
CSStr LeftStr(CSStr t, SQChar f, Uint32 w)
{
// Is the specified width valid?
if (!w)
{
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')
{
// Insert only the fill character
std::memset(b.Data(), f, w);
}
else
{
// Calculate the string length
const Uint32 n = strlen(t);
// Insert only the fill character first
std::memset(b.Data(), f, w);
// Overwrite with the specified string
std::strncpy(b.Data(), t, n);
}
// End the resulted string
b.At(w) = '\0';
// Return the resulted string
return b.Get< SQChar >();
}
CSStr LeftStr(CSStr t, SQChar f, Uint32 w, Uint32 o)
{
// Is the specified width valid?
if (!w)
{
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')
{
// Insert only the fill character
std::memset(b.Data(), f, w);
}
else
{
// Clculate the string length
const Uint32 n = strlen(t);
// Insert the fill character first
std::memset(b.Data(), f, w);
// Overwrite with the specified string
std::strncpy(b.Data() + o, t, w - n);
}
// End the resulted string
b.At(w) = '\0';
// Return the resulted string
return b.Get();
}
// ------------------------------------------------------------------------------------------------
CSStr RightStr(CSStr t, SQChar f, Uint32 w)
{
// Is the specified width valid?
if (!w)
{
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')
{
// Insert only the fill character
std::memset(b.Data(), f, w);
}
else
{
// Calculate the string length
const Uint32 n = strlen(t);
// Insert the fill character first
std::memset(b.Data(), f, w);
// Overwrite with the specified string
if (n >= w)
{
std::strncpy(b.Data(), t, w);
}
else
{
std::strncpy(b.Data() + (w - n), t, n);
}
}
// End the resulted string
b.At(w) = '\0';
// Return the resulted string
return b.Get< SQChar >();
}
CSStr RightStr(CSStr t, SQChar f, Uint32 w, Uint32 o)
{
// Is the specified width valid?
if (!w)
{
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')
{
// Insert only the fill character
std::memset(b.Data(), f, w);
}
else
{
// Calculate the string length
const Uint32 n = strlen(t);
// Insert the fill character first
std::memset(b.Data(), f, w);
// Overwrite with the specified string
if (n >= w || (n + o) >= w)
{
std::strncpy(b.Data(), t, w - o);
}
else
{
std::strncpy(b.Data() + ((w - n) - o), t, n);
}
}
// End the resulted string
b.At(w) = '\0';
// Return the resulted string
return b.Get< SQChar >();
}
// ------------------------------------------------------------------------------------------------
CSStr CenterStr(CSStr t, SQChar f, Uint32 w)
{
// Is the specified width valid?
if (!w)
{
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')
{
// Insert only the fill character
std::memset(b.Data(), f, w);
}
else
{
// Calculate the string length
const Uint32 n = strlen(t);
// Insert only the fill character first
std::memset(b.Data(), f, w);
// Overwrite with the specified string
std::strncpy(b.Data() + ((w/2) - (n/2)), t, n);
}
// End the resulted string
b.At(w) = '\0';
// Return the resulted string
return b.Get< SQChar >();
}
// ------------------------------------------------------------------------------------------------
CSStr StrJustAlphaNum(CSStr str)
{
// See if we actually have something to search for
if(!str || *str == '\0')
{
return _SC(""); // Default to an empty string!
}
// Calculate the string length
Uint32 size = std::strlen(str);
// Obtain a temporary buffer
Buffer b(size + 1); // + null terminator
// Resulted string size
Uint32 n = 0;
// Currently processed character
SQChar c = 0;
// Process characters
while ((c = *(str++)) != '\0')
{
// Is this an alpha-numeric character?
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';
// Return the string
return b.Get< SQChar >();
}
// ------------------------------------------------------------------------------------------------
CSStr StrToLowercase(CSStr str)
{
// See if we actually have something to search for
if(!str || *str == '\0')
{
return _SC(""); // Default to an empty string!
}
// Calculate the string length
Uint32 size = std::strlen(str);
// Obtain a temporary buffer
Buffer b(size + 1); // + null terminator
// Resulted string size
Uint32 n = 0;
// Currently processed character
SQChar c = 0;
// Process characters
while ((c = *(str++)) != '\0')
{
// Convert it and move to the next one
b.At(n++) = std::tolower(c);
}
// End the resulted string
b.At(n) = '\0';
// Return the string
return b.Get< SQChar >();
}
// ------------------------------------------------------------------------------------------------
CSStr StrToUppercase(CSStr str)
{
// See if we actually have something to search for
if(!str || *str == '\0')
{
return _SC(""); // Default to an empty string!
}
// Calculate the string length
Uint32 size = std::strlen(str); // + null terminator
// Obtain a temporary buffer
Buffer b(size + 1); // + null terminator
// Resulted string size
Uint32 n = 0;
// Currently processed character
SQChar c = 0;
// Process characters
while ((c = *(str++)) != '\0')
{
// Convert it and move to the next one
b.At(n++) = std::toupper(c);
}
// End the resulted string
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 >();
}
// ------------------------------------------------------------------------------------------------
static CSStr FromArray(Array & arr)
{
// Determine array size
const Int32 length = static_cast< Int32 >(arr.Length());
// Obtain a temporary buffer
Buffer b(length * sizeof(Int32));
// Get array elements as integers
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';
// Return the string
return b.Get< SQChar >();
}
// ------------------------------------------------------------------------------------------------
static SQInteger StdPrintF(HSQUIRRELVM vm)
{
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 the error!
}
// Send the resulted string to console as a user message
LogUsr("%s", msg);
// This function doesn't return anything
return 0;
}
// ================================================================================================
void Register_String(HSQUIRRELVM vm)
{
Table strns(vm);
strns.Func(_SC("FromArray"), &FromArray)
.Overload< CSStr (*)(CSStr, SQChar, Uint32) >(_SC("Left"), &LeftStr)
.Overload< CSStr (*)(CSStr, SQChar, Uint32, Uint32) >(_SC("Left"), &LeftStr)
.Overload< CSStr (*)(CSStr, SQChar, Uint32) >(_SC("Right"), &RightStr)
.Overload< CSStr (*)(CSStr, SQChar, Uint32, Uint32) >(_SC("Right"), &RightStr)
.Func(_SC("Center"), &CenterStr)
.Func(_SC("JustAlphaNum"), &StrJustAlphaNum)
.Func(_SC("Lowercase"), &StrToLowercase)
.Func(_SC("Uppercase"), &StrToUppercase)
.Func(_SC("Explode"), &StrExplode)
.Func(_SC("Implode"), &StrImplode);
RootTable(vm).Bind(_SC("SqStr"), strns);
RootTable(vm).SquirrelFunc(_SC("printf"), &StdPrintF);
}
} // Namespace:: SqMod