| /*------------------------------------------------------------------------- |
| * |
| * 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); |
| } |