From a2961406c48bc14ce7341c8d943e7546864e846a Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Mon, 26 Sep 2016 15:30:24 +0300
Subject: [PATCH 2/8] Move encoding routines to src/common/

The following encoding routines are moved for decode and encode:
- escape
- base64
- hex
base64 and hex are to be used by the new SCRAM-SHA-256 mechanism, moving
'escape' too for consistency.
---
 src/backend/utils/adt/encode.c    | 408 +--------------------------------
 src/backend/utils/adt/varlena.c   |   1 +
 src/common/Makefile               |   6 +-
 src/common/encode_utils.c         | 470 ++++++++++++++++++++++++++++++++++++++
 src/include/common/encode_utils.h |  30 +++
 src/include/utils/builtins.h      |   2 -
 src/tools/msvc/Mkvcbuild.pm       |   2 +-
 7 files changed, 506 insertions(+), 413 deletions(-)
 create mode 100644 src/common/encode_utils.c
 create mode 100644 src/include/common/encode_utils.h

diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index d833efc..306dbdd 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -15,6 +15,7 @@
 
 #include <ctype.h>
 
+#include "common/encode_utils.h"
 #include "utils/builtins.h"
 
 
@@ -106,413 +107,6 @@ binary_decode(PG_FUNCTION_ARGS)
 
 
 /*
- * HEX
- */
-
-static const char hextbl[] = "0123456789abcdef";
-
-static const int8 hexlookup[128] = {
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-};
-
-unsigned
-hex_encode(const char *src, unsigned len, char *dst)
-{
-	const char *end = src + len;
-
-	while (src < end)
-	{
-		*dst++ = hextbl[(*src >> 4) & 0xF];
-		*dst++ = hextbl[*src & 0xF];
-		src++;
-	}
-	return len * 2;
-}
-
-static inline char
-get_hex(char c)
-{
-	int			res = -1;
-
-	if (c > 0 && c < 127)
-		res = hexlookup[(unsigned char) c];
-
-	if (res < 0)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("invalid hexadecimal digit: \"%c\"", c)));
-
-	return (char) res;
-}
-
-unsigned
-hex_decode(const char *src, unsigned len, char *dst)
-{
-	const char *s,
-			   *srcend;
-	char		v1,
-				v2,
-			   *p;
-
-	srcend = src + len;
-	s = src;
-	p = dst;
-	while (s < srcend)
-	{
-		if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
-		{
-			s++;
-			continue;
-		}
-		v1 = get_hex(*s++) << 4;
-		if (s >= srcend)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				  errmsg("invalid hexadecimal data: odd number of digits")));
-
-		v2 = get_hex(*s++);
-		*p++ = v1 | v2;
-	}
-
-	return p - dst;
-}
-
-static unsigned
-hex_enc_len(const char *src, unsigned srclen)
-{
-	return srclen << 1;
-}
-
-static unsigned
-hex_dec_len(const char *src, unsigned srclen)
-{
-	return srclen >> 1;
-}
-
-/*
- * BASE64
- */
-
-static const char _base64[] =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static const int8 b64lookup[128] = {
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
-	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
-	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-	-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-};
-
-static unsigned
-b64_encode(const char *src, unsigned len, char *dst)
-{
-	char	   *p,
-			   *lend = dst + 76;
-	const char *s,
-			   *end = src + len;
-	int			pos = 2;
-	uint32		buf = 0;
-
-	s = src;
-	p = dst;
-
-	while (s < end)
-	{
-		buf |= (unsigned char) *s << (pos << 3);
-		pos--;
-		s++;
-
-		/* write it out */
-		if (pos < 0)
-		{
-			*p++ = _base64[(buf >> 18) & 0x3f];
-			*p++ = _base64[(buf >> 12) & 0x3f];
-			*p++ = _base64[(buf >> 6) & 0x3f];
-			*p++ = _base64[buf & 0x3f];
-
-			pos = 2;
-			buf = 0;
-		}
-		if (p >= lend)
-		{
-			*p++ = '\n';
-			lend = p + 76;
-		}
-	}
-	if (pos != 2)
-	{
-		*p++ = _base64[(buf >> 18) & 0x3f];
-		*p++ = _base64[(buf >> 12) & 0x3f];
-		*p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
-		*p++ = '=';
-	}
-
-	return p - dst;
-}
-
-static unsigned
-b64_decode(const char *src, unsigned len, char *dst)
-{
-	const char *srcend = src + len,
-			   *s = src;
-	char	   *p = dst;
-	char		c;
-	int			b = 0;
-	uint32		buf = 0;
-	int			pos = 0,
-				end = 0;
-
-	while (s < srcend)
-	{
-		c = *s++;
-
-		if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
-			continue;
-
-		if (c == '=')
-		{
-			/* end sequence */
-			if (!end)
-			{
-				if (pos == 2)
-					end = 1;
-				else if (pos == 3)
-					end = 2;
-				else
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("unexpected \"=\" while decoding base64 sequence")));
-			}
-			b = 0;
-		}
-		else
-		{
-			b = -1;
-			if (c > 0 && c < 127)
-				b = b64lookup[(unsigned char) c];
-			if (b < 0)
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("invalid symbol \"%c\" while decoding base64 sequence", (int) c)));
-		}
-		/* add it to buffer */
-		buf = (buf << 6) + b;
-		pos++;
-		if (pos == 4)
-		{
-			*p++ = (buf >> 16) & 255;
-			if (end == 0 || end > 1)
-				*p++ = (buf >> 8) & 255;
-			if (end == 0 || end > 2)
-				*p++ = buf & 255;
-			buf = 0;
-			pos = 0;
-		}
-	}
-
-	if (pos != 0)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("invalid base64 end sequence"),
-				 errhint("Input data is missing padding, is truncated, or is otherwise corrupted.")));
-
-	return p - dst;
-}
-
-
-static unsigned
-b64_enc_len(const char *src, unsigned srclen)
-{
-	/* 3 bytes will be converted to 4, linefeed after 76 chars */
-	return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);
-}
-
-static unsigned
-b64_dec_len(const char *src, unsigned srclen)
-{
-	return (srclen * 3) >> 2;
-}
-
-/*
- * Escape
- * Minimally escape bytea to text.
- * De-escape text to bytea.
- *
- * We must escape zero bytes and high-bit-set bytes to avoid generating
- * text that might be invalid in the current encoding, or that might
- * change to something else if passed through an encoding conversion
- * (leading to failing to de-escape to the original bytea value).
- * Also of course backslash itself has to be escaped.
- *
- * De-escaping processes \\ and any \### octal
- */
-
-#define VAL(CH)			((CH) - '0')
-#define DIG(VAL)		((VAL) + '0')
-
-static unsigned
-esc_encode(const char *src, unsigned srclen, char *dst)
-{
-	const char *end = src + srclen;
-	char	   *rp = dst;
-	int			len = 0;
-
-	while (src < end)
-	{
-		unsigned char c = (unsigned char) *src;
-
-		if (c == '\0' || IS_HIGHBIT_SET(c))
-		{
-			rp[0] = '\\';
-			rp[1] = DIG(c >> 6);
-			rp[2] = DIG((c >> 3) & 7);
-			rp[3] = DIG(c & 7);
-			rp += 4;
-			len += 4;
-		}
-		else if (c == '\\')
-		{
-			rp[0] = '\\';
-			rp[1] = '\\';
-			rp += 2;
-			len += 2;
-		}
-		else
-		{
-			*rp++ = c;
-			len++;
-		}
-
-		src++;
-	}
-
-	return len;
-}
-
-static unsigned
-esc_decode(const char *src, unsigned srclen, char *dst)
-{
-	const char *end = src + srclen;
-	char	   *rp = dst;
-	int			len = 0;
-
-	while (src < end)
-	{
-		if (src[0] != '\\')
-			*rp++ = *src++;
-		else if (src + 3 < end &&
-				 (src[1] >= '0' && src[1] <= '3') &&
-				 (src[2] >= '0' && src[2] <= '7') &&
-				 (src[3] >= '0' && src[3] <= '7'))
-		{
-			int			val;
-
-			val = VAL(src[1]);
-			val <<= 3;
-			val += VAL(src[2]);
-			val <<= 3;
-			*rp++ = val + VAL(src[3]);
-			src += 4;
-		}
-		else if (src + 1 < end &&
-				 (src[1] == '\\'))
-		{
-			*rp++ = '\\';
-			src += 2;
-		}
-		else
-		{
-			/*
-			 * One backslash, not followed by ### valid octal. Should never
-			 * get here, since esc_dec_len does same check.
-			 */
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-					 errmsg("invalid input syntax for type bytea")));
-		}
-
-		len++;
-	}
-
-	return len;
-}
-
-static unsigned
-esc_enc_len(const char *src, unsigned srclen)
-{
-	const char *end = src + srclen;
-	int			len = 0;
-
-	while (src < end)
-	{
-		if (*src == '\0' || IS_HIGHBIT_SET(*src))
-			len += 4;
-		else if (*src == '\\')
-			len += 2;
-		else
-			len++;
-
-		src++;
-	}
-
-	return len;
-}
-
-static unsigned
-esc_dec_len(const char *src, unsigned srclen)
-{
-	const char *end = src + srclen;
-	int			len = 0;
-
-	while (src < end)
-	{
-		if (src[0] != '\\')
-			src++;
-		else if (src + 3 < end &&
-				 (src[1] >= '0' && src[1] <= '3') &&
-				 (src[2] >= '0' && src[2] <= '7') &&
-				 (src[3] >= '0' && src[3] <= '7'))
-		{
-			/*
-			 * backslash + valid octal
-			 */
-			src += 4;
-		}
-		else if (src + 1 < end &&
-				 (src[1] == '\\'))
-		{
-			/*
-			 * two backslashes = backslash
-			 */
-			src += 2;
-		}
-		else
-		{
-			/*
-			 * one backslash, not followed by ### valid octal
-			 */
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-					 errmsg("invalid input syntax for type bytea")));
-		}
-
-		len++;
-	}
-	return len;
-}
-
-/*
  * Common
  */
 
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 260a5aa..ac13067 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -21,6 +21,7 @@
 #include "access/tuptoaster.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
