blob: 8f03f714011ad964e0999c610451a705b56feffe [file]
/*-------------------------------------------------------------------------
*
* fe_memutils.c
* memory management support for frontend code
*
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/common/fe_memutils.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#error "This file is not expected to be compiled for backend code"
#endif
#include "postgres_fe.h"
#include "common/int.h"
static pg_noinline void add_size_error(Size s1, Size s2) pg_attribute_noreturn();
static pg_noinline void mul_size_error(Size s1, Size s2) pg_attribute_noreturn();
static inline void *
pg_malloc_internal(size_t size, int flags)
{
void *tmp;
/* Avoid unportable behavior of malloc(0) */
if (size == 0)
size = 1;
tmp = malloc(size);
if (tmp == NULL)
{
if ((flags & MCXT_ALLOC_NO_OOM) == 0)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
return NULL;
}
if ((flags & MCXT_ALLOC_ZERO) != 0)
MemSet(tmp, 0, size);
return tmp;
}
void *
pg_malloc(size_t size)
{
return pg_malloc_internal(size, 0);
}
void *
pg_malloc0(size_t size)
{
return pg_malloc_internal(size, MCXT_ALLOC_ZERO);
}
void *
pg_malloc_extended(size_t size, int flags)
{
return pg_malloc_internal(size, flags);
}
void *
pg_realloc(void *ptr, size_t size)
{
void *tmp;
/* Avoid unportable behavior of realloc(NULL, 0) */
if (ptr == NULL && size == 0)
size = 1;
tmp = realloc(ptr, size);
if (!tmp)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
return tmp;
}
/*
* "Safe" wrapper around strdup().
*/
char *
pg_strdup(const char *in)
{
char *tmp;
if (!in)
{
fprintf(stderr,
_("cannot duplicate null pointer (internal error)\n"));
exit(EXIT_FAILURE);
}
tmp = strdup(in);
if (!tmp)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
return tmp;
}
void
pg_free(void *ptr)
{
if (ptr != NULL)
free(ptr);
}
/*
* Frontend emulation of backend memory management functions. Useful for
* programs that compile backend files.
*/
void *
palloc(Size size)
{
return pg_malloc_internal(size, 0);
}
void *
palloc0(Size size)
{
return pg_malloc_internal(size, MCXT_ALLOC_ZERO);
}
void *
palloc_extended(Size size, int flags)
{
return pg_malloc_internal(size, flags);
}
void
pfree(void *pointer)
{
pg_free(pointer);
}
char *
pstrdup(const char *in)
{
return pg_strdup(in);
}
char *
pnstrdup(const char *in, Size size)
{
char *tmp;
int len;
if (!in)
{
fprintf(stderr,
_("cannot duplicate null pointer (internal error)\n"));
exit(EXIT_FAILURE);
}
len = strnlen(in, size);
tmp = malloc(len + 1);
if (tmp == NULL)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
memcpy(tmp, in, len);
tmp[len] = '\0';
return tmp;
}
void *
repalloc(void *pointer, Size size)
{
return pg_realloc(pointer, size);
}
/*
* Support for safe calculation of memory request sizes
*
* These functions perform the requested calculation, but throw error if the
* result overflows.
*
* An important property of these functions is that if an argument was a
* negative signed int before promotion (implying overflow in calculating it)
* we will detect that as an error. That happens because we reject results
* larger than SIZE_MAX / 2. In the backend we rely on later checks to do
* that, but in frontend we must do it here.
*/
Size
add_size(Size s1, Size s2)
{
Size result;
if (unlikely(pg_add_size_overflow(s1, s2, &result) ||
result > (SIZE_MAX / 2)))
add_size_error(s1, s2);
return result;
}
static pg_noinline void
add_size_error(Size s1, Size s2)
{
fprintf(stderr, _("invalid memory allocation request size %zu + %zu\n"),
s1, s2);
exit(EXIT_FAILURE);
}
Size
mul_size(Size s1, Size s2)
{
Size result;
if (unlikely(pg_mul_size_overflow(s1, s2, &result) ||
result > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return result;
}
static pg_noinline void
mul_size_error(Size s1, Size s2)
{
fprintf(stderr, _("invalid memory allocation request size %zu * %zu\n"),
s1, s2);
exit(EXIT_FAILURE);
}
/*
* pg_malloc_mul
* Equivalent to pg_malloc(mul_size(s1, s2)).
*/
void *
pg_malloc_mul(Size s1, Size s2)
{
/* inline mul_size() for efficiency */
Size req;
if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
req > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return pg_malloc(req);
}
/*
* pg_malloc0_mul
* Equivalent to pg_malloc0(mul_size(s1, s2)).
*
* This is comparable to standard calloc's behavior.
*/
void *
pg_malloc0_mul(Size s1, Size s2)
{
/* inline mul_size() for efficiency */
Size req;
if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
req > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return pg_malloc0(req);
}
/*
* pg_malloc_mul_extended
* Equivalent to pg_malloc_extended(mul_size(s1, s2), flags).
*/
void *
pg_malloc_mul_extended(Size s1, Size s2, int flags)
{
/* inline mul_size() for efficiency */
Size req;
if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
req > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return pg_malloc_extended(req, flags);
}
/*
* pg_realloc_mul
* Equivalent to pg_realloc(p, mul_size(s1, s2)).
*/
void *
pg_realloc_mul(void *p, Size s1, Size s2)
{
/* inline mul_size() for efficiency */
Size req;
if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
req > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return pg_realloc(p, req);
}
/*
* palloc_mul
* Equivalent to palloc(mul_size(s1, s2)).
*/
void *
palloc_mul(Size s1, Size s2)
{
/* inline mul_size() for efficiency */
Size req;
if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
req > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return palloc(req);
}
/*
* palloc0_mul
* Equivalent to palloc0(mul_size(s1, s2)).
*
* This is comparable to standard calloc's behavior.
*/
void *
palloc0_mul(Size s1, Size s2)
{
/* inline mul_size() for efficiency */
Size req;
if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
req > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return palloc0(req);
}
/*
* palloc_mul_extended
* Equivalent to palloc_extended(mul_size(s1, s2), flags).
*/
void *
palloc_mul_extended(Size s1, Size s2, int flags)
{
/* inline mul_size() for efficiency */
Size req;
if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
req > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return palloc_extended(req, flags);
}
/*
* repalloc_mul
* Equivalent to repalloc(p, mul_size(s1, s2)).
*/
void *
repalloc_mul(void *p, Size s1, Size s2)
{
/* inline mul_size() for efficiency */
Size req;
if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
req > (SIZE_MAX / 2)))
mul_size_error(s1, s2);
return repalloc(p, req);
}