On Mon, Jan 11, 2021 at 11:27:30AM -0500, Bruce Momjian wrote:
> Sure, I realize the elog/pg_log is odd, but the alternatives seem worse.

I guess that it depends on the use cases.  If there is no need to
worry about shared libraries, elog/pg_log would do just fine.

> You can take ownership of my hex patch and just add to it.  I know you
> already did the hex length part, and have other ideas of what you want.

OK, thanks.  I have been looking at it, and tweaked the patch as per
the attached.  That's basically what you did on the following points:
- Use size_t for the arguments, uint64 as return result.
- Leave ECPG out of the equation.
- Use pg_log()/elog() to report failures in src/common/, rather than
error codes.
- Renamed the arguments of encode.c to src, srclen, dst, dstlen.

The two only things that were not present are the set of checks for
overflows, and the adjustments for varlena.c.  The first point makes
the code of encode.c safer, as previously the code would issue a FATAL
*after* writing out-of-bound data.  Now it issues an ERROR before any
overwrite is done, and I have added some assertions as an extra safety
net.  For the second, I think that it makes the allocation pattern
easier to follow, similarly to checksum manifests.

Thoughts?
--
Michael
From 3e5c9fa42ff981933bc6eeede92335ae89e94e21 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 12 Jan 2021 11:21:42 +0900
Subject: [PATCH v5] Refactor hex code

---
 src/include/common/hex.h                  |  25 +++
 src/include/common/hex_decode.h           |  16 --
 src/include/utils/builtins.h              |   3 -
 src/backend/replication/backup_manifest.c |  28 ++--
 src/backend/utils/adt/encode.c            |  96 ++++++-----
 src/backend/utils/adt/varlena.c           |  16 +-
 src/common/Makefile                       |   2 +-
 src/common/hex.c                          | 196 ++++++++++++++++++++++
 src/common/hex_decode.c                   | 106 ------------
 src/tools/msvc/Mkvcbuild.pm               |   2 +-
 10 files changed, 308 insertions(+), 182 deletions(-)
 create mode 100644 src/include/common/hex.h
 delete mode 100644 src/include/common/hex_decode.h
 create mode 100644 src/common/hex.c
 delete mode 100644 src/common/hex_decode.c

diff --git a/src/include/common/hex.h b/src/include/common/hex.h
new file mode 100644
index 0000000000..3c3c956bb6
--- /dev/null
+++ b/src/include/common/hex.h
@@ -0,0 +1,25 @@
+/*------------------------------------------------------------------------
+ *
+ *	hex.h
+ *	  Encoding and decoding routines for hex strings.
+ *
+ *	Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ *	Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *		  src/include/common/hex.h
+ *
+ *------------------------------------------------------------------------
+ */
+
+#ifndef COMMON_HEX_H
+#define COMMON_HEX_H
+
+extern uint64 pg_hex_decode(const char *src, size_t srclen,
+							char *dst, size_t dstlen);
+extern uint64 pg_hex_encode(const char *src, size_t srclen,
+							char *dst, size_t dstlen);
+extern uint64 pg_hex_enc_len(size_t srclen);
+extern uint64 pg_hex_dec_len(size_t srclen);
+
+#endif							/* COMMON_HEX_H */
diff --git a/src/include/common/hex_decode.h b/src/include/common/hex_decode.h
deleted file mode 100644
index 29ab248458..0000000000
--- a/src/include/common/hex_decode.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- *	hex_decode.h
- *		hex decoding
- *
- *	Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
- *	Portions Copyright (c) 1994, Regents of the University of California
- *
- *	src/include/common/hex_decode.h
- */
-#ifndef COMMON_HEX_DECODE_H
-#define COMMON_HEX_DECODE_H
-
-extern uint64 hex_decode(const char *src, size_t len, char *dst);
-
-
-#endif							/* COMMON_HEX_DECODE_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 6f739a8822..27d2f2ffb3 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -31,9 +31,6 @@ extern void domain_check(Datum value, bool isnull, Oid domainType,
 extern int	errdatatype(Oid datatypeOid);
 extern int	errdomainconstraint(Oid datatypeOid, const char *conname);
 
-/* encode.c */
-extern uint64 hex_encode(const char *src, size_t len, char *dst);
-
 /* int.c */
 extern int2vector *buildint2vector(const int16 *int2s, int n);
 
diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c
index 8af94610b3..0df5186828 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -13,11 +13,11 @@
 #include "postgres.h"
 
 #include "access/timeline.h"
