mirror of
https://github.com/jqlang/jq.git
synced 2026-10-11 21:49:50 +00:00
Improve performance of string output (#3643)
Printing a string wrote each character with a separate call, one fwrite() per character, or one jv_string_append_buf() for tojson and tostring, which made output of string-heavy data slow. Scan ahead for the next byte that needs escaping and write the run of bytes before it with a single call, without changing the output. Printing strings is now significantly faster, most of all for long strings with few characters to escape, and the same applies to tojson and tostring. With --ascii-output, text that is almost all non-ASCII is about the same speed, since nearly every byte is escaped anyway.
This commit is contained in:
+12
-1
@@ -126,7 +126,18 @@ static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S, int T) {
|
||||
int c = 0;
|
||||
char buf[32];
|
||||
put_char('"', F, S, T);
|
||||
while ((i = jvp_utf8_next((cstart = i), end, &c))) {
|
||||
while (i < end) {
|
||||
// Write each run of bytes that need no escaping with a single call.
|
||||
for (cstart = i; cstart < end; cstart++) {
|
||||
unsigned char b = *cstart;
|
||||
if (b < 0x20 || b == '"' || b == '\\' || b == 0x7F || (ascii_only && b >= 0x80))
|
||||
break;
|
||||
}
|
||||
if (cstart > i)
|
||||
put_buf(i, cstart - i, F, S, T);
|
||||
if (cstart == end)
|
||||
break;
|
||||
i = jvp_utf8_next(cstart, end, &c);
|
||||
assert(c != -1);
|
||||
int unicode_escape = 0;
|
||||
if (0x20 <= c && c <= 0x7E) {
|
||||
|
||||
@@ -100,6 +100,15 @@ null
|
||||
["foo", 1, ["a", 1, "b", 2, {"foo":"bar"}]]
|
||||
["foo",1,["a",1,"b",2,{"foo":"bar"}]]
|
||||
|
||||
# Put escapes on both sides of runs of bytes that need no escaping
|
||||
map(tojson)
|
||||
["abcdefg\"", "abcdefgh\\", "abcdefghi\u0001j", "abcdefghijklmno\u007f", "abcdefgh\té", "abcdefghijklmnop\n"]
|
||||
["\"abcdefg\\\"\"","\"abcdefgh\\\\\"","\"abcdefghi\\u0001j\"","\"abcdefghijklmno\\u007f\"","\"abcdefgh\\té\"","\"abcdefghijklmnop\\n\""]
|
||||
|
||||
[range(18) as $i | ("\"", "\\", "\u0001", "\u007f", "é", "😀") as $c | "abcdefghijklmnopq"[:$i] + $c + "abcdefghijklmnopq"[$i:] | (tojson | fromjson) == .] | all
|
||||
null
|
||||
true
|
||||
|
||||
#
|
||||
# Dictionary construction syntax
|
||||
#
|
||||
|
||||
@@ -21,4 +21,18 @@ for l in 1 2 3 4; do
|
||||
fi
|
||||
done
|
||||
|
||||
# --ascii-output escapes non-ASCII characters on both sides of runs of
|
||||
# bytes that need no escaping
|
||||
$VALGRIND $Q $JQ -cn '[range(18) as $i | ("é", "😀", "\u007f") as $c |
|
||||
"abcdefghijklmnopq"[:$i] + $c + "abcdefghijklmnopq"[$i:]]' > $d/input
|
||||
$VALGRIND $Q $JQ -ac . $d/input > $d/out
|
||||
if LC_ALL=C grep -q '[^ -~]' $d/out; then
|
||||
echo "--ascii-output printed a non-ASCII byte"
|
||||
exit 1
|
||||
fi
|
||||
if ! $VALGRIND $Q $JQ -e --slurpfile a $d/input '. == $a[0]' $d/out > /dev/null; then
|
||||
echo "--ascii-output changed a string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user