/*
 * SPDX-License-Identifier: ISC
 *
 * Copyright (C) 2018-2020 Michael Drake <tlsa@netsurf-browser.org>
 */

/**
 * \file
 * \brief CYAML memory allocation handling.
 */

#ifndef CYAML_MEM_H
#define CYAML_MEM_H

#include "cyaml/cyaml.h"

/**
 * Helper for freeing using the client's choice of allocator routine.
 *
 * \param[in]  config  The CYAML client config.
 * \param[in]  ptr     Pointer to allocation to free.
 */
static inline void cyaml__free(
		const cyaml_config_t *config,
		void *ptr)
{
	config->mem_fn(config->mem_ctx, ptr, 0);
}

/**
 * Helper for new allocations using the client's choice of allocator routine.
 *
 * \note On failure, any existing allocation is still owned by the caller, and
 *       they are responsible for freeing it.
 *
 * \param[in]  config        The CYAML client config.
 * \param[in]  ptr           The existing allocation or NULL.
 * \param[in]  current_size  Size of the current allocation.  (Only needed if
 *                           `clean != false`).
 * \param[in]  new_size      The number of bytes to resize allocation to.
 * \param[in]  clean         Only applies if `new_size > current_size`.
 *                           If `false`, the new memory is uninitialised,
 *                           if `true`, the new memory is initialised to zero.
 * \return Pointer to allocation on success, or `NULL` on failure.
 */
static inline void * cyaml__realloc(
		const cyaml_config_t *config,
		void *ptr,
		size_t current_size,
		size_t new_size,
		bool clean)
{
	uint8_t *temp = config->mem_fn(config->mem_ctx, ptr, new_size);
	if (temp == NULL) {
		return NULL;
	}

	if (clean && (new_size > current_size)) {
		memset(temp + current_size, 0, new_size - current_size);
	}

	return temp;
}

/**
 * Helper for new allocations using the client's choice of allocator routine.
 *
 * \param[in]  config  The CYAML client config.
 * \param[in]  size    The number of bytes to allocate.
 * \param[in]  clean   If `false`, the memory is uninitialised, if `true`,
 *                     the memory is initialised to zero.
 * \return Pointer to allocation on success, or `NULL` on failure.
 */
static inline void * cyaml__alloc(
		const cyaml_config_t *config,
		size_t size,
		bool clean)
{
	return cyaml__realloc(config, NULL, 0, size, clean);
}

/**
 * Helper for string duplication using the client's choice of allocator routine.
 *
 * \param[in]  config   The CYAML client config.
 * \param[in]  str      The string to duplicate.
 * \param[in]  len_out  If non-NULL, updated to length of string on success.
 * \return Pointer to new string on success, or `NULL` on failure.
 */
static inline char * cyaml__strdup(
		const cyaml_config_t *config,
		const char *str,
		size_t *len_out)
{
	size_t len = strlen(str) + 1;
	char *dup = cyaml__alloc(config, len, false);
	if (dup == NULL) {
		return NULL;
	}

	memcpy(dup, str, len);

	if (len_out != NULL) {
		*len_out = len - 1;
	}
	return dup;
}

#endif