+#include "common/hex.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
 #include "replication/backup_manifest.h"
-#include "utils/builtins.h"
 #include "utils/json.h"
 
 static void AppendStringToManifest(backup_manifest_info *manifest, char *s);
@@ -150,10 +150,12 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid,
 	}
 	else
 	{
+		uint64	dstlen = pg_hex_enc_len(pathlen);
+
 		appendStringInfoString(&buf, "{ \"Encoded-Path\": \"");
-		enlargeStringInfo(&buf, 2 * pathlen);
-		buf.len += hex_encode(pathname, pathlen,
-							  &buf.data[buf.len]);
+		enlargeStringInfo(&buf, dstlen);
+		buf.len += pg_hex_encode(pathname, pathlen,
+								 &buf.data[buf.len], dstlen);
 		appendStringInfoString(&buf, "\", ");
 	}
 
@@ -176,6 +178,7 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid,
 	{
 		uint8		checksumbuf[PG_CHECKSUM_MAX_LENGTH];
 		int			checksumlen;
+		uint64		dstlen;
 
 		checksumlen = pg_checksum_final(checksum_ctx, checksumbuf);
 		if (checksumlen < 0)
@@ -185,9 +188,10 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid,
 		appendStringInfo(&buf,
 						 ", \"Checksum-Algorithm\": \"%s\", \"Checksum\": \"",
 						 pg_checksum_type_name(checksum_ctx->type));
-		enlargeStringInfo(&buf, 2 * checksumlen);
-		buf.len += hex_encode((char *) checksumbuf, checksumlen,
-							  &buf.data[buf.len]);
+		dstlen = pg_hex_enc_len(checksumlen);
+		enlargeStringInfo(&buf, dstlen);
+		buf.len += pg_hex_encode((char *) checksumbuf, checksumlen,
+								 &buf.data[buf.len], dstlen);
 		appendStringInfoChar(&buf, '"');
 	}
 
@@ -307,8 +311,9 @@ SendBackupManifest(backup_manifest_info *manifest)
 {
 	StringInfoData protobuf;
 	uint8		checksumbuf[PG_SHA256_DIGEST_LENGTH];
-	char		checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH];
+	char	   *checksumstringbuf;
 	size_t		manifest_bytes_done = 0;
+	uint64		dstlen;
 
 	if (!IsManifestEnabled(manifest))
 		return;
@@ -328,8 +333,11 @@ SendBackupManifest(backup_manifest_info *manifest)
 	if (pg_cryptohash_final(manifest->manifest_ctx, checksumbuf) < 0)
 		elog(ERROR, "failed to finalize checksum of backup manifest");
 	AppendStringToManifest(manifest, "\"Manifest-Checksum\": \"");
-	hex_encode((char *) checksumbuf, sizeof checksumbuf, checksumstringbuf);
-	checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
+	dstlen = pg_hex_enc_len(PG_SHA256_DIGEST_LENGTH);
+	checksumstringbuf = palloc0(dstlen + 1);	/* includes \0 */
+	pg_hex_encode((char *) checksumbuf, sizeof checksumbuf,
+				  checksumstringbuf, dstlen);
+	checksumstringbuf[dstlen] = '\0';
 	AppendStringToManifest(manifest, checksumstringbuf);
 	AppendStringToManifest(manifest, "\"}\n");
 
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index 4759be25e9..6e26f2af8a 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -15,7 +15,7 @@
 
 #include <ctype.h>
 
-#include "common/hex_decode.h"
+#include "common/hex.h"
 #include "mb/pg_wchar.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
@@ -32,10 +32,12 @@
  */
 struct pg_encoding
 {
-	uint64		(*encode_len) (const char *data, size_t dlen);
-	uint64		(*decode_len) (const char *data, size_t dlen);
-	uint64		(*encode) (const char *data, size_t dlen, char *res);
-	uint64		(*decode) (const char *data, size_t dlen, char *res);
+	uint64		(*encode_len) (const char *src, size_t srclen);
+	uint64		(*decode_len) (const char *src, size_t srclen);
+	uint64		(*encode) (const char *src, size_t srclen,
+						   char *dst, size_t dstlen);
+	uint64		(*decode) (const char *src, size_t srclen,
+						   char *dst, size_t dstlen);
 };
 
 static const struct pg_encoding *pg_find_encoding(const char *name);
