From 97196948de301df9667d7bb4521806f6343e4d2f Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 27 Feb 2026 22:21:36 +1300
Subject: [PATCH v1] Provide stack allocation API.

Several places open-coded an array on the stack for non-escaping memory
of dynamic size, with fallback to palloc()/pfree().  Create a standard
API for that, and implement it with alloca() when available (in
practice, always).  alloca() is non-standard and discouraged due to
overflow risk and implementation details, but we apply the existing
space limit and restrict usage to suitable compilers.

Existing users of the technique in pg_local_XXX functions are updated to
use the new API, as well as some nearby candidates that were previously
using palloc().

Reviewed-by:
---
 src/backend/utils/adt/pg_locale.c      |   7 +-
 src/backend/utils/adt/pg_locale_icu.c  |  59 +++-------
 src/backend/utils/adt/pg_locale_libc.c | 150 +++++++++---------------
 src/include/utils/stack_buffer.h       | 154 +++++++++++++++++++++++++
 4 files changed, 226 insertions(+), 144 deletions(-)
 create mode 100644 src/include/utils/stack_buffer.h

diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index ac324ecaad2..c2e9b303639 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -50,6 +50,7 @@
 #include "utils/pg_locale.h"
 #include "utils/pg_locale_c.h"
 #include "utils/relcache.h"
+#include "utils/stack_buffer.h"
 #include "utils/syscache.h"
 
 #ifdef WIN32
@@ -60,12 +61,6 @@
 #define		PGLOCALE_SUPPORT_ERROR(provider) \
 	elog(ERROR, "unsupported collprovider for %s: %c", __func__, provider)
 
-/*
- * This should be large enough that most strings will fit, but small enough
- * that we feel comfortable putting it on the stack
- */
-#define		TEXTBUFLEN			1024
-
 #define		MAX_L10N_DATA		80
 
 /* pg_locale_builtin.c */
diff --git a/src/backend/utils/adt/pg_locale_icu.c b/src/backend/utils/adt/pg_locale_icu.c
index 352b4c3885f..8ec13679d90 100644
--- a/src/backend/utils/adt/pg_locale_icu.c
+++ b/src/backend/utils/adt/pg_locale_icu.c
@@ -39,16 +39,9 @@
 #include "utils/formatting.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/stack_buffer.h"
 #include "utils/syscache.h"
 
-/*
- * Size of stack buffer to use for string transformations, used to avoid heap
- * allocations in typical cases. This should be large enough that most strings
- * will fit, but small enough that we feel comfortable putting it on the
- * stack.
- */
-#define		TEXTBUFLEN			1024
-
 extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
 
 #ifdef USE_ICU
