Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions core/logic/smn_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,25 @@ static cell_t StringToInt64(IPluginContext *pCtx, const cell_t *params)
return dummy - str;
}

static cell_t StringToInt64Ex(IPluginContext *pCtx, const cell_t *params)
{
cell_t* out;
if (int err = pCtx->LocalToPhysAddr(params[1], &out); err != SP_ERROR_NONE)
return pCtx->ThrowNativeErrorEx(err, "Could not read argument (error %d)", err);

char *str, *dummy = nullptr;
if (int err = pCtx->LocalToString(params[2], &str); err != SP_ERROR_NONE)
return pCtx->ThrowNativeErrorEx(err, "Invalid str (error %d)", err);

cell_t *bytes;
if (int err = pCtx->LocalToPhysAddr(params[3], &bytes); err != SP_ERROR_NONE)
return pCtx->ThrowNativeErrorEx(err, "Invalid bytes (error %d)", err);

*reinterpret_cast<int64_t*>(out) = strtoll(str, &dummy, params[4]);
*bytes = dummy - str;
return 0;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

everything here it's pretty inconsistent with everything else, nothing here error checks, init-statement use

functionally looks good otherwise

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused. Could you give a more specific description of the problem?

Regarding the error checking of the pCtx->LocalTo*** part, I referenced: https://github.com/alliedmodders/sourcepawn/blob/4e87a849f092e9bb3e70e50f6a6f689e963974f0/vm/shell/shell.cpp#L127

I think this way is safer, and those few condition checks won't really slow things down.

Regarding the usage of the strtoll part, I referenced:

uint64_t number = (uint64_t)strtoll(str, &dummy, params[3]);

}

