diff --git a/core/logic/smn_string.cpp b/core/logic/smn_string.cpp index be7488dcd6..ca181d41d6 100644 --- a/core/logic/smn_string.cpp +++ b/core/logic/smn_string.cpp @@ -155,6 +155,25 @@ static cell_t StringToInt64(IPluginContext *pCtx, const cell_t *params) return dummy - str; } +static cell_t StringToI64(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 *consumed; + if (int err = pCtx->LocalToPhysAddr(params[4], &consumed); err != SP_ERROR_NONE) + return pCtx->ThrowNativeErrorEx(err, "Invalid nConsumed (error %d)", err); + + *reinterpret_cast(out) = strtoll(str, &dummy, params[3]); + *consumed = dummy - str; + return 0; +} + static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params) { char *str; @@ -636,6 +655,7 @@ REGISTER_NATIVES(basicStrings) {"StringToInt", sm_strconvint}, {"StringToIntEx", StringToIntEx}, {"StringToInt64", StringToInt64}, + {"StringToI64", StringToI64}, {"StringToFloat", sm_strtofloat}, {"StringToFloatEx", StringToFloatEx}, {"StripQuotes", StripQuotes}, diff --git a/plugins/include/string.inc b/plugins/include/string.inc index c3851009cd..af15d88f2f 100644 --- a/plugins/include/string.inc +++ b/plugins/include/string.inc @@ -204,6 +204,10 @@ native int StringToIntEx(const char[] str, int &result, int nBase=10); /** * Converts a string to a 64-bit integer. * + * @note If the value is outside the range of representable values, + * If the value is positive, this function will return INT64_MAX. + * If the value is negative, this function will return INT64_MIN. + * * @param str String to convert. * @param result Array to store the upper and lower * 32-bits of the 64-bit integer. @@ -212,6 +216,20 @@ 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 type value. + * + * @note If the value is outside the range of representable values, + * If the value is positive, this function will return INT64_MAX. + * If the value is negative, this function will return INT64_MIN. + * + * @param str String to convert. + * @param nBase Numerical base to use. 10 is default. + * @param nConsumed Number of characters consumed. + * @return int64 conversion of string, or 0 on failure. + */ +native int64 StringToI64(const char[] str, int nBase=10, int &nConsumed=0); + /** * Converts an integer to a string. * diff --git a/plugins/include/testing.inc b/plugins/include/testing.inc index 6ff6d73201..657612cf65 100644 --- a/plugins/include/testing.inc +++ b/plugins/include/testing.inc @@ -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); + } +} diff --git a/plugins/testsuite/mock/test_string.sp b/plugins/testsuite/mock/test_string.sp new file mode 100644 index 0000000000..5c9ea07205 --- /dev/null +++ b/plugins/testsuite/mock/test_string.sp @@ -0,0 +1,144 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include + + +public void OnPluginStart() +{ + Test_StringToI64(); +} + + +void Test_StringToI64() +{ + SetTestContext("Test StringToI64"); + + AssertInt64Eq("DEC 0", StringToI64("0"), 0); + AssertInt64Eq("DEC 1234567", StringToI64("1234567"), 1234567); + AssertInt64Eq("DEC 1234567654321", StringToI64("1234567654321"), 1234567654321); + AssertInt64Eq("DEC 9223372036854775807", StringToI64("9223372036854775807"), 9223372036854775807); + AssertInt64Eq("DEC -1234567", StringToI64("-1234567"), -1234567); + AssertInt64Eq("DEC -1234567654321", StringToI64("-1234567654321"), -1234567654321); + AssertInt64Eq("DEC -9223372036854775807", StringToI64("-9223372036854775807"), -9223372036854775807); + + int nConsumed; + + // nConsumed + AssertInt64Eq( + "result 0", + StringToI64("0", .nConsumed=nConsumed), + 0); + AssertEq("nConsumed 0", nConsumed, 1); + + AssertInt64Eq( + "result 1234567", + StringToI64("1234567", .nConsumed=nConsumed), + 1234567); + AssertEq("nConsumed 1234567", nConsumed, 7); + + AssertInt64Eq( + "result 1234567654321", + StringToI64("1234567654321", .nConsumed=nConsumed), + 1234567654321); + AssertEq("nConsumed 1234567654321", nConsumed, 13); + + AssertInt64Eq( + "result 9223372036854775807", + StringToI64("9223372036854775807", .nConsumed=nConsumed), + 9223372036854775807); + AssertEq("nConsumed 9223372036854775807", nConsumed, 19); + + AssertInt64Eq( + "result -1234567", + StringToI64("-1234567", .nConsumed=nConsumed), + -1234567); + AssertEq("nConsumed -1234567", nConsumed, 8); + + AssertInt64Eq( + "result -1234567654321", + StringToI64("-1234567654321", .nConsumed=nConsumed), + -1234567654321); + AssertEq("nConsumed -1234567654321", nConsumed, 14); + + AssertInt64Eq( + "result -9223372036854775807", + StringToI64("-9223372036854775807", .nConsumed=nConsumed), + -9223372036854775807); + AssertEq("nConsumed -9223372036854775807", nConsumed, 20); + + // Special nBase + AssertInt64Eq( + "result 10001111101110001111101110110101110110001", + StringToI64("10001111101110001111101110110101110110001", 2, nConsumed), + 1234567654321); + AssertEq("nConsumed 10001111101110001111101110110101110110001", nConsumed, 41); + + AssertInt64Eq( + "result 21756175665661", + StringToI64("21756175665661", 8, nConsumed), + 1234567654321); + AssertEq("nConsumed 21756175665661", nConsumed, 14); + + AssertInt64Eq( + "result 11F71F76BB1", + StringToI64("11F71F76BB1", 16, nConsumed), + 1234567654321); + AssertEq("nConsumed 11F71F76BB1", nConsumed, 11); + + // Other + AssertInt64Eq( + "result a1b2c3d4e5f6g7", + StringToI64("a1b2c3d4e5f6g7", .nConsumed=nConsumed), + 0); + AssertEq("nConsumed a1b2c3d4e5f6g7", nConsumed, 0); + + AssertInt64Eq( + "result 0b10101", + StringToI64("0b10101", .nConsumed=nConsumed), + 0); + AssertEq("nConsumed 0b10101", nConsumed, 1); + + AssertInt64Eq( + "result 1_234_567", + StringToI64("1_234_567", .nConsumed=nConsumed), + 1); + AssertEq("nConsumed 1_234_567", nConsumed, 1); + + AssertInt64Eq( + "result 9223372036854775807", + StringToI64("9223372036854775807", .nConsumed=nConsumed), + 9223372036854775807); + AssertEq("nConsumed 9223372036854775807", nConsumed, 19); + + AssertInt64Eq( + "result 9223372036854775808", + StringToI64("9223372036854775807", .nConsumed=nConsumed), + 9223372036854775807); + AssertEq("nConsumed 9223372036854775808", nConsumed, 19); + + AssertInt64Eq( + "result 92233720368547758070", + StringToI64("9223372036854775807", .nConsumed=nConsumed), + 9223372036854775807); + AssertEq("nConsumed 92233720368547758070", nConsumed, 19); + + AssertInt64Eq( + "result -9223372036854775808", + StringToI64("-9223372036854775808", .nConsumed=nConsumed), + -9223372036854775807-1); + AssertEq("nConsumed -9223372036854775808", nConsumed, 20); + + AssertInt64Eq( + "result -9223372036854775809", + StringToI64("-9223372036854775808", .nConsumed=nConsumed), + -9223372036854775807-1); + AssertEq("nConsumed -9223372036854775809", nConsumed, 20); + + AssertInt64Eq( + "result -92233720368547758080", + StringToI64("-9223372036854775808", .nConsumed=nConsumed), + -9223372036854775807-1); + AssertEq("nConsumed -92233720368547758080", nConsumed, 20); +}