@@ -755,23 +748,17 @@ size_t
 strnxfrm_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
 			 pg_locale_t locale)
 {
-	char		sbuf[TEXTBUFLEN];
-	char	   *buf = sbuf;
 	UChar	   *uchar;
 	int32_t		ulen;
-	size_t		uchar_bsize;
 	Size		result_bsize;
 
+	DECLARE_STACK_BUFFER();
+
 	init_icu_converter();
 
 	ulen = uchar_length(icu_converter, src, srclen);
 
-	uchar_bsize = (ulen + 1) * sizeof(UChar);
-
-	if (uchar_bsize > TEXTBUFLEN)
-		buf = palloc(uchar_bsize);
-
-	uchar = (UChar *) buf;
+	uchar = stack_buffer_alloc_array(UChar, ulen + 1);
 
 	ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
 
@@ -786,8 +773,7 @@ strnxfrm_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
 	Assert(result_bsize > 0);
 	result_bsize--;
 
-	if (buf != sbuf)
-		pfree(buf);
+	stack_buffer_free(uchar);
 
 	/* if dest is defined, it should be nul-terminated */
 	Assert(result_bsize >= destsize || dest[result_bsize] == '\0');
@@ -1020,16 +1006,14 @@ static int
 strncoll_icu(const char *arg1, ssize_t len1,
 			 const char *arg2, ssize_t len2, pg_locale_t locale)
 {
-	char		sbuf[TEXTBUFLEN];
-	char	   *buf = sbuf;
 	int32_t		ulen1;
 	int32_t		ulen2;
-	size_t		bufsize1;
-	size_t		bufsize2;
 	UChar	   *uchar1,
 			   *uchar2;
 	int			result;
 
+	DECLARE_STACK_BUFFER();
+
 	/* if encoding is UTF8, use more efficient strncoll_icu_utf8 */
 #ifdef HAVE_UCOL_STRCOLLUTF8
 	Assert(GetDatabaseEncoding() != PG_UTF8);
@@ -1040,14 +1024,8 @@ strncoll_icu(const char *arg1, ssize_t len1,
 	ulen1 = uchar_length(icu_converter, arg1, len1);
 	ulen2 = uchar_length(icu_converter, arg2, len2);
 
-	bufsize1 = (ulen1 + 1) * sizeof(UChar);
-	bufsize2 = (ulen2 + 1) * sizeof(UChar);
-
-	if (bufsize1 + bufsize2 > TEXTBUFLEN)
-		buf = palloc(bufsize1 + bufsize2);
-
-	uchar1 = (UChar *) buf;
-	uchar2 = (UChar *) (buf + bufsize1);
+	uchar1 = stack_buffer_alloc_array(UChar, ulen1 + 1);
+	uchar2 = stack_buffer_alloc_array(UChar, ulen2 + 1);
 
 	ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1);
 	ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2);
@@ -1056,8 +1034,8 @@ strncoll_icu(const char *arg1, ssize_t len1,
 						  uchar1, ulen1,
 						  uchar2, ulen2);
 
-	if (buf != sbuf)
-		pfree(buf);
+	stack_buffer_free(uchar1);
+	stack_buffer_free(uchar2);
 
 	return result;
 }
@@ -1068,16 +1046,15 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
 					const char *src, ssize_t srclen,
 					pg_locale_t locale)
 {
-	char		sbuf[TEXTBUFLEN];
-	char	   *buf = sbuf;
 	UCharIterator iter;
 	uint32_t	state[2];
 	UErrorCode	status;
 	int32_t		ulen = -1;
 	UChar	   *uchar = NULL;
-	size_t		uchar_bsize;
 	Size		result_bsize;
 
+	DECLARE_STACK_BUFFER();
+
 	/* if encoding is UTF8, use more efficient strnxfrm_prefix_icu_utf8 */
 	Assert(GetDatabaseEncoding() != PG_UTF8);
 
@@ -1085,12 +1062,7 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
 
 	ulen = uchar_length(icu_converter, src, srclen);
 
-	uchar_bsize = (ulen + 1) * sizeof(UChar);
-
-	if (uchar_bsize > TEXTBUFLEN)
-		buf = palloc(uchar_bsize);
-
-	uchar = (UChar *) buf;
+	uchar = stack_buffer_alloc_array(UChar, ulen + 1);
 
 	ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
 
@@ -1108,8 +1080,7 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
 				(errmsg("sort key generation failed: %s",
 						u_errorName(status))));
 
-	if (buf != sbuf)
-		pfree(buf);
+	stack_buffer_free(uchar);
 
 	return result_bsize;
 }
diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c
index 78f6ea161a0..0f1bb490c32 100644
--- a/src/backend/utils/adt/pg_locale_libc.c
+++ b/src/backend/utils/adt/pg_locale_libc.c
@@ -23,6 +23,7 @@
 #include "utils/formatting.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/stack_buffer.h"
 #include "utils/syscache.h"
 
 #ifdef __GLIBC__
@@ -72,14 +73,6 @@
  * NB: the coding here assumes pg_wchar is an unsigned type.
  */
 
