0
0
mirror of https://github.com/vim/vim.git synced 2025-09-26 04:04:07 -04:00

patch 8.2.4695: JSON encoding could be faster

Problem:    JSON encoding could be faster.
Solution:   Optimize encoding JSON strings. (closes #10086)
This commit is contained in:
LemonBoy
2022-04-05 15:07:32 +01:00
committed by Bram Moolenaar
parent 02560424bf
commit beb0ef1ab2
3 changed files with 94 additions and 36 deletions

View File

@@ -114,23 +114,45 @@ json_encode_lsp_msg(typval_T *val)
} }
#endif #endif
/*
* Lookup table to quickly know if the given ASCII character must be escaped.
*/
static const char ascii_needs_escape[128] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x0.
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x1.
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x2.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 0x5.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x6.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
};
/*
* Encode the utf-8 encoded string "str" into "gap".
*/
static void static void
write_string(garray_T *gap, char_u *str) write_string(garray_T *gap, char_u *str)
{ {
char_u *res = str; char_u *res = str;
char_u numbuf[NUMBUFLEN]; char_u numbuf[NUMBUFLEN];
char_u *from;
if (res == NULL)
ga_concat(gap, (char_u *)"\"\"");
else
{
#if defined(USE_ICONV) #if defined(USE_ICONV)
vimconv_T conv; vimconv_T conv;
char_u *converted = NULL; char_u *converted = NULL;
#endif
int c;
if (res == NULL)
{
ga_concat(gap, (char_u *)"\"\"");
return;
}
#if defined(USE_ICONV)
if (!enc_utf8) if (!enc_utf8)
{ {
// Convert the text from 'encoding' to utf-8, the JSON string is // Convert the text from 'encoding' to utf-8, because a JSON string is
// always utf-8. // always utf-8.
conv.vc_type = CONV_NONE; conv.vc_type = CONV_NONE;
convert_setup(&conv, p_enc, (char_u*)"utf-8"); convert_setup(&conv, p_enc, (char_u*)"utf-8");
@@ -140,11 +162,24 @@ write_string(garray_T *gap, char_u *str)
} }
#endif #endif
ga_append(gap, '"'); ga_append(gap, '"');
while (*res != NUL) // `from` is the beginning of a sequence of bytes we can directly copy from
// the input string, avoiding the overhead associated to decoding/encoding
// them.
from = res;
while ((c = *res) != NUL)
{ {
int c;
// always use utf-8 encoding, ignore 'encoding' // always use utf-8 encoding, ignore 'encoding'
c = utf_ptr2char(res); if (c < 0x80)
{
if (!ascii_needs_escape[c])
{
res += 1;
continue;
}
if (res != from)
ga_concat_len(gap, from, res - from);
from = res + 1;
switch (c) switch (c)
{ {
@@ -164,26 +199,44 @@ write_string(garray_T *gap, char_u *str)
ga_append(gap, c); ga_append(gap, c);
break; break;
default: default:
if (c >= 0x20) vim_snprintf((char *)numbuf, NUMBUFLEN, "\\u%04lx",
{ (long)c);
numbuf[utf_char2bytes(c, numbuf)] = NUL;
ga_concat(gap, numbuf); ga_concat(gap, numbuf);
} }
res += 1;
}
else else
{ {
vim_snprintf((char *)numbuf, NUMBUFLEN, int l = utf_ptr2len(res);
"\\u%04lx", (long)c);
if (l > 1)
{
res += l;
continue;
}
// Invalid utf-8 sequence, replace it with the Unicode replacement
// character U+FFFD.
if (res != from)
ga_concat_len(gap, from, res - from);
from = res + 1;
numbuf[utf_char2bytes(0xFFFD, numbuf)] = NUL;
ga_concat(gap, numbuf); ga_concat(gap, numbuf);
res += l;
} }
} }
res += utf_ptr2len(res);
} if (res != from)
ga_concat_len(gap, from, res - from);
ga_append(gap, '"'); ga_append(gap, '"');
#if defined(USE_ICONV) #if defined(USE_ICONV)
vim_free(converted); vim_free(converted);
#endif #endif
} }
}
/* /*
* Return TRUE if "key" can be used without quotes. * Return TRUE if "key" can be used without quotes.

View File

@@ -107,6 +107,9 @@ func Test_json_encode()
call assert_equal('"café"', json_encode("caf\xe9")) call assert_equal('"café"', json_encode("caf\xe9"))
let &encoding = save_encoding let &encoding = save_encoding
" Invalid utf-8 sequences are replaced with U+FFFD (replacement character)
call assert_equal('"foo' . "\ufffd" . '"', json_encode("foo\xAB"))
call assert_fails('echo json_encode(function("tr"))', 'E1161: Cannot json encode a func') call assert_fails('echo json_encode(function("tr"))', 'E1161: Cannot json encode a func')
call assert_fails('echo json_encode([function("tr")])', 'E1161: Cannot json encode a func') call assert_fails('echo json_encode([function("tr")])', 'E1161: Cannot json encode a func')

View File

@@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
4695,
/**/ /**/
4694, 4694,
/**/ /**/