+#include "common/encode_utils.h"
 #include "common/md5.h"
 #include "lib/hyperloglog.h"
 #include "libpq/pqformat.h"
diff --git a/src/common/Makefile b/src/common/Makefile
index f1cce0f..3b36c0c 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -40,9 +40,9 @@ override CPPFLAGS += -DVAL_LDFLAGS_EX="\"$(LDFLAGS_EX)\""
 override CPPFLAGS += -DVAL_LDFLAGS_SL="\"$(LDFLAGS_SL)\""
 override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\""
 
-OBJS_COMMON = config_info.o controldata_utils.o exec.o ip.o keywords.o \
-	md5.o pg_lzcompress.o pgfnames.o psprintf.o relpath.o rmtree.o \
-	string.o username.o wait_error.o
+OBJS_COMMON = config_info.o controldata_utils.o exec.o encode_utils.o ip.o \
+	keywords.o md5.o pg_lzcompress.o pgfnames.o psprintf.o relpath.o \
+	rmtree.o string.o username.o wait_error.o
 
 ifeq ($(with_openssl),yes)
 OBJS_COMMON += sha_openssl.o
diff --git a/src/common/encode_utils.c b/src/common/encode_utils.c
new file mode 100644
index 0000000..490392a
--- /dev/null
+++ b/src/common/encode_utils.c
@@ -0,0 +1,470 @@
+/*-------------------------------------------------------------------------
+ *
+ * encode_utils.c
+ *	  Various data encoding/decoding things for base64, hexadecimal and
+ *	  escape. In case of failure, those routines return elog(ERROR) in
+ *	  the backend, and 0 in the frontend to let the caller handle the
+ *	  error handling, something needed by libpq.
+ *
+ * Copyright (c) 2001-2016, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/common/encode_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/encode_utils.h"
+
+/*
+ * BASE64
+ */
+
+static const char _base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const int8 b64lookup[128] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+	-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+};
+
+unsigned
+b64_encode(const char *src, unsigned len, char *dst)
+{
+	char	   *p,
+			   *lend = dst + 76;
+	const char *s,
+			   *end = src + len;
+	int			pos = 2;
+	uint32		buf = 0;
+
+	s = src;
+	p = dst;
+
+	while (s < end)
+	{
+		buf |= (unsigned char) *s << (pos << 3);
+		pos--;
+		s++;
+
+		/* write it out */
+		if (pos < 0)
+		{
+			*p++ = _base64[(buf >> 18) & 0x3f];
+			*p++ = _base64[(buf >> 12) & 0x3f];
+			*p++ = _base64[(buf >> 6) & 0x3f];
+			*p++ = _base64[buf & 0x3f];
+
+			pos = 2;
+			buf = 0;
+		}
+		if (p >= lend)
+		{
+			*p++ = '\n';
+			lend = p + 76;
+		}
+	}
+	if (pos != 2)
+	{
+		*p++ = _base64[(buf >> 18) & 0x3f];
+		*p++ = _base64[(buf >> 12) & 0x3f];
+		*p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
+		*p++ = '=';
+	}
+
+	return p - dst;
+}
+
+unsigned
+b64_decode(const char *src, unsigned len, char *dst)
+{
+	const char *srcend = src + len,
+			   *s = src;
+	char	   *p = dst;
+	char		c;
+	int			b = 0;
+	uint32		buf = 0;
+	int			pos = 0,
+				end = 0;
+
+	while (s < srcend)
+	{
+		c = *s++;
+
+		if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+			continue;
+
+		if (c == '=')
+		{
+			/* end sequence */
+			if (!end)
+			{
+				if (pos == 2)
+					end = 1;
+				else if (pos == 3)
+					end = 2;
+				else
+				{
+#ifndef FRONTEND
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("unexpected \"=\" while decoding base64 sequence")));
+#else
+					return 0;
+#endif
+				}
+			}
+			b = 0;
+		}
+		else
+		{
+			b = -1;
+			if (c > 0 && c < 127)
+				b = b64lookup[(unsigned char) c];
+			if (b < 0)
+			{
+#ifndef FRONTEND
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("invalid symbol \"%c\" while decoding base64 sequence",
+								(int) c)));
+#else
+				return 0;
+#endif
+			}
+		}
+		/* add it to buffer */
+		buf = (buf << 6) + b;
+		pos++;
+		if (pos == 4)
+		{
+			*p++ = (buf >> 16) & 255;
+			if (end == 0 || end > 1)
+				*p++ = (buf >> 8) & 255;
+			if (end == 0 || end > 2)
+				*p++ = buf & 255;
+			buf = 0;
+			pos = 0;
+		}
+	}
+
+	if (pos != 0)
+	{
+#ifndef FRONTEND
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid base64 end sequence"),
+				 errhint("Input data is missing padding, is truncated, or is otherwise corrupted.")));
+#else
+		return 0;
+#endif
+	}
+
+	return p - dst;
+}
+
+
+unsigned
+b64_enc_len(const char *src, unsigned srclen)
+{
+	/* 3 bytes will be converted to 4, linefeed after 76 chars */
+	return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);
+}
+
+unsigned
+b64_dec_len(const char *src, unsigned srclen)
+{
+	return (srclen * 3) >> 2;
+}
+
+/*
+ * Escape
+ * Minimally escape bytea to text.
+ * De-escape text to bytea.
+ *
+ * We must escape zero bytes and high-bit-set bytes to avoid generating
+ * text that might be invalid in the current encoding, or that might
+ * change to something else if passed through an encoding conversion
+ * (leading to failing to de-escape to the original bytea value).
+ * Also of course backslash itself has to be escaped.
+ *
+ * De-escaping processes \\ and any \### octal
+ */
+
+#define VAL(CH)			((CH) - '0')
+#define DIG(VAL)		((VAL) + '0')
+
+unsigned
+esc_encode(const char *src, unsigned srclen, char *dst)
+{
+	const char *end = src + srclen;
+	char	   *rp = dst;
+	int			len = 0;
+
+	while (src < end)
+	{
+		unsigned char c = (unsigned char) *src;
+
+		if (c == '\0' || IS_HIGHBIT_SET(c))
+		{
+			rp[0] = '\\';
+			rp[1] = DIG(c >> 6);
+			rp[2] = DIG((c >> 3) & 7);
+			rp[3] = DIG(c & 7);
+			rp += 4;
+			len += 4;
+		}
+		else if (c == '\\')
+		{
+			rp[0] = '\\';
+			rp[1] = '\\';
+			rp += 2;
+			len += 2;
+		}
+		else
+		{
+			*rp++ = c;
+			len++;
+		}
+
+		src++;
+	}
+
+	return len;
+}
+
+unsigned
+esc_decode(const char *src, unsigned srclen, char *dst)
+{
+	const char *end = src + srclen;
+	char	   *rp = dst;
+	int			len = 0;
+
+	while (src < end)
+	{
+		if (src[0] != '\\')
+			*rp++ = *src++;
+		else if (src + 3 < end &&
+				 (src[1] >= '0' && src[1] <= '3') &&
+				 (src[2] >= '0' && src[2] <= '7') &&
+				 (src[3] >= '0' && src[3] <= '7'))
+		{
+			int			val;
+
+			val = VAL(src[1]);
+			val <<= 3;
+			val += VAL(src[2]);
+			val <<= 3;
+			*rp++ = val + VAL(src[3]);
+			src += 4;
+		}
+		else if (src + 1 < end &&
+				 (src[1] == '\\'))
+		{
+			*rp++ = '\\';
+			src += 2;
+		}
+		else
+		{
+			/*
+			 * One backslash, not followed by ### valid octal. Should never
+			 * get here, since esc_dec_len does same check.
+			 */
+#ifndef FRONTEND
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid input syntax for type bytea")));
+#else
+			return 0;
+#endif
+		}
+
+		len++;
+	}
+
+	return len;
+}
+
+unsigned
+esc_enc_len(const char *src, unsigned srclen)
+{
+	const char *end = src + srclen;
+	int			len = 0;
+
+	while (src < end)
+	{
+		if (*src == '\0' || IS_HIGHBIT_SET(*src))
+			len += 4;
+		else if (*src == '\\')
+			len += 2;
+		else
+			len++;
+
+		src++;
+	}
+
+	return len;
+}
+
+unsigned
+esc_dec_len(const char *src, unsigned srclen)
+{
+	const char *end = src + srclen;
+	int			len = 0;
+
+	while (src < end)
+	{
+		if (src[0] != '\\')
+			src++;
+		else if (src + 3 < end &&
+				 (src[1] >= '0' && src[1] <= '3') &&
+				 (src[2] >= '0' && src[2] <= '7') &&
+				 (src[3] >= '0' && src[3] <= '7'))
+		{
+			/*
+			 * backslash + valid octal
+			 */
+			src += 4;
+		}
+		else if (src + 1 < end &&
+				 (src[1] == '\\'))
+		{
+			/*
+			 * two backslashes = backslash
+			 */
+			src += 2;
+		}
+		else
+		{
+			/*
+			 * one backslash, not followed by ### valid octal
+			 */
+#ifndef FRONTEND
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("invalid input syntax for type bytea")));
+#else
+			return 0;
+#endif
+		}
+
+		len++;
+	}
+	return len;
+}
+
+/*
+ * HEX
+ */
+
+static const char hextbl[] = "0123456789abcdef";
+
+static const int8 hexlookup[128] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+unsigned
+hex_encode(const char *src, unsigned len, char *dst)
+{
+	const char *end = src + len;
+
+	while (src < end)
+	{
+		*dst++ = hextbl[(*src >> 4) & 0xF];
+		*dst++ = hextbl[*src & 0xF];
+		src++;
+	}
+	return len * 2;
+}
+
+static inline char
+get_hex(char c)
+{
+	int			res = -1;
+
+	if (c > 0 && c < 127)
+		res = hexlookup[(unsigned char) c];
+
+	if (res < 0)
+	{
+#ifndef FRONTEND
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid hexadecimal digit: \"%c\"", c)));
+#else
+		return 0;
+#endif
+	}
+
+	return (char) res;
+}
+
+unsigned
+hex_decode(const char *src, unsigned len, char *dst)
+{
+	const char *s,
+			   *srcend;
+	char		v1,
+				v2,
+			   *p;
+
+	srcend = src + len;
+	s = src;
+	p = dst;
+	while (s < srcend)
+	{
+		if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+		{
+			s++;
+			continue;
+		}
+		v1 = get_hex(*s++) << 4;
+		if (s >= srcend)
+		{
+#ifndef FRONTEND
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				  errmsg("invalid hexadecimal data: odd number of digits")));
+#else
+			return 0;
+#endif
+		}
+
+		v2 = get_hex(*s++);
+		*p++ = v1 | v2;
+	}
+
+	return p - dst;
+}
+
+unsigned
+hex_enc_len(const char *src, unsigned srclen)
+{
+	return srclen << 1;
+}
+
+unsigned
+hex_dec_len(const char *src, unsigned srclen)
+{
+	return srclen >> 1;
+}
diff --git a/src/include/common/encode_utils.h b/src/include/common/encode_utils.h
new file mode 100644
index 0000000..92d5b2f
--- /dev/null
+++ b/src/include/common/encode_utils.h
@@ -0,0 +1,30 @@
+/*
+ *	encode_utils.h
+ *		Encoding and decoding routines for base64, hexadecimal and escape.
+ *
+ *	Portions Copyright (c) 2001-2016, PostgreSQL Global Development Group
+ *
+ *	src/include/common/encode_utils.h
+ */
+#ifndef ENCODE_UTILS_H
+#define ENCODE_UTILS_H
+
+/* base 64 */
+unsigned b64_encode(const char *src, unsigned len, char *dst);
+unsigned b64_decode(const char *src, unsigned len, char *dst);
+unsigned b64_enc_len(const char *src, unsigned srclen);
+unsigned b64_dec_len(const char *src, unsigned srclen);
+
+/* hex */
+unsigned hex_encode(const char *src, unsigned len, char *dst);
+unsigned hex_decode(const char *src, unsigned len, char *dst);
+unsigned hex_enc_len(const char *src, unsigned srclen);
+unsigned hex_dec_len(const char *src, unsigned srclen);
+
+/* escape */
+unsigned esc_encode(const char *src, unsigned srclen, char *dst);
+unsigned esc_decode(const char *src, unsigned srclen, char *dst);
+unsigned esc_enc_len(const char *src, unsigned srclen);
+unsigned esc_dec_len(const char *src, unsigned srclen);
+
+#endif   /* ENCODE_UTILS_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2ae212a..e36d28e 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -161,8 +161,6 @@ extern int	errdomainconstraint(Oid datatypeOid, const char *conname);
 /* encode.c */
 extern Datum binary_encode(PG_FUNCTION_ARGS);
 extern Datum binary_decode(PG_FUNCTION_ARGS);
-extern unsigned hex_encode(const char *src, unsigned len, char *dst);
-extern unsigned hex_decode(const char *src, unsigned len, char *dst);
 
 /* enum.c */
 extern Datum enum_in(PG_FUNCTION_ARGS);
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index fc075f4..8e749e6 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -110,7 +110,7 @@ sub mkvcbuild
 	}
 
 	our @pgcommonallfiles = qw(
-	  config_info.c controldata_utils.c exec.c ip.c keywords.c
+	  config_info.c controldata_utils.c encode_utils.c exec.c ip.c keywords.c
 	  md5.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
 	  scram-common.c string.c username.c wait_error.c);
 
-- 
2.9.3