-/*
- * Size of stack buffer to use for string transformations, used to avoid heap
- * allocations in typical cases. This should be large enough that most strings
- * will fit, but small enough that we feel comfortable putting it on the
- * stack.
- */
-#define		TEXTBUFLEN			1024
-
 extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
 
 static int	strncoll_libc(const char *arg1, ssize_t len1,
@@ -502,6 +495,8 @@ strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 	size_t		curr_char;
 	size_t		max_size;
 
+	DECLARE_STACK_BUFFER();
+
 	if (srclen < 0)
 		srclen = strlen(src);
 
@@ -512,7 +507,7 @@ strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 				 errmsg("out of memory")));
 
 	/* Output workspace cannot have more codes than input bytes */
-	workspace = palloc_array(wchar_t, srclen + 1);
+	workspace = stack_buffer_alloc_array(wchar_t, srclen + 1);
 
 	char2wchar(workspace, srclen + 1, src, srclen, loc);
 
@@ -523,7 +518,7 @@ strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 	 * Make result large enough; case change might change number of bytes
 	 */
 	max_size = curr_char * pg_database_encoding_max_length();
-	result = palloc(max_size + 1);
+	result = stack_buffer_alloc(max_size + 1);
 
 	result_size = wchar2char(result, workspace, max_size + 1, loc);
 
@@ -533,8 +528,8 @@ strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 		dest[result_size] = '\0';
 	}
 
-	pfree(workspace);
-	pfree(result);
+	stack_buffer_free(workspace);
+	stack_buffer_free(result);
 
 	return result_size;
 }
@@ -607,6 +602,8 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 	size_t		curr_char;
 	size_t		max_size;
 
+	DECLARE_STACK_BUFFER();
+
 	if (srclen < 0)
 		srclen = strlen(src);
 
@@ -617,7 +614,7 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 				 errmsg("out of memory")));
 
 	/* Output workspace cannot have more codes than input bytes */
-	workspace = palloc_array(wchar_t, srclen + 1);
+	workspace = stack_buffer_alloc_array(wchar_t, srclen + 1);
 
 	char2wchar(workspace, srclen + 1, src, srclen, loc);
 
@@ -634,7 +631,7 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 	 * Make result large enough; case change might change number of bytes
 	 */
 	max_size = curr_char * pg_database_encoding_max_length();
-	result = palloc(max_size + 1);
+	result = stack_buffer_alloc(max_size + 1);
 
 	result_size = wchar2char(result, workspace, max_size + 1, loc);
 
@@ -644,8 +641,8 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 		dest[result_size] = '\0';
 	}
 
-	pfree(workspace);
-	pfree(result);
+	stack_buffer_free(workspace);
+	stack_buffer_free(result);
 
 	return result_size;
 }
@@ -700,6 +697,8 @@ strupper_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 	size_t		curr_char;
 	size_t		max_size;
 
+	DECLARE_STACK_BUFFER();
+
 	if (srclen < 0)
 		srclen = strlen(src);
 
@@ -710,7 +709,7 @@ strupper_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 				 errmsg("out of memory")));
 
 	/* Output workspace cannot have more codes than input bytes */
-	workspace = palloc_array(wchar_t, srclen + 1);
+	workspace = stack_buffer_alloc_array(wchar_t, srclen + 1);
 
 	char2wchar(workspace, srclen + 1, src, srclen, loc);
 
@@ -721,7 +720,7 @@ strupper_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 	 * Make result large enough; case change might change number of bytes
 	 */
 	max_size = curr_char * pg_database_encoding_max_length();
-	result = palloc(max_size + 1);
+	result = stack_buffer_alloc(max_size + 1);
 
 	result_size = wchar2char(result, workspace, max_size + 1, loc);
 
@@ -731,8 +730,8 @@ strupper_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
 		dest[result_size] = '\0';
 	}
 
-	pfree(workspace);
-	pfree(result);
+	stack_buffer_free(workspace);
+	stack_buffer_free(result);
 
 	return result_size;
 }
