aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/json.c')
-rw-r--r--src/backend/utils/adt/json.c104
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, '"');
}