From 7c4d479cd9331335322d42a4471356c4a9927718 Mon Sep 17 00:00:00 2001
From: Andrey <amborodin@acm.org>
Date: Mon, 24 Jun 2019 13:36:47 +0500
Subject: [PATCH] Use memcpy in pglz decompression

Previously pglz was using byte loop during decompression.
This commit is employing memcpy for this purpose if match length
is greater than 8 bytes.
---
 src/common/pg_lzcompress.c | 59 +++++++++++++++++++++++++++++---------
 1 file changed, 46 insertions(+), 13 deletions(-)

diff --git a/src/common/pg_lzcompress.c b/src/common/pg_lzcompress.c
index 3adad62d9a..c88a921788 100644
--- a/src/common/pg_lzcompress.c
+++ b/src/common/pg_lzcompress.c
@@ -714,11 +714,13 @@ pglz_decompress(const char *source, int32 slen, char *dest,
 			if (ctrl & 1)
 			{
 				/*
-				 * Otherwise it contains the match length minus 3 and the
-				 * upper 4 bits of the offset. The next following byte
-				 * contains the lower 8 bits of the offset. If the length is
-				 * coded as 18, another extension tag byte tells how much
-				 * longer the match really was (0-255).
+				 * Set control bit means we must read a match tag.
+				 * The match is coded with two bytes. First byte use lower
+				 * nibble to code length - 3. Higher nibble contain upper 4
+				 * bits of the offset. The next following byte contains the
+				 * lower 8 bits of the offset. If the length is coded as 18,
+				 * another extension tag byte tells how much longer the match
+				 * really was (0-255).
 				 */
 				int32		len;
 				int32		off;
@@ -731,22 +733,53 @@ pglz_decompress(const char *source, int32 slen, char *dest,
 
 				/*
 				 * Now we copy the bytes specified by the tag from OUTPUT to
-				 * OUTPUT. It is dangerous and platform dependent to use
-				 * memcpy() here, because the copied areas could overlap
-				 * extremely!
+				 * OUTPUT (copy len bytes from dp - off to dp). The copied
+				 * areas could overlap, to preven possible uncertanity, we copy
+				 * only non-overlapping regions.
 				 */
 				len = Min(len, destend - dp);
-				while (len--)
+				if (len <= 8)
 				{
-					*dp = dp[-off];
-					dp++;
+					/* use byte-loop for small matches */
+					while (len--)
+					{
+						*dp = dp[-off];
+						dp++;
+					}
+				}
+				else
+				{
+					while (off < len)
+					{
+						/*
+						 * When offset is smaller than lengh - source and
+						 * destination regions overlap. memmove() is resolving this
+						 * overlap in an incompatible way with pglz. Thus we resort
+						 * to memcpy()-ing non-overlapping regions.
+						 * Consider input: 112341234123412341234
+						 * At byte 5       here ^ we have match with length 16 and
+						 * offset 4.       11234M(len=16, off=4)
+						 * We are decoding first period of match and rewrite match
+						 *                 112341234M(len=12, off=8)
+						 * Same match is now at position 9, it aims to same start
+						 * byte of output, but from another position: offset is
+						 * doubled. We iterate through this offset growth until we
+						 * can proceed to usual memcpy().
+						 */
+						memcpy(dp, dp - off, off);
+						len -= off;
+						dp  += off;
+						off += off;
+					}
+					memcpy(dp, dp - off, len);
+					dp+=len;
 				}
 			}
 			else
 			{
 				/*
-				 * An unset control bit means LITERAL BYTE. So we just copy
-				 * one from INPUT to OUTPUT.
+				 * An unset control bit means LITERAL BYTE. So we just
+				 * copy one from INPUT to OUTPUT.
 				 */
 				*dp++ = *sp++;
 			}
-- 
2.20.1