@@ -896,48 +895,25 @@ int
 strncoll_libc(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2,
 			  pg_locale_t locale)
 {
-	char		sbuf[TEXTBUFLEN];
-	char	   *buf = sbuf;
-	size_t		bufsize1 = (len1 == -1) ? 0 : len1 + 1;
-	size_t		bufsize2 = (len2 == -1) ? 0 : len2 + 1;
-	const char *arg1n;
-	const char *arg2n;
+	char	   *cstr1 = NULL;
+	char	   *cstr2 = NULL;
 	int			result;
 
-	if (bufsize1 + bufsize2 > TEXTBUFLEN)
-		buf = palloc(bufsize1 + bufsize2);
+	DECLARE_STACK_BUFFER();
 
 	/* nul-terminate arguments if necessary */
-	if (len1 == -1)
-	{
-		arg1n = arg1;
-	}
-	else
-	{
-		char	   *buf1 = buf;
+	if (len1 != -1)
+		arg1 = cstr1 = stack_buffer_strdup_with_len(arg1, len1);
 
-		memcpy(buf1, arg1, len1);
-		buf1[len1] = '\0';
-		arg1n = buf1;
-	}
+	if (len2 != -1)
+		arg2 = cstr2 = stack_buffer_strdup_with_len(arg2, len2);
 
-	if (len2 == -1)
-	{
-		arg2n = arg2;
-	}
-	else
-	{
-		char	   *buf2 = buf + bufsize1;
-
-		memcpy(buf2, arg2, len2);
-		buf2[len2] = '\0';
-		arg2n = buf2;
-	}
+	result = strcoll_l(arg1, arg2, locale->lt);
 
-	result = strcoll_l(arg1n, arg2n, locale->lt);
-
-	if (buf != sbuf)
-		pfree(buf);
+	if (cstr1)
+		stack_buffer_free(cstr1);
+	if (cstr2)
+		stack_buffer_free(cstr2);
 
 	return result;
 }
@@ -953,25 +929,17 @@ size_t
 strnxfrm_libc(char *dest, size_t destsize, const char *src, ssize_t srclen,
 			  pg_locale_t locale)
 {
-	char		sbuf[TEXTBUFLEN];
-	char	   *buf = sbuf;
-	size_t		bufsize = srclen + 1;
+	char	   *cstr;
 	size_t		result;
 
+	DECLARE_STACK_BUFFER();
+
 	if (srclen == -1)
 		return strxfrm_l(dest, src, destsize, locale->lt);
 
-	if (bufsize > TEXTBUFLEN)
-		buf = palloc(bufsize);
-
-	/* nul-terminate argument */
-	memcpy(buf, src, srclen);
-	buf[srclen] = '\0';
-
-	result = strxfrm_l(dest, buf, destsize, locale->lt);
-
-	if (buf != sbuf)
-		pfree(buf);
+	cstr = stack_buffer_strdup_with_len(src, srclen);
+	result = strxfrm_l(dest, cstr, destsize, locale->lt);
+	stack_buffer_free(cstr);
 
 	/* if dest is defined, it should be nul-terminated */
 	Assert(result >= destsize || dest[result] == '\0');
@@ -1057,15 +1025,13 @@ static int
 strncoll_libc_win32_utf8(const char *arg1, ssize_t len1, const char *arg2,
 						 ssize_t len2, pg_locale_t locale)
 {
-	char		sbuf[TEXTBUFLEN];
-	char	   *buf = sbuf;
-	char	   *a1p,
-			   *a2p;
-	int			a1len;
-	int			a2len;
+	wchar_t    *w1p;
+	wchar_t    *w2p;
 	int			r;
 	int			result;
 
+	DECLARE_STACK_BUFFER();
+
 	Assert(GetDatabaseEncoding() == PG_UTF8);
 
 	if (len1 == -1)
@@ -1073,50 +1039,42 @@ strncoll_libc_win32_utf8(const char *arg1, ssize_t len1, const char *arg2,
 	if (len2 == -1)
 		len2 = strlen(arg2);
 
-	a1len = len1 * 2 + 2;
-	a2len = len2 * 2 + 2;
-
-	if (a1len + a2len > TEXTBUFLEN)
-		buf = palloc(a1len + a2len);
-
-	a1p = buf;
-	a2p = buf + a1len;
+	w1p = stack_buffer_alloc_array(wchar_t, len1 + 1);
+	w2p = stack_buffer_alloc_array(wchar_t, len2 + 1);
 
 	/* API does not work for zero-length input */
 	if (len1 == 0)
 		r = 0;
 	else
 	{
-		r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1,
-								(LPWSTR) a1p, a1len / 2);
+		r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1, w1p, len1);
 		if (!r)
 			ereport(ERROR,
 					(errmsg("could not convert string to UTF-16: error code %lu",
 							GetLastError())));
 	}
-	((LPWSTR) a1p)[r] = 0;
+	w1p[r] = 0;
 
 	if (len2 == 0)
 		r = 0;
 	else
 	{
-		r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2,
-								(LPWSTR) a2p, a2len / 2);
+		r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2, w2p, len2);
 		if (!r)
 			ereport(ERROR,
 					(errmsg("could not convert string to UTF-16: error code %lu",
 							GetLastError())));
 	}