static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params)
{
char *str;
Expand Down Expand Up @@ -636,6 +655,7 @@ REGISTER_NATIVES(basicStrings)
{"StringToInt", sm_strconvint},
{"StringToIntEx", StringToIntEx},
{"StringToInt64", StringToInt64},
{"StringToInt64Ex", StringToInt64Ex},
{"StringToFloat", sm_strtofloat},
{"StringToFloatEx", StringToFloatEx},
{"StripQuotes", StripQuotes},
Expand Down
10 changes: 10 additions & 0 deletions plugins/include/string.inc
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,16 @@ native int StringToIntEx(const char[] str, int &result, int nBase=10);
*/
native int StringToInt64(const char[] str, int result[2], int nBase=10);

/**
* Converts a string to an int64 value.
*
* @param str String to convert.
* @param bytes Number of characters consumed.
* @param nBase Numerical base to use. 10 is default.
* @return int64 conversion of string, or 0 on failure.
*/
native int64 StringToInt64Ex(const char[] str, int &bytes=0, int nBase=10);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little torn on the name. With the other StringTo_Ex natives, the Ex versions return the count of chars consumed and give the actual result via an out param. This does the opposite, due to it using the Ex suffix for a different reason.

That said, I'm struggling to think of a better name. Something like StringToInt64Native sounds clunky. Maybe StringToI64? Anyone else have thoughts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think StringToI64 is a good name and fits the existing StringTo_Ex natives well.

I'd also like to suggest ParseInt64 as another candidate — it avoids the Ex suffix confusion entirely, and the Parse verb more naturally conveys "extract a value from a string" with the result returned directly. It also aligns with naming conventions in languages like native int ParseTime(const char[] dateTime, const char[] format);, C# (Int64.Parse) and Java (Long.parseLong), which might feel intuitive to a wider audience.

Happy to go with whichever name the team prefers!


/**
* Converts an integer to a string.
*
Expand Down
14 changes: 14 additions & 0 deletions plugins/include/testing.inc
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,17 @@ stock void AssertStrArrayEq(const char[] text, const char[][] value, const char[
}
PrintToServer("[%d] %s: '%s' arrays are equal OK", TestNumber, TestContext, text);
}

stock void AssertInt64Eq(const char[] text, int64 value, int64 expected)
{
TestNumber++;
if (value == expected)
{
PrintToServer("[%d] %s: %s == %ld OK", TestNumber, TestContext, text, expected);
}
else
{
PrintToServer("[%d] %s FAIL: %s should be %ld, got %ld", TestNumber, TestContext, text, expected, value);
ThrowError("test %d (%s in %s) failed", TestNumber, text, TestContext);
}
}
108 changes: 108 additions & 0 deletions plugins/testsuite/mock/test_string.sp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <testing>


public void OnPluginStart()
{
Test_StringToInt64Ex();
}


void Test_StringToInt64Ex()
{
SetTestContext("Test StringToInt64Ex");

AssertInt64Eq("DEC 0", StringToInt64Ex("0"), 0);
AssertInt64Eq("DEC 1234567", StringToInt64Ex("1234567"), 1234567);
AssertInt64Eq("DEC 1234567654321", StringToInt64Ex("1234567654321"), 1234567654321);
AssertInt64Eq("DEC 9223372036854775807", StringToInt64Ex("9223372036854775807"), 9223372036854775807);
AssertInt64Eq("DEC -1234567", StringToInt64Ex("-1234567"), -1234567);
AssertInt64Eq("DEC -1234567654321", StringToInt64Ex("-1234567654321"), -1234567654321);
AssertInt64Eq("DEC -9223372036854775807", StringToInt64Ex("-9223372036854775807"), -9223372036854775807);

int bytes;

// bytes
AssertInt64Eq(
"result 0",
StringToInt64Ex("0", bytes),
0);
AssertEq("bytes 0", bytes, 1);

AssertInt64Eq(
"result 1234567",
StringToInt64Ex("1234567", bytes),
1234567);
AssertEq("bytes 1234567", bytes, 7);

AssertInt64Eq(
"result 1234567654321",
StringToInt64Ex("1234567654321", bytes),
1234567654321);
AssertEq("bytes 1234567654321", bytes, 13);

AssertInt64Eq(
"result 9223372036854775807",
StringToInt64Ex("9223372036854775807", bytes),
9223372036854775807);
AssertEq("bytes 9223372036854775807", bytes, 19);

AssertInt64Eq(
"result -1234567",
StringToInt64Ex("-1234567", bytes),
-1234567);
AssertEq("bytes -1234567", bytes, 8);

AssertInt64Eq(
"result -1234567654321",
StringToInt64Ex("-1234567654321", bytes),
-1234567654321);
AssertEq("bytes -1234567654321", bytes, 14);

AssertInt64Eq(
"result -9223372036854775807",
StringToInt64Ex("-9223372036854775807", bytes),
-9223372036854775807);
AssertEq("bytes -9223372036854775807", bytes, 20);

// Special nBase
AssertInt64Eq(
"result 10001111101110001111101110110101110110001",
StringToInt64Ex("10001111101110001111101110110101110110001", bytes, 2),
1234567654321);
AssertEq("bytes 10001111101110001111101110110101110110001", bytes, 41);

AssertInt64Eq(
"result 21756175665661",
StringToInt64Ex("21756175665661", bytes, 8),
1234567654321);
AssertEq("bytes 11F71F76BB1", bytes, 14);

AssertInt64Eq(
"result 11F71F76BB1",
StringToInt64Ex("11F71F76BB1", bytes, 16),
1234567654321);
AssertEq("bytes 11F71F76BB1", bytes, 11);

// Orther
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor typo here.

AssertInt64Eq(
"result a1b2c3d4e5f6g7",
StringToInt64Ex("a1b2c3d4e5f6g7", bytes),
0);
AssertEq("bytes a1b2c3d4e5f6g7", bytes, 0);

AssertInt64Eq(
"result 0b10101",
StringToInt64Ex("0b10101", bytes),
0);
AssertEq("bytes 0b10101", bytes, 1);

AssertInt64Eq(
"result 1_234_567",
StringToInt64Ex("1_234_567", bytes),
1);
AssertEq("bytes 1_234_567", bytes, 1);
}