@@ -81,11 +83,7 @@ binary_encode(PG_FUNCTION_ARGS)
 
 	result = palloc(VARHDRSZ + resultlen);
 
-	res = enc->encode(dataptr, datalen, VARDATA(result));
-
-	/* Make this FATAL 'cause we've trodden on memory ... */
-	if (res > resultlen)
-		elog(FATAL, "overflow - encode estimate too small");
+	res = enc->encode(dataptr, datalen, VARDATA(result), resultlen);
 
 	SET_VARSIZE(result, VARHDRSZ + res);
 
@@ -129,11 +127,7 @@ binary_decode(PG_FUNCTION_ARGS)
 
 	result = palloc(VARHDRSZ + resultlen);
 
-	res = enc->decode(dataptr, datalen, VARDATA(result));
-
-	/* Make this FATAL 'cause we've trodden on memory ... */
-	if (res > resultlen)
-		elog(FATAL, "overflow - decode estimate too small");
+	res = enc->decode(dataptr, datalen, VARDATA(result), resultlen);
 
 	SET_VARSIZE(result, VARHDRSZ + res);
 
@@ -145,32 +139,20 @@ binary_decode(PG_FUNCTION_ARGS)
  * HEX
  */
 
-static const char hextbl[] = "0123456789abcdef";
-
-uint64
-hex_encode(const char *src, size_t len, char *dst)
-{
-	const char *end = src + len;
-
-	while (src < end)
-	{
-		*dst++ = hextbl[(*src >> 4) & 0xF];
-		*dst++ = hextbl[*src & 0xF];
-		src++;
-	}
-	return (uint64) len * 2;
-}
-
+/*
+ * Those two wrappers are still needed to match with the layer of
+ * src/common/.
+ */
 static uint64
 hex_enc_len(const char *src, size_t srclen)
 {
-	return (uint64) srclen << 1;
+	return pg_hex_enc_len(srclen);
 }
 
 static uint64
 hex_dec_len(const char *src, size_t srclen)
 {
-	return (uint64) srclen >> 1;
+	return pg_hex_dec_len(srclen);
 }
 
 /*
@@ -192,12 +174,12 @@ static const int8 b64lookup[128] = {
 };
 
 static uint64
-pg_base64_encode(const char *src, size_t len, char *dst)
+pg_base64_encode(const char *src, size_t srclen, char *dst, size_t dstlen)
 {
 	char	   *p,
 			   *lend = dst + 76;
 	const char *s,
-			   *end = src + len;
+			   *end = src + srclen;
 	int			pos = 2;
 	uint32		buf = 0;
 
@@ -213,6 +195,8 @@ pg_base64_encode(const char *src, size_t len, char *dst)
 		/* write it out */
 		if (pos < 0)
 		{
+			if ((p - dst + 4) > dstlen)
+				elog(ERROR, "overflow - encode estimate too small");
 			*p++ = _base64[(buf >> 18) & 0x3f];
 			*p++ = _base64[(buf >> 12) & 0x3f];
 			*p++ = _base64[(buf >> 6) & 0x3f];
@@ -223,25 +207,30 @@ pg_base64_encode(const char *src, size_t len, char *dst)
 		}
 		if (p >= lend)
 		{
+			if ((p - dst + 1) > dstlen)
+				elog(ERROR, "overflow - encode estimate too small");
 			*p++ = '\n';
 			lend = p + 76;
 		}
 	}
 	if (pos != 2)
 	{
+		if ((p - dst + 4) > dstlen)
+			elog(ERROR, "overflow - encode estimate too small");
 		*p++ = _base64[(buf >> 18) & 0x3f];
 		*p++ = _base64[(buf >> 12) & 0x3f];
 		*p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
 		*p++ = '=';
 	}
 
+	Assert((p - dst) <= dstlen);
 	return p - dst;
 }
 
 static uint64