-	((LPWSTR) a2p)[r] = 0;
+	w2p[r] = 0;
 
 	errno = 0;
-	result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->lt);
+	result = wcscoll_l(w1p, w2p, locale->lt);
 	if (result == 2147483647)	/* _NLSCMPERROR; missing from mingw headers */
 		ereport(ERROR,
 				(errmsg("could not compare Unicode strings: %m")));
 
-	if (buf != sbuf)
-		pfree(buf);
+	stack_buffer_free(w1p);
+	stack_buffer_free(w2p);
 
 	return result;
 }
@@ -1289,8 +1247,12 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
 	else
 #endif							/* WIN32 */
 	{
+		char	   *str;
+
+		DECLARE_STACK_BUFFER();
+
 		/* mbstowcs requires ending '\0' */
-		char	   *str = pnstrdup(from, fromlen);
+		str = stack_buffer_strdup_with_len(from, fromlen);
 
 		if (loc == (locale_t) 0)
 		{
@@ -1303,7 +1265,7 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
 			result = mbstowcs_l(to, str, tolen, loc);
 		}
 
-		pfree(str);
+		stack_buffer_free(str);
 	}
 
 	if (result == -1)
diff --git a/src/include/utils/stack_buffer.h b/src/include/utils/stack_buffer.h
new file mode 100644
index 00000000000..168263e9f61
--- /dev/null
+++ b/src/include/utils/stack_buffer.h
@@ -0,0 +1,154 @@
+/*-------------------------------------------------------------------------
+ *
+ * stack_buffer.h
+ *		Memory allocator for small non-escaping objects.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/stack_buffer.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STACK_BUFFER_H
+#define STACK_BUFFER_H
+
+#include "utils/palloc.h"
+
+/*
+ * The alloca()-based implementation uses alloca() in the argument list of a
+ * function call.  GCC, Clang and MSVC can do that, but historical compilers
+ * could not.
+ */
+#if defined(__GNUC__) || defined(_MSC_VER)
+#define USE_ALLOCA
+#endif
+
+/*
+ * Unixen traditionally either required <alloca.h> or <stdlib.h> to be included
+ * to use alloca().  It is not standardized by C or POSIX.  GCC and Clang
+ * understand it as a builtin, so we only need to include a header for MSVC.
+ */
+#if defined(WIN32) && defined(_MSC_VER)
+#include <malloc.h>
+#endif
+
+/*
+ * This should be large enough that most strings and other small objects will
+ * fit, but small enough that we feel comfortable putting it on the stack.
+ */
+#define STACK_BUFFER_SIZE 1024
+
+/*
+ * Declare a stack buffer, allowing the following API to be used to allocate
+ * memory that doesn't escape the present function.
+ */
+#define DECLARE_STACK_BUFFER() \
+	DECLARE_STACK_BUFFER_IMPL(STACK_BUFFER_SIZE)
+
+/*
+ * Allocate memory from the stack if the declared limit wouldn't be exceeded,
+ * and otherwise fall back to palloc().  The returned pointer is aligned to
+ * MAXIMUM_ALIGNOF (not max_align_t), like palloc().
+ */
+#define stack_buffer_alloc(size) \
+	(stack_buffer_tmp = (size), /* avoid double evaluation */ \
+	 stack_buffer_alloc_impl(stack_buffer_tmp))
+
+/* Like palloc_array(T, size). */
+#define stack_buffer_alloc_array(T, size) \
+	(T *) stack_buffer_alloc((size) * sizeof(T))
+
+/* Like palloc_object(T). */
+#define stack_buffer_alloc_object(T) \
+	stack_buffer_alloc_array(T, 1)
+
+/*
+ * Allocate a fresh NUL-terminated string copied from a string of known size,
+ * not counting the NUL-terminator.  The input string need not be
+ * NUL-terminated.
+ */
+#define stack_buffer_strdup_with_len(data, size) \
+	(stack_buffer_tmp = (size), /* avoid double evaluation */ \
+	 stack_buffer_strdup_with_len_impl(stack_buffer_alloc_impl(stack_buffer_tmp + 1), \
+									   (data), \
+									   stack_buffer_tmp))
+
+/*
+ * Free memory allocated with stack_buffer_alloc().  A no-op if it came from
+ * the stack, and otherwise pfree().
+ */
+#define stack_buffer_free(ptr) \
+	stack_buffer_free_impl(stack_buffer_l, stack_buffer_h, (ptr))
+
+
+
+#ifdef USE_ALLOCA
+/* alloca() with a size limit. */
+#define DECLARE_STACK_BUFFER_IMPL(size) \
+const size_t stack_buffer_max_size = (size); \
+char *stack_buffer_l = NULL; \
+char *stack_buffer_h = NULL; \
+size_t stack_buffer_tmp pg_attribute_unused()
+#define stack_buffer_alloc_impl(size) \
+	(((stack_buffer_h - stack_buffer_l) + (size)) < stack_buffer_max_size ? \
+	 stack_buffer_alloc_impl2(&stack_buffer_l, \
+							  &stack_buffer_h, \
+							  alloca(size)) : \
+	 palloc(size))
+static inline void *
+stack_buffer_alloc_impl2(char **l, char **h, void *ptr)
+{
+	char	   *p = (char *) ptr;
+
+	if (*l == NULL || p < *l)
+		*l = p;
+	if (*h == NULL || p > *h)
+		*h = p;
+
+	return p;
+}
+#else
+/* Standard C implementation using a big array. */
+#define DECLARE_STACK_BUFFER_IMPL(size) \
+alignas(MAXIMUM_ALIGNOF) char stack_buffer_array[MAXALIGN(size)]; \
+char *stack_buffer_allocator = stack_buffer_array + sizeof(MAXALIGN(size)); \
+const char *stack_buffer_l = stack_buffer_array; \
+const char *stack_buffer_h = stack_buffer_allocator; \
+size_t stack_buffer_tmp pg_attribute_unused()
+#define stack_buffer_alloc_impl(size) \
+	stack_buffer_alloc_impl2(stack_buffer_l, \
+							 &stack_buffer_allocator, \
+							 (size))
+static inline void *
+stack_buffer_alloc_impl2(const char *l, char **allocator, size_t size)
+{
+	size = MAXALIGN(size);
+
+	if (*allocator - size >= l)
+	{
+		*allocator -= size;
+		return *allocator;
+	}
+	return palloc(size);
+}
+#endif
+
+static inline char *
+stack_buffer_strdup_with_len_impl(char *dst, const char *data, size_t size)
+{
+	memcpy(dst, data, size);
+	dst[size] = 0;
+	return dst;
+}
+
+static inline void
+stack_buffer_free_impl(const char *l, const char *h, void *ptr)
+{
+	char	   *p = (char *) ptr;
+
+	if (p < l || p > h)
+		pfree(ptr);
+}
+
+#endif
-- 
2.50.1 (Apple Git-155)

