Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Jsrt function JsCopyStringOneByte #3408

Merged
Merged
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
28 changes: 28 additions & 0 deletions bin/NativeTests/JsRTApiTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2143,4 +2143,32 @@ namespace JsRTApiTest
JsRTApiTest::RunWithAttributes(JsRTApiTest::ObjectHasOwnPropertyMethodTest);
}

void JsCopyStringOneByteMethodTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime)
{
size_t written = 0;
char buf[10] = {0};
JsValueRef value;
REQUIRE(JsCreateStringUtf16(reinterpret_cast<uint16_t*>(_u("0\x10\x80\xa9\uabcd\U000104377")), 8, &value) == JsNoError);
REQUIRE(JsCopyStringOneByte(value, 0, -1, nullptr, &written) == JsNoError);
CHECK(written == 8);
buf[written] = '\xff';

REQUIRE(JsCopyStringOneByte(value, 0, 10, buf, &written) == JsNoError);
CHECK(written == 8);
CHECK(buf[0] == '0');
CHECK(buf[1] == '\x10');
CHECK(buf[2] == '\x80');
CHECK(buf[3] == '\xA9');
CHECK(buf[4] == '\xcd');
CHECK(buf[5] == '\x01');
CHECK(buf[6] == '\x37');
CHECK(buf[7] == '7');
CHECK(buf[8] == '\xff');
}

TEST_CASE("ApiTest_JsCopyStringOneByteMethodTest", "[ApiTest]")
{
JsRTApiTest::RunWithAttributes(JsRTApiTest::JsCopyStringOneByteMethodTest);
}

}
38 changes: 38 additions & 0 deletions lib/Jsrt/ChakraCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -679,5 +679,43 @@ CHAKRA_API
_In_ JsPropertyIdRef propertyId,
_Out_ bool *hasOwnProperty);

/// <summary>
/// Write JS string value into char string buffer without a null terminator
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add some more details about the semantics of this API, specifically what happens if the codepoint being copied is not within the char range

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes, that's great. Also clarify that UTF16 encoded codepoints are being truncated in this case. Actually, what happens in the case of a surrogate pair?

/// </summary>
/// <remarks>
/// <para>
/// When size of the `buffer` is unknown,
/// `buffer` argument can be nullptr.
/// In that case, `written` argument will return the length needed.
/// </para>
/// <para>
/// When start is out of range or &lt; 0, returns JsErrorInvalidArgument
/// and `written` will be equal to 0.
/// If calculated length is 0 (It can be due to string length or `start`
/// and length combination), then `written` will be equal to 0 and call
/// returns JsNoError
/// </para>
/// <para>
/// The JS string `value` will be converted one utf16 code point at a time,
/// and if it has code points that do not fit in one byte, those values
/// will be truncated.
/// </para>
/// </remarks>
/// <param name="value">JavascriptString value</param>
/// <param name="start">Start offset of buffer</param>
/// <param name="length">Number of characters to be written</param>
/// <param name="buffer">Pointer to buffer</param>
/// <param name="written">Total number of characters written</param>
/// <returns>
/// The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
/// </returns>
CHAKRA_API
JsCopyStringOneByte(
_In_ JsValueRef value,
_In_ int start,
_In_ int length,
_Out_opt_ char* buffer,
_Out_opt_ size_t* written);

#endif // CHAKRACOREBUILD_
#endif // _CHAKRACORE_H_
40 changes: 30 additions & 10 deletions lib/Jsrt/Jsrt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4181,14 +4181,15 @@ JsErrorCode WriteStringCopy(
*written = 0; // init to 0 for default
}

const char16* str = nullptr;
size_t strLength = 0;
JsErrorCode errorCode = JsStringToPointer(value, &str, &strLength);
if (errorCode != JsNoError)
if (!Js::JavascriptString::Is(value))
{
return errorCode;
return JsErrorInvalidArgument;
}

Js::JavascriptString *jsString = Js::JavascriptString::FromVar(value);
const char16* str = jsString->GetSz();
size_t strLength = jsString->GetLength();

if (start < 0 || (size_t)start > strLength)
{
return JsErrorInvalidArgument; // start out of range, no chars written
Expand All @@ -4200,7 +4201,7 @@ JsErrorCode WriteStringCopy(
return JsNoError; // no chars written
}

errorCode = copyFunc(str + start, count, written);
JsErrorCode errorCode = copyFunc(str + start, count, written);
if (errorCode != JsNoError)
{
return errorCode;
Expand Down Expand Up @@ -4231,10 +4232,6 @@ CHAKRA_API JsCopyStringUtf16(
{
memmove(buffer, src, sizeof(char16) * count);
}
else
{
*needed = count;
}
return JsNoError;
});
}
Expand Down Expand Up @@ -4746,4 +4743,27 @@ CHAKRA_API JsHasOwnProperty(_In_ JsValueRef object, _In_ JsPropertyIdRef propert
});
}

CHAKRA_API JsCopyStringOneByte(
_In_ JsValueRef value,
_In_ int start,
_In_ int length,
_Out_opt_ char* buffer,
_Out_opt_ size_t* written)
{
PARAM_NOT_NULL(value);
VALIDATE_JSREF(value);
return WriteStringCopy(value, start, length, written,
[buffer](const char16* src, size_t count, size_t *needed)
{
if (buffer)
{
for (size_t i = 0; i < count; i++)
{
buffer[i] = (char)src[i];
}
}
return JsNoError;
});
}

#endif // CHAKRACOREBUILD_
1 change: 1 addition & 0 deletions lib/Jsrt/JsrtCommonExports.inc
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,5 @@
JsGetWeakReferenceValue
JsGetAndClearExceptionWithMetadata
JsHasOwnProperty
JsCopyStringOneByte
#endif