/*-------------------------------------------------------------------------
 *
 * memutils.h
 *	  This file contains declarations for memory allocation utility
 *	  functions.  These are functions that are not quite widely used
 *	  enough to justify going in utils/palloc.h, but are still part
 *	  of the API of the memory management subsystem.
 *
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/memutils.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef MEMUTILS_H
#define MEMUTILS_H

#include "nodes/memnodes.h"


/*
 * MaxAllocSize, MaxAllocHugeSize
 *		Quasi-arbitrary limits on size of allocations.
 *
 * Note:
 *		There is no guarantee that smaller allocations will succeed, but
 *		larger requests will be summarily denied.
 *
 * palloc() enforces MaxAllocSize, chosen to correspond to the limiting size
 * of varlena objects under TOAST.  See VARSIZE_4B() and related macros in
 * postgres.h.  Many datatypes assume that any allocatable size can be
 * represented in a varlena header.  This limit also permits a caller to use
 * an "int" variable for an index into or length of an allocation.  Callers
 * careful to avoid these hazards can access the higher limit with
 * MemoryContextAllocHuge().  Both limits permit code to assume that it may
 * compute twice an allocation's size without overflow.
 */
#define MaxAllocSize	((Size) 0x3fffffff) /* 1 gigabyte - 1 */

#define AllocSizeIsValid(size)	((Size) (size) <= MaxAllocSize)

/* Do not make this any bigger; see add_size() and mul_size() */
#define MaxAllocHugeSize	(SIZE_MAX / 2)

#define InvalidAllocSize	SIZE_MAX

#define AllocHugeSizeIsValid(size)	((Size) (size) <= MaxAllocHugeSize)


/*
 * Standard top-level memory contexts.
 *
 * Only TopMemoryContext and ErrorContext are initialized by
 * MemoryContextInit() itself.
 */
extern PGDLLIMPORT MemoryContext TopMemoryContext;
extern PGDLLIMPORT MemoryContext ErrorContext;
extern PGDLLIMPORT MemoryContext PostmasterContext;
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
extern PGDLLIMPORT MemoryContext MessageContext;
extern PGDLLIMPORT MemoryContext TopTransactionContext;
extern PGDLLIMPORT MemoryContext CurTransactionContext;

/* This is a transient link to the active portal's memory context: */
extern PGDLLIMPORT MemoryContext PortalContext;


/*
 * Memory-context-type-independent functions in mcxt.c
 */
extern void MemoryContextInit(void);
extern void MemoryContextReset(MemoryContext context);
extern void MemoryContextDelete(MemoryContext context);
extern void MemoryContextResetOnly(MemoryContext context);
extern void MemoryContextResetChildren(MemoryContext context);
extern void MemoryContextDeleteChildren(MemoryContext context);
extern void MemoryContextSetIdentifier(MemoryContext context, const char *id);
extern void MemoryContextSetParent(MemoryContext context,
								   MemoryContext new_parent);
extern MemoryContext GetMemoryChunkContext(void *pointer);
extern Size GetMemoryChunkSpace(void *pointer);
extern MemoryContext MemoryContextGetParent(MemoryContext context);
extern bool MemoryContextIsEmpty(MemoryContext context);
extern Size MemoryContextMemAllocated(MemoryContext context, bool recurse);
extern void MemoryContextMemConsumed(MemoryContext context,
									 MemoryContextCounters *consumed);
extern void MemoryContextStats(MemoryContext context);
extern void MemoryContextStatsDetail(MemoryContext context,
									 int max_level, int max_children,
									 bool print_to_stderr);
extern void MemoryContextAllowInCriticalSection(MemoryContext context,
												bool allow);

#ifdef MEMORY_CONTEXT_CHECKING
extern void MemoryContextCheck(MemoryContext context);
#endif

/* Handy macro for copying and assigning context ID ... but note double eval */
#define MemoryContextCopyAndSetIdentifier(cxt, id) \
	MemoryContextSetIdentifier(cxt, MemoryContextStrdup(cxt, id))

extern void HandleLogMemoryContextInterrupt(void);
extern void ProcessLogMemoryContextInterrupt(void);

/*
 * Memory-context-type-specific functions
 */

/* aset.c */
extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
												   const char *name,
												   Size minContextSize,
												   Size initBlockSize,
												   Size maxBlockSize);

/*
 * This wrapper macro exists to check for non-constant strings used as context
 * names; that's no longer supported.  (Use MemoryContextSetIdentifier if you
 * want to provide a variable identifier.)
 */
#ifdef HAVE__BUILTIN_CONSTANT_P
#define AllocSetContextCreate(parent, name, ...) \
	(StaticAssertExpr(__builtin_constant_p(name), \
					  "memory context names must be constant strings"), \
	 AllocSetContextCreateInternal(parent, name, __VA_ARGS__))
#else
#define AllocSetContextCreate \
	AllocSetContextCreateInternal
#endif

/* slab.c */
extern MemoryContext SlabContextCreate(MemoryContext parent,
									   const char *name,
									   Size blockSize,
									   Size chunkSize);

/* generation.c */
extern MemoryContext GenerationContextCreate(MemoryContext parent,
											 const char *name,
											 Size minContextSize,
											 Size initBlockSize,
											 Size maxBlockSize);

/* bump.c */
extern MemoryContext BumpContextCreate(MemoryContext parent,
									   const char *name,
									   Size minContextSize,
									   Size initBlockSize,
									   Size maxBlockSize);

/*
 * Recommended default alloc parameters, suitable for "ordinary" contexts
 * that might hold quite a lot of data.
 */