-pg_base64_decode(const char *src, size_t len, char *dst)
+pg_base64_decode(const char *src, size_t srclen, char *dst, size_t dstlen)
 {
-	const char *srcend = src + len,
+	const char *srcend = src + srclen,
 			   *s = src;
 	char	   *p = dst;
 	char		c;
@@ -289,11 +278,21 @@ pg_base64_decode(const char *src, size_t len, char *dst)
 		pos++;
 		if (pos == 4)
 		{
+			if ((p - dst + 1) > dstlen)
+				elog(ERROR, "overflow - decode estimate too small");
 			*p++ = (buf >> 16) & 255;
 			if (end == 0 || end > 1)
+			{
+				if ((p - dst + 1) > dstlen)
+					elog(ERROR, "overflow - decode estimate too small");
 				*p++ = (buf >> 8) & 255;
+			}
 			if (end == 0 || end > 2)
+			{
+				if ((p - dst + 1) > dstlen)
+					elog(ERROR, "overflow - decode estimate too small");
 				*p++ = buf & 255;
+			}
 			buf = 0;
 			pos = 0;
 		}
@@ -305,6 +304,7 @@ pg_base64_decode(const char *src, size_t len, char *dst)
 				 errmsg("invalid base64 end sequence"),
 				 errhint("Input data is missing padding, is truncated, or is otherwise corrupted.")));
 
+	Assert((p - dst) <= dstlen);
 	return p - dst;
 }
 
@@ -340,7 +340,7 @@ pg_base64_dec_len(const char *src, size_t srclen)
 #define DIG(VAL)		((VAL) + '0')
 
 static uint64
-esc_encode(const char *src, size_t srclen, char *dst)
+esc_encode(const char *src, size_t srclen, char *dst, size_t dstlen)
 {
 	const char *end = src + srclen;
 	char	   *rp = dst;
@@ -352,6 +352,8 @@ esc_encode(const char *src, size_t srclen, char *dst)
 
 		if (c == '\0' || IS_HIGHBIT_SET(c))
 		{
+			if ((rp - dst + 4) > dstlen)
+				elog(ERROR, "overflow - encode estimate too small");
 			rp[0] = '\\';
 			rp[1] = DIG(c >> 6);
 			rp[2] = DIG((c >> 3) & 7);
@@ -361,6 +363,8 @@ esc_encode(const char *src, size_t srclen, char *dst)
 		}
 		else if (c == '\\')
 		{
+			if ((rp - dst + 2) > dstlen)
+				elog(ERROR, "overflow - encode estimate too small");
 			rp[0] = '\\';
 			rp[1] = '\\';
 			rp += 2;
@@ -368,6 +372,8 @@ esc_encode(const char *src, size_t srclen, char *dst)
 		}
 		else
 		{
+			if ((rp - dst + 1) > dstlen)
+				elog(ERROR, "overflow - encode estimate too small");
 			*rp++ = c;
 			len++;
 		}
@@ -375,11 +381,12 @@ esc_encode(const char *src, size_t srclen, char *dst)
 		src++;
 	}
 
+	Assert((rp - dst) <= dstlen);
 	return len;
 }
 
 static uint64
-esc_decode(const char *src, size_t srclen, char *dst)
+esc_decode(const char *src, size_t srclen, char *dst, size_t dstlen)
 {
 	const char *end = src + srclen;
 	char	   *rp = dst;
@@ -388,7 +395,11 @@ esc_decode(const char *src, size_t srclen, char *dst)
 	while (src < end)
 	{
 		if (src[0] != '\\')
+		{
+			if ((rp - dst + 1) > dstlen)
+				elog(ERROR, "overflow - decode estimate too small");
 			*rp++ = *src++;
+		}
 		else if (src + 3 < end &&
 				 (src[1] >= '0' && src[1] <= '3') &&
 				 (src[2] >= '0' && src[2] <= '7') &&
@@ -400,12 +411,16 @@ esc_decode(const char *src, size_t srclen, char *dst)
 			val <<= 3;
 			val += VAL(src[2]);
 			val <<= 3;
+			if ((rp - dst + 1) > dstlen)
+				elog(ERROR, "overflow - decode estimate too small");
 			*rp++ = val + VAL(src[3]);
 			src += 4;
 		}
 		else if (src + 1 < end &&
 				 (src[1] == '\\'))
 		{
+			if ((rp - dst + 1) > dstlen)
+				elog(ERROR, "overflow - decode estimate too small");
 			*rp++ = '\\';
 			src += 2;
 		}
@@ -423,6 +438,7 @@ esc_decode(const char *src, size_t srclen, char *dst)
 		len++;
 	}
 
+	Assert((rp - dst) <= dstlen);
 	return len;
 }
 
