public inbox for [email protected]
help / color / mirror / Atom feedFrom: Thomas Munro <[email protected]>
To: PostgreSQL Hackers <[email protected]>
Subject: A stack allocation API
Date: Sat, 28 Feb 2026 00:59:42 +1300
Message-ID: <CA+hUKG+ixUUYOGRcuZpkk5pmJZaUQv6VCPAjdTZXFP5vA8jxcA@mail.gmail.com> (raw)
Hi,
In the locale code we often use a 1KB array for copies of strings
where we need a NUL-terminated or transcoded version to give a library
function, with a fallback to palloc() + pfree() if we need more space
than that, but:
* we open code it repeatedly
* we often have two allocations but won't use the stack if we can't fit both
* we don't use it in nearby places that are obviously similar,
probably because it's a bit tedious to repeat
* in the past we've forgotten to pfree() large allocations and had to fix leaks
* it's not very type-safe
* we don't seem to consider alignment for non-char types, eg UChar,
wchar_t (apparently ASAN has never complained about that and I think I
see why it's always OK as written, but I suspect that might be UB)
In the attached, I tried to tidy that up with an interface that lets you write:
DECLARE_STACK_BUFFER();
p = stack_buffer_alloc(n);
...
stack_buffer_free(p);
The point of the _free() call is that it might need to call pfree() if
it was a large allocation and not from the stack.
Or slightly higher level and supporting the most common use cases with
a one-liner:
cstr1 = stack_buffer_strdup_with_len(str1, len1);
cstr2 = stack_buffer_strdup_with_len(str2, len2);
result = strcoll_l(cstr1, cstr2, locale);
stack_buffer_free(cstr1);
stack_buffer_free(cstr2);
Or for non-char cases without casts or pointer/size arithmetic, in the
style of recent palloc() variants:
wcstr = stack_buffer_alloc_array(wchar_t, len);
uchar = stack_buffer_alloc_array(UChar, len);
Better names/ideas welcome.
I also wondered if we might have a reasonable case for using alloca(),
where available. It's pretty much the thing we are emulating, but
keeps the stack nice and compact without big holes to step over for
the following call to strcoll_l() or whatever it might be. Though
it's non-standard and often discouraged due to the inherent danger of
overflow, our usage is metered. I don't see why it's any more
dangerous than the existing code as long as our cap is applied to it,
or am I missing some other problem with that idea? One issue with
USE_ALLOCA is that we have no systems where that wouldn't be used, so
the fallback code would be untested unless you comment the #define
out...
Attachments:
[application/octet-stream] v1-0001-Provide-stack-allocation-API.patch (19.7K, 2-v1-0001-Provide-stack-allocation-API.patch)
download | inline diff:
From 97196948de301df9667d7bb4521806f6343e4d2f Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
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)
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: A stack allocation API
In-Reply-To: <CA+hUKG+ixUUYOGRcuZpkk5pmJZaUQv6VCPAjdTZXFP5vA8jxcA@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox