Skip to content

Commit ad5f987

Browse files
Sampson Gaoaddaleax
Sampson Gao
authored andcommitted
n-api: add string api for latin1 encoding
PR-URL: #12368 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jason Ginchereau <jasongin@microsoft.com>
1 parent 47c255e commit ad5f987

File tree

4 files changed

+271
-18
lines changed

4 files changed

+271
-18
lines changed

src/node_api.cc

+70-8
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,25 @@ napi_status napi_create_array_with_length(napi_env env,
12671267
return GET_RETURN_STATUS(env);
12681268
}
12691269

1270+
napi_status napi_create_string_latin1(napi_env env,
1271+
const char* str,
1272+
size_t length,
1273+
napi_value* result) {
1274+
NAPI_PREAMBLE(env);
1275+
CHECK_ARG(env, result);
1276+
1277+
auto isolate = env->isolate;
1278+
auto str_maybe =
1279+
v8::String::NewFromOneByte(isolate,
1280+
reinterpret_cast<const uint8_t*>(str),
1281+
v8::NewStringType::kInternalized,
1282+
length);
1283+
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
1284+
1285+
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
1286+
return GET_RETURN_STATUS(env);
1287+
}
1288+
12701289
napi_status napi_create_string_utf8(napi_env env,
12711290
const char* str,
12721291
size_t length,
@@ -1714,9 +1733,46 @@ napi_status napi_get_value_string_length(napi_env env,
17141733
return GET_RETURN_STATUS(env);
17151734
}
17161735

1736+
// Copies a JavaScript string into a LATIN-1 string buffer. The result is the
1737+
// number of bytes (excluding the null terminator) copied into buf.
1738+
// A sufficient buffer size should be greater than the length of string,
1739+
// reserving space for null terminator.
1740+
// If bufsize is insufficient, the string will be truncated and null terminated.
1741+
// If buf is NULL, this method returns the length of the string (in bytes)
1742+
// via the result parameter.
1743+
// The result argument is optional unless buf is NULL.
1744+
napi_status napi_get_value_string_latin1(napi_env env,
1745+
napi_value value,
1746+
char* buf,
1747+
size_t bufsize,
1748+
size_t* result) {
1749+
NAPI_PREAMBLE(env);
1750+
1751+
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
1752+
RETURN_STATUS_IF_FALSE(env, val->IsString(), napi_string_expected);
1753+
1754+
if (!buf) {
1755+
CHECK_ARG(env, result);
1756+
*result = val.As<v8::String>()->Length();
1757+
} else {
1758+
int copied = val.As<v8::String>()->WriteOneByte(
1759+
reinterpret_cast<uint8_t*>(buf), 0, bufsize - 1,
1760+
v8::String::NO_NULL_TERMINATION);
1761+
1762+
buf[copied] = '\0';
1763+
if (result != nullptr) {
1764+
*result = copied;
1765+
}
1766+
}
1767+
1768+
return GET_RETURN_STATUS(env);
1769+
}
1770+
17171771
// Copies a JavaScript string into a UTF-8 string buffer. The result is the
1718-
// number of bytes copied into buf, including the null terminator. If bufsize
1719-
// is insufficient, the string will be truncated, including a null terminator.
1772+
// number of bytes (excluding the null terminator) copied into buf.
1773+
// A sufficient buffer size should be greater than the length of string,
1774+
// reserving space for null terminator.
1775+
// If bufsize is insufficient, the string will be truncated and null terminated.
17201776
// If buf is NULL, this method returns the length of the string (in bytes)
17211777
// via the result parameter.
17221778
// The result argument is optional unless buf is NULL.
@@ -1735,8 +1791,10 @@ napi_status napi_get_value_string_utf8(napi_env env,
17351791
*result = val.As<v8::String>()->Utf8Length();
17361792
} else {
17371793
int copied = val.As<v8::String>()->WriteUtf8(
1738-
buf, bufsize, nullptr, v8::String::REPLACE_INVALID_UTF8);
1794+
buf, bufsize - 1, nullptr, v8::String::REPLACE_INVALID_UTF8 |
1795+
v8::String::NO_NULL_TERMINATION);
17391796

1797+
buf[copied] = '\0';
17401798
if (result != nullptr) {
17411799
*result = copied;
17421800
}
@@ -1746,10 +1804,12 @@ napi_status napi_get_value_string_utf8(napi_env env,
17461804
}
17471805

17481806
// Copies a JavaScript string into a UTF-16 string buffer. The result is the
1749-
// number of 2-byte code units copied into buf, including the null terminator.
1750-
// If bufsize is insufficient, the string will be truncated, including a null
1751-
// terminator. If buf is NULL, this method returns the length of the string
1752-
// (in 2-byte code units) via the result parameter.
1807+
// number of 2-byte code units (excluding the null terminator) copied into buf.
1808+
// A sufficient buffer size should be greater than the length of string,
1809+
// reserving space for null terminator.
1810+
// If bufsize is insufficient, the string will be truncated and null terminated.
1811+
// If buf is NULL, this method returns the length of the string (in 2-byte
1812+
// code units) via the result parameter.
17531813
// The result argument is optional unless buf is NULL.
17541814
napi_status napi_get_value_string_utf16(napi_env env,
17551815
napi_value value,
@@ -1767,8 +1827,10 @@ napi_status napi_get_value_string_utf16(napi_env env,
17671827
*result = val.As<v8::String>()->Length();
17681828
} else {
17691829
int copied = val.As<v8::String>()->Write(
1770-
reinterpret_cast<uint16_t*>(buf), 0, bufsize, v8::String::NO_OPTIONS);
1830+
reinterpret_cast<uint16_t*>(buf), 0, bufsize - 1,
1831+
v8::String::NO_NULL_TERMINATION);
17711832

1833+
buf[copied] = '\0';
17721834
if (result != nullptr) {
17731835
*result = copied;
17741836
}

src/node_api.h

+11
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ NAPI_EXTERN napi_status napi_create_array_with_length(napi_env env,
121121
NAPI_EXTERN napi_status napi_create_number(napi_env env,
122122
double value,
123123
napi_value* result);
124+
NAPI_EXTERN napi_status napi_create_string_latin1(napi_env env,
125+
const char* str,
126+
size_t length,
127+
napi_value* result);
124128
NAPI_EXTERN napi_status napi_create_string_utf8(napi_env env,
125129
const char* str,
126130
size_t length,
@@ -172,6 +176,13 @@ NAPI_EXTERN napi_status napi_get_value_string_length(napi_env env,
172176
napi_value value,
173177
size_t* result);
174178

179+
// Copies LATIN-1 encoded bytes from a string into a buffer.
180+
NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env,
181+
napi_value value,
182+
char* buf,
183+
size_t bufsize,
184+
size_t* result);
185+
175186
// Copies UTF-8 encoded bytes from a string into a buffer.
176187
NAPI_EXTERN napi_status napi_get_value_string_utf8(napi_env env,
177188
napi_value value,

test/addons-napi/test_string/test.js

+52-7
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,67 @@ const assert = require('assert');
55
// testing api calls for string
66
const test_string = require(`./build/${common.buildType}/test_string`);
77

8+
const empty = '';
9+
assert.strictEqual(test_string.TestLatin1(empty), empty);
10+
assert.strictEqual(test_string.TestUtf8(empty), empty);
11+
assert.strictEqual(test_string.TestUtf16(empty), empty);
12+
assert.strictEqual(test_string.Length(empty), 0);
13+
assert.strictEqual(test_string.Utf8Length(empty), 0);
14+
815
const str1 = 'hello world';
9-
assert.strictEqual(test_string.Copy(str1), str1);
16+
assert.strictEqual(test_string.TestLatin1(str1), str1);
17+
assert.strictEqual(test_string.TestUtf8(str1), str1);
18+
assert.strictEqual(test_string.TestUtf16(str1), str1);
19+
assert.strictEqual(test_string.TestLatin1Insufficient(str1), str1.slice(0, 3));
20+
assert.strictEqual(test_string.TestUtf8Insufficient(str1), str1.slice(0, 3));
21+
assert.strictEqual(test_string.TestUtf16Insufficient(str1), str1.slice(0, 3));
1022
assert.strictEqual(test_string.Length(str1), 11);
1123
assert.strictEqual(test_string.Utf8Length(str1), 11);
1224

1325
const str2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
14-
assert.strictEqual(test_string.Copy(str2), str2);
26+
assert.strictEqual(test_string.TestLatin1(str2), str2);
27+
assert.strictEqual(test_string.TestUtf8(str2), str2);
28+
assert.strictEqual(test_string.TestUtf16(str2), str2);
29+
assert.strictEqual(test_string.TestLatin1Insufficient(str2), str2.slice(0, 3));
30+
assert.strictEqual(test_string.TestUtf8Insufficient(str2), str2.slice(0, 3));
31+
assert.strictEqual(test_string.TestUtf16Insufficient(str2), str2.slice(0, 3));
1532
assert.strictEqual(test_string.Length(str2), 62);
1633
assert.strictEqual(test_string.Utf8Length(str2), 62);
1734

1835
const str3 = '?!@#$%^&*()_+-=[]{}/.,<>\'"\\';
19-
assert.strictEqual(test_string.Copy(str3), str3);
36+
assert.strictEqual(test_string.TestLatin1(str3), str3);
37+
assert.strictEqual(test_string.TestUtf8(str3), str3);
38+
assert.strictEqual(test_string.TestUtf16(str3), str3);
39+
assert.strictEqual(test_string.TestLatin1Insufficient(str3), str3.slice(0, 3));
40+
assert.strictEqual(test_string.TestUtf8Insufficient(str3), str3.slice(0, 3));
41+
assert.strictEqual(test_string.TestUtf16Insufficient(str3), str3.slice(0, 3));
2042
assert.strictEqual(test_string.Length(str3), 27);
2143
assert.strictEqual(test_string.Utf8Length(str3), 27);
2244

23-
const str4 = '\u{2003}\u{2101}\u{2001}';
24-
assert.strictEqual(test_string.Copy(str4), str4);
25-
assert.strictEqual(test_string.Length(str4), 3);
26-
assert.strictEqual(test_string.Utf8Length(str4), 9);
45+
const str4 = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿';
46+
assert.strictEqual(test_string.TestLatin1(str4), str4);
47+
assert.strictEqual(test_string.TestUtf8(str4), str4);
48+
assert.strictEqual(test_string.TestUtf16(str4), str4);
49+
assert.strictEqual(test_string.TestLatin1Insufficient(str4), str4.slice(0, 3));
50+
assert.strictEqual(test_string.TestUtf8Insufficient(str4), str4.slice(0, 1));
51+
assert.strictEqual(test_string.TestUtf16Insufficient(str4), str4.slice(0, 3));
52+
assert.strictEqual(test_string.Length(str4), 31);
53+
assert.strictEqual(test_string.Utf8Length(str4), 62);
54+
55+
const str5 = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ';
56+
assert.strictEqual(test_string.TestLatin1(str5), str5);
57+
assert.strictEqual(test_string.TestUtf8(str5), str5);
58+
assert.strictEqual(test_string.TestUtf16(str5), str5);
59+
assert.strictEqual(test_string.TestLatin1Insufficient(str5), str5.slice(0, 3));
60+
assert.strictEqual(test_string.TestUtf8Insufficient(str5), str5.slice(0, 1));
61+
assert.strictEqual(test_string.TestUtf16Insufficient(str5), str5.slice(0, 3));
62+
assert.strictEqual(test_string.Length(str5), 63);
63+
assert.strictEqual(test_string.Utf8Length(str5), 126);
64+
65+
const str6 = '\u{2003}\u{2101}\u{2001}\u{202}\u{2011}';
66+
assert.strictEqual(test_string.TestUtf8(str6), str6);
67+
assert.strictEqual(test_string.TestUtf16(str6), str6);
68+
assert.strictEqual(test_string.TestUtf8Insufficient(str6), str6.slice(0, 1));
69+
assert.strictEqual(test_string.TestUtf16Insufficient(str6), str6.slice(0, 3));
70+
assert.strictEqual(test_string.Length(str6), 5);
71+
assert.strictEqual(test_string.Utf8Length(str6), 14);

test/addons-napi/test_string/test_string.c

+138-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
11
#include <node_api.h>
22
#include "../common.h"
33

4-
napi_value Copy(napi_env env, napi_callback_info info) {
4+
napi_value TestLatin1(napi_env env, napi_callback_info info) {
5+
size_t argc = 1;
6+
napi_value args[1];
7+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
8+
9+
NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
10+
11+
napi_valuetype valuetype;
12+
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype));
13+
14+
NAPI_ASSERT(env, valuetype == napi_string,
15+
"Wrong type of argment. Expects a string.");
16+
17+
char buffer[128];
18+
size_t buffer_size = 128;
19+
size_t copied;
20+
21+
NAPI_CALL(env,
22+
napi_get_value_string_latin1(env, args[0], buffer, buffer_size, &copied));
23+
24+
napi_value output;
25+
NAPI_CALL(env, napi_create_string_latin1(env, buffer, copied, &output));
26+
27+
return output;
28+
}
29+
30+
napi_value TestUtf8(napi_env env, napi_callback_info info) {
531
size_t argc = 1;
632
napi_value args[1];
733
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
@@ -22,7 +48,111 @@ napi_value Copy(napi_env env, napi_callback_info info) {
2248
napi_get_value_string_utf8(env, args[0], buffer, buffer_size, &copied));
2349

2450
napi_value output;
25-
NAPI_CALL(env, napi_create_string_utf8(env, buffer, copied-1, &output));
51+
NAPI_CALL(env, napi_create_string_utf8(env, buffer, copied, &output));
52+
53+
return output;
54+
}
55+
56+
napi_value TestUtf16(napi_env env, napi_callback_info info) {
57+
size_t argc = 1;
58+
napi_value args[1];
59+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
60+
61+
NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
62+
63+
napi_valuetype valuetype;
64+
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype));
65+
66+
NAPI_ASSERT(env, valuetype == napi_string,
67+
"Wrong type of argment. Expects a string.");
68+
69+
char16_t buffer[128];
70+
size_t buffer_size = 128;
71+
size_t copied;
72+
73+
NAPI_CALL(env,
74+
napi_get_value_string_utf16(env, args[0], buffer, buffer_size, &copied));
75+
76+
napi_value output;
77+
NAPI_CALL(env, napi_create_string_utf16(env, buffer, copied, &output));
78+
79+
return output;
80+
}
81+
82+
napi_value TestLatin1Insufficient(napi_env env, napi_callback_info info) {
83+
size_t argc = 1;
84+
napi_value args[1];
85+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
86+
87+
NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
88+
89+
napi_valuetype valuetype;
90+
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype));
91+
92+
NAPI_ASSERT(env, valuetype == napi_string,
93+
"Wrong type of argment. Expects a string.");
94+
95+
char buffer[4];
96+
size_t buffer_size = 4;
97+
size_t copied;
98+
99+
NAPI_CALL(env,
100+
napi_get_value_string_latin1(env, args[0], buffer, buffer_size, &copied));
101+
102+
napi_value output;
103+
NAPI_CALL(env, napi_create_string_latin1(env, buffer, copied, &output));
104+
105+
return output;
106+
}
107+
108+
napi_value TestUtf8Insufficient(napi_env env, napi_callback_info info) {
109+
size_t argc = 1;
110+
napi_value args[1];
111+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
112+
113+
NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
114+
115+
napi_valuetype valuetype;
116+
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype));
117+
118+
NAPI_ASSERT(env, valuetype == napi_string,
119+
"Wrong type of argment. Expects a string.");
120+
121+
char buffer[4];
122+
size_t buffer_size = 4;
123+
size_t copied;
124+
125+
NAPI_CALL(env,
126+
napi_get_value_string_utf8(env, args[0], buffer, buffer_size, &copied));
127+
128+
napi_value output;
129+
NAPI_CALL(env, napi_create_string_utf8(env, buffer, copied, &output));
130+
131+
return output;
132+
}
133+
134+
napi_value TestUtf16Insufficient(napi_env env, napi_callback_info info) {
135+
size_t argc = 1;
136+
napi_value args[1];
137+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
138+
139+
NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
140+
141+
napi_valuetype valuetype;
142+
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype));
143+
144+
NAPI_ASSERT(env, valuetype == napi_string,
145+
"Wrong type of argment. Expects a string.");
146+
147+
char16_t buffer[4];
148+
size_t buffer_size = 4;
149+
size_t copied;
150+
151+
NAPI_CALL(env,
152+
napi_get_value_string_utf16(env, args[0], buffer, buffer_size, &copied));
153+
154+
napi_value output;
155+
NAPI_CALL(env, napi_create_string_utf16(env, buffer, copied, &output));
26156

27157
return output;
28158
}
@@ -73,7 +203,12 @@ napi_value Utf8Length(napi_env env, napi_callback_info info) {
73203

74204
void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
75205
napi_property_descriptor properties[] = {
76-
DECLARE_NAPI_PROPERTY("Copy", Copy),
206+
DECLARE_NAPI_PROPERTY("TestLatin1", TestLatin1),
207+
DECLARE_NAPI_PROPERTY("TestLatin1Insufficient", TestLatin1Insufficient),
208+
DECLARE_NAPI_PROPERTY("TestUtf8", TestUtf8),
209+
DECLARE_NAPI_PROPERTY("TestUtf8Insufficient", TestUtf8Insufficient),
210+
DECLARE_NAPI_PROPERTY("TestUtf16", TestUtf16),
211+
DECLARE_NAPI_PROPERTY("TestUtf16Insufficient", TestUtf16Insufficient),
77212
DECLARE_NAPI_PROPERTY("Length", Length),
78213
DECLARE_NAPI_PROPERTY("Utf8Length", Utf8Length),
79214
};

0 commit comments

Comments
 (0)