@@ -504,7 +520,7 @@ static const struct
 	{
 		"hex",
 		{
-			hex_enc_len, hex_dec_len, hex_encode, hex_decode
+			hex_enc_len, hex_dec_len, pg_hex_encode, pg_hex_decode
 		}
 	},
 	{
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 4838bfb4ff..479ed9ae54 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -21,8 +21,8 @@
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/hashfn.h"
+#include "common/hex.h"
 #include "common/int.h"
-#include "common/hex_decode.h"
 #include "common/unicode_norm.h"
 #include "lib/hyperloglog.h"
 #include "libpq/pqformat.h"
@@ -304,10 +304,12 @@ byteain(PG_FUNCTION_ARGS)
 	if (inputText[0] == '\\' && inputText[1] == 'x')
 	{
 		size_t		len = strlen(inputText);
+		uint64		dstlen = pg_hex_dec_len(len - 2);
 
-		bc = (len - 2) / 2 + VARHDRSZ;	/* maximum possible length */
+		bc = dstlen + VARHDRSZ;	/* maximum possible length */
 		result = palloc(bc);
-		bc = hex_decode(inputText + 2, len - 2, VARDATA(result));
+
+		bc = pg_hex_decode(inputText + 2, len - 2, VARDATA(result), dstlen);
 		SET_VARSIZE(result, bc + VARHDRSZ); /* actual length */
 
 		PG_RETURN_BYTEA_P(result);
@@ -396,11 +398,15 @@ byteaout(PG_FUNCTION_ARGS)
 
 	if (bytea_output == BYTEA_OUTPUT_HEX)
 	{
+		uint64		dstlen = pg_hex_enc_len(VARSIZE_ANY_EXHDR(vlena));
+
 		/* Print hex format */
-		rp = result = palloc(VARSIZE_ANY_EXHDR(vlena) * 2 + 2 + 1);
+		rp = result = palloc(dstlen + 2 + 1);
 		*rp++ = '\\';
 		*rp++ = 'x';
-		rp += hex_encode(VARDATA_ANY(vlena), VARSIZE_ANY_EXHDR(vlena), rp);
+
+		rp += pg_hex_encode(VARDATA_ANY(vlena), VARSIZE_ANY_EXHDR(vlena), rp,
+							dstlen);
 	}
 	else if (bytea_output == BYTEA_OUTPUT_ESCAPE)
 	{
diff --git a/src/common/Makefile b/src/common/Makefile
index f624977939..93eb27a2aa 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -58,7 +58,7 @@ OBJS_COMMON = \
 	file_perm.o \
 	file_utils.o \
 	hashfn.o \
-	hex_decode.o \
+	hex.o \
 	ip.o \
 	jsonapi.o \
 	keywords.o \
diff --git a/src/common/hex.c b/src/common/hex.c
new file mode 100644
index 0000000000..0123c69697
--- /dev/null
+++ b/src/common/hex.c
@@ -0,0 +1,196 @@
+/*-------------------------------------------------------------------------
+ *
+ * hex.c
+ *	  Encoding and decoding routines for hex.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/common/hex.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/hex.h"
+#ifdef FRONTEND
+#include "common/logging.h"
+#endif
+#include "mb/pg_wchar.h"
+
+
+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,
+};
+
+static const char hextbl[] = "0123456789abcdef";
+
+static inline char
+get_hex(const char *cp)
+{
+	unsigned char c = (unsigned char) *cp;
+	int			res = -1;
+
+	if (c < 127)
+		res = hexlookup[c];
+
+	if (res < 0)
+	{
+#ifdef FRONTEND
+		pg_log_fatal("invalid hexadecimal digit");
+		exit(EXIT_FAILURE);
+#else
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid hexadecimal digit: \"%.*s\"",
+						pg_mblen(cp), cp)));
+#endif
+	}
+
+	return (char) res;
+}
+
+/*
+ * pg_hex_encode
+ *
+ * Encode into hex the given string.  Returns the length of the encoded
+ * string.
+ */
+uint64
+pg_hex_encode(const char *src, size_t srclen, char *dst, size_t dstlen)
+{
+	const char *end = src + srclen;
+	char	   *p;
+
+	p = dst;
+
+	while (src < end)
+	{
+		/*
+		 * Leave if there is an overflow in the area allocated for the
+		 * encoded string.
+		 */
+		if ((p - dst + 2) > dstlen)
+		{
+#ifdef FRONTEND
+			pg_log_fatal("overflow of destination buffer during hex encoding");
+			exit(EXIT_FAILURE);
+#else
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("overflow of destination buffer during hex encoding")));
+#endif
+		}
+
+		*p++ = hextbl[(*src >> 4) & 0xF];
+		*p++ = hextbl[*src & 0xF];
+		src++;
+	}
+
+	Assert((p - dst) <= dstlen);
+	return p - dst;
+}
+
+/*
+ * pg_hex_decode
+ *
+ * Decode the given hex string.  Returns the length of the decoded string.
+ */
+uint64
+pg_hex_decode(const char *src, size_t srclen, char *dst, size_t dstlen)
+{
+	const char *s,
+			   *srcend;
+	char		v1,
+				v2,
+			   *p;
+
+	srcend = src + srclen;
+	s = src;
+	p = dst;
+	while (s < srcend)
+	{
+		if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+		{
+			s++;
+			continue;
+		}
+		v1 = get_hex(s) << 4;
+		s++;
+
+		if (s >= srcend)
+		{
+#ifdef FRONTEND
+			pg_log_fatal("invalid hexadecimal data: odd number of digits");
+			exit(EXIT_FAILURE);
+#else
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("invalid hexadecimal data: odd number of digits")));
+#endif
+		}
+
+		v2 = get_hex(s);
+		s++;
+
+		/* overflow check */
+		if ((p - dst + 1) > dstlen)
+		{
+#ifdef FRONTEND
+			pg_log_fatal("overflow of destination buffer during hex decoding");
+			exit(EXIT_FAILURE);
+#else
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("overflow of destination buffer during hex decoding")));
+#endif
+		}
+
+		*p++ = v1 | v2;
+	}
+
+	Assert((p - dst) <= dstlen);
+	return p - dst;
+}
+
+/*
+ * pg_hex_enc_len
+ *
+ * Returns to caller the length of the string if it were encoded with
+ * hex based on the length provided by caller.  This is useful to estimate
+ * how large a buffer allocation needs to be done before doing the actual
+ * encoding.
+ */
+uint64
+pg_hex_enc_len(size_t srclen)
+{
+	return (srclen << 1);
+}
+
+/*
+ * pg_hex_dec_len
+ *
+ * Returns to caller the length of the string if it were to be decoded
+ * with hex, based on the length given by caller.  This is useful to
+ * estimate how large a buffer allocation needs to be done before doing
+ * the actual decoding.
+ */
+uint64
+pg_hex_dec_len(size_t srclen)
+{
+	return (srclen >> 1);
+}
diff --git a/src/common/hex_decode.c b/src/common/hex_decode.c
deleted file mode 100644
index dbc0e1c30a..0000000000
--- a/src/common/hex_decode.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * hex_decode.c
- *		hex decoding
- *
- *
- * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- *	  src/common/hex_decode.c
- *
- *-------------------------------------------------------------------------
- */
-
-
-#ifndef FRONTEND
-#include "postgres.h"
-#else
-#include "postgres_fe.h"
-#endif
-
-#ifdef FRONTEND
-#include "common/logging.h"
-#else
-#include "mb/pg_wchar.h"
-#endif
-#include "common/hex_decode.h"
-
-
-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,
-};
-
-static inline char
-get_hex(const char *cp)
-{
-	unsigned char c = (unsigned char) *cp;
-	int			res = -1;
-
-	if (c < 127)
-		res = hexlookup[c];
-
-	if (res < 0)
-	{
-#ifdef FRONTEND
-		pg_log_fatal("invalid hexadecimal digit");
-		exit(EXIT_FAILURE);
-#else
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("invalid hexadecimal digit: \"%.*s\"",
-						pg_mblen(cp), cp)));
-#endif
-	}
-
-	return (char) res;
-}
-
-uint64
-hex_decode(const char *src, size_t 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;
-		s++;
-		if (s >= srcend)
-		{
-#ifdef FRONTEND
-			pg_log_fatal("invalid hexadecimal data: odd number of digits");
-			exit(EXIT_FAILURE);
-#else
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("invalid hexadecimal data: odd number of digits")));
-#endif
-		}
-		v2 = get_hex(s);
-		s++;
-		*p++ = v1 | v2;
-	}
-
-	return p - dst;
-}
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 7f014a12c9..60b216cce0 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -121,7 +121,7 @@ sub mkvcbuild
 	our @pgcommonallfiles = qw(
 	  archive.c base64.c checksum_helper.c
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
-	  f2s.c file_perm.c file_utils.c hashfn.c hex_decode.c ip.c jsonapi.c
+	  f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.c
 	  keywords.c kwlookup.c link-canary.c md5_common.c
 	  pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
 	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-- 
2.30.0

Attachment: signature.asc
Description: PGP signature

Reply via email to