#define ALLOCSET_DEFAULT_MINSIZE   0
#define ALLOCSET_DEFAULT_INITSIZE  (8 * 1024)
#define ALLOCSET_DEFAULT_MAXSIZE   (8 * 1024 * 1024)
#define ALLOCSET_DEFAULT_SIZES \
	ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE

/*
 * Recommended alloc parameters for "small" contexts that are never expected
 * to contain much data (for example, a context to contain a query plan).
 */
#define ALLOCSET_SMALL_MINSIZE	 0
#define ALLOCSET_SMALL_INITSIZE  (1 * 1024)
#define ALLOCSET_SMALL_MAXSIZE	 (8 * 1024)
#define ALLOCSET_SMALL_SIZES \
	ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE

/*
 * Recommended alloc parameters for contexts that should start out small,
 * but might sometimes grow big.
 */
#define ALLOCSET_START_SMALL_SIZES \
	ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE


/*
 * Threshold above which a request in an AllocSet context is certain to be
 * allocated separately (and thereby have constant allocation overhead).
 * Few callers should be interested in this, but tuplesort/tuplestore need
 * to know it.
 */
#define ALLOCSET_SEPARATE_THRESHOLD  8192

#define SLAB_DEFAULT_BLOCK_SIZE		(8 * 1024)
#define SLAB_LARGE_BLOCK_SIZE		(8 * 1024 * 1024)

/*
 * pg_memory_is_all_zeros
 *
 * Test if a memory region starting at "ptr" and of size "len" is full of
 * zeroes.
 *
 * The test is divided into multiple cases for safety reason and multiple
 * phases for efficiency.
 *
 * Case 1: len < sizeof(size_t) bytes, then byte-by-byte comparison.
 * Case 2: len < (sizeof(size_t) * 8 - 1) bytes:
 *       - Phase 1: byte-by-byte comparison, until the pointer is aligned.
 *       - Phase 2: size_t comparisons, with aligned pointers, up to the last
 *                  location possible.
 *       - Phase 3: byte-by-byte comparison, until the end location.
 * Case 3: len >= (sizeof(size_t) * 8) bytes, same as case 2 except that an
 *         additional phase is placed between Phase 1 and Phase 2, with
 *         (8 * sizeof(size_t)) comparisons using bitwise OR to encourage
 *         compilers to use SIMD instructions if available, up to the last
 *         aligned location possible.
 *
 * Case 1 and Case 2 are mandatory to ensure that we won't read beyond the
 * memory area.  This is portable for 32-bit and 64-bit architectures.
 *
 * Caller must ensure that "ptr" is not NULL.
 */
static inline bool
pg_memory_is_all_zeros(const void *ptr, size_t len)
{
	const unsigned char *p = (const unsigned char *) ptr;
	const unsigned char *end = &p[len];
	const unsigned char *aligned_end = (const unsigned char *)
		((uintptr_t) end & (~(sizeof(size_t) - 1)));

	if (len < sizeof(size_t))
	{
		while (p < end)
		{
			if (*p++ != 0)
				return false;
		}
		return true;
	}

	/* "len" in the [sizeof(size_t), sizeof(size_t) * 8 - 1] range */
	if (len < sizeof(size_t) * 8)
	{
		/* Compare bytes until the pointer "p" is aligned */
		while (((uintptr_t) p & (sizeof(size_t) - 1)) != 0)
		{
			if (p == end)
				return true;
			if (*p++ != 0)
				return false;
		}

		/*
		 * Compare remaining size_t-aligned chunks.
		 *
		 * There is no risk to read beyond the memory area, as "aligned_end"
		 * cannot be higher than "end".
		 */
		for (; p < aligned_end; p += sizeof(size_t))
		{
			if (*(size_t *) p != 0)
				return false;
		}

		/* Compare remaining bytes until the end */
		while (p < end)
		{
			if (*p++ != 0)
				return false;
		}
		return true;
	}

	/* "len" in the [sizeof(size_t) * 8, inf) range */

	/* Compare bytes until the pointer "p" is aligned */
	while (((uintptr_t) p & (sizeof(size_t) - 1)) != 0)
	{
		if (p == end)
			return true;

		if (*p++ != 0)
			return false;
	}

	/*
	 * Compare 8 * sizeof(size_t) chunks at once.
	 *
	 * For performance reasons, we manually unroll this loop and purposefully
	 * use bitwise-ORs to combine each comparison.  This prevents boolean
	 * short-circuiting and lets the compiler know that it's safe to access
	 * all 8 elements regardless of the result of the other comparisons.  This
	 * seems to be enough to coax a few compilers into using SIMD
	 * instructions.
	 */
	for (; p < aligned_end - (sizeof(size_t) * 7); p += sizeof(size_t) * 8)
	{
		if ((((size_t *) p)[0] != 0) | (((size_t *) p)[1] != 0) |
			(((size_t *) p)[2] != 0) | (((size_t *) p)[3] != 0) |
			(((size_t *) p)[4] != 0) | (((size_t *) p)[5] != 0) |
			(((size_t *) p)[6] != 0) | (((size_t *) p)[7] != 0))
			return false;
	}

	/*
	 * Compare remaining size_t-aligned chunks.
	 *
	 * There is no risk to read beyond the memory area, as "aligned_end"
	 * cannot be higher than "end".
	 */
	for (; p < aligned_end; p += sizeof(size_t))
	{
		if (*(size_t *) p != 0)
			return false;
	}

	/* Compare remaining bytes until the end */
	while (p < end)
	{
		if (*p++ != 0)
			return false;
	}

	return true;
}

#endif							/* MEMUTILS_H */
