diff options
Diffstat (limited to 'src/backend/utils/adt/json.c')
-rw-r--r-- | src/backend/utils/adt/json.c | 104 |
1 files changed, 102 insertions, 2 deletions
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index be7bc46038f..4eeeeaf0a60 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -19,6 +19,7 @@ #include "funcapi.h" #include "libpq/pqformat.h" #include "miscadmin.h" +#include "port/simd.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/date.h" @@ -1595,6 +1596,18 @@ escape_json(StringInfo buf, const char *str) } /* + * Define the number of bytes that escape_json_with_len will look ahead in the + * input string before flushing the input string to the destination buffer. + * Looking ahead too far could result in cachelines being evicted that will + * need to be reloaded in order to perform the appendBinaryStringInfo call. + * Smaller values will result in a larger number of calls to + * appendBinaryStringInfo and introduce additional function call overhead. + * Values larger than the size of L1d cache will likely result in worse + * performance. + */ +#define ESCAPE_JSON_FLUSH_AFTER 512 + +/* * escape_json_with_len * Produce a JSON string literal, properly escaping the possibly not * NUL-terminated characters in 'str'. 'len' defines the number of bytes @@ -1603,11 +1616,98 @@ escape_json(StringInfo buf, const char *str) void escape_json_with_len(StringInfo buf, const char *str, int len) { + int vlen; + + Assert(len >= 0); + + /* + * Since we know the minimum length we'll need to append, let's just + * enlarge the buffer now rather than incrementally making more space when + * we run out. Add two extra bytes for the enclosing quotes. + */ + enlargeStringInfo(buf, len + 2); + + /* + * Figure out how many bytes to process using SIMD. Round 'len' down to + * the previous multiple of sizeof(Vector8), assuming that's a power-of-2. + */ + vlen = len & (int) (~(sizeof(Vector8) - 1)); + appendStringInfoCharMacro(buf, '"'); - for (int i = 0; i < len; i++) - escape_json_char(buf, str[i]); + for (int i = 0, copypos = 0;;) + { + /* + * To speed this up, try searching sizeof(Vector8) bytes at once for + * special characters that we need to escape. When we find one, we + * fall out of the Vector8 loop and copy the portion we've vector + * searched and then we process sizeof(Vector8) bytes one byte at a + * time. Once done, come back and try doing vector searching again. + * We'll also process any remaining bytes at the tail end of the + * string byte-by-byte. This optimization assumes that most chunks of + * sizeof(Vector8) bytes won't contain any special characters. + */ + for (; i < vlen; i += sizeof(Vector8)) + { + Vector8 chunk; + + vector8_load(&chunk, (const uint8 *) &str[i]); + + /* + * Break on anything less than ' ' or if we find a '"' or '\\'. + * Those need special handling. That's done in the per-byte loop. + */ + if (vector8_has_le(chunk, (unsigned char) 0x1F) || + vector8_has(chunk, (unsigned char) '"') || + vector8_has(chunk, (unsigned char) '\\')) + break; + +#ifdef ESCAPE_JSON_FLUSH_AFTER + + /* + * Flush what's been checked so far out to the destination buffer + * every so often to avoid having to re-read cachelines when + * escaping large strings. + */ + if (i - copypos >= ESCAPE_JSON_FLUSH_AFTER) + { + appendBinaryStringInfo(buf, &str[copypos], i - copypos); + copypos = i; + } +#endif + } + + /* + * Write to the destination up to the point that we've vector searched + * so far. Do this only when switching into per-byte mode rather than + * once every sizeof(Vector8) bytes. + */ + if (copypos < i) + { + appendBinaryStringInfo(buf, &str[copypos], i - copypos); + copypos = i; + } + + /* + * Per-byte loop for Vector8s containing special chars and for + * processing the tail of the string. + */ + for (int b = 0; b < sizeof(Vector8); b++) + { + /* check if we've finished */ + if (i == len) + goto done; + + Assert(i < len); + + escape_json_char(buf, str[i++]); + } + + copypos = i; + /* We're not done yet. Try the vector search again. */ + } +done: appendStringInfoCharMacro(buf, '"'); } |