/*

Buddy allocator

Copyright (c) 2021 Aron Barath
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. Neither the name of the Author nor the names of contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

Source code: svn://svn.repo.hu/libualloc/trunk
Contact the author: aron-dev@mailbox.org

*/

#ifndef BUDDY_H
#define BUDDY_H

#include <stdlib.h>
#include <libualloc/libualloc.h>

/* Can be redefined by the caller to make addresses smaller */
#ifndef UALL_BUDDY_ADDR_T
#	define UALL_BUDDY_ADDR_T size_t
#endif

typedef UALL_BUDDY_ADDR_T uall_buddy_addr_t;
typedef uall_buddy_addr_t uall_buddy_size_t;
typedef uall_buddy_addr_t buddy_segm_t;
typedef unsigned char uall_buddy_byte_t;

typedef struct uall_bchunk_s {
	uall_buddy_addr_t next;
	uall_buddy_addr_t prev;
} uall_bchunk_t;

typedef struct aull_buddy1_s uall_buddy1_t;
struct aull_buddy1_s {
	uall_buddy1_t *next;          /* next buddy allocator (only used in "uall" mode) */
	uall_buddy_addr_t memory;     /* base address of the allocatable memory */
	uall_buddy_addr_t chunks_info;/* size and reservation information about chunks */
	uall_buddy_size_t  total_size;     /* total size of the allocatable memory (will not changed) */
	uall_buddy_size_t  act_max_size;   /* maximal size of the actually allocatable chunk */
	uall_buddy_size_t  min_size;       /* minimal possible chunk size to allocate */
	uall_buddy_size_t  num_minchunks;  /* total number of min-sized chunk (size of the chunks_info) */
	uall_buddy_byte_t  min_bits;       /* log2 of min_size */
	uall_buddy_byte_t  num_levels;     /* total number of levels */
	uall_buddy_byte_t  max_bits;       /* power of the largest block */
	uall_buddy_byte_t  padding;        /* (really, just padding) */
	uall_bchunk_t chunk_lists[1];      /* list of free chunks */
};

/* this is the core buddy allocator -- the whole state can be copied */

UALL_INLINE uall_buddy1_t *uall_buddy_init_(uall_buddy1_t *const buddy, uall_buddy_size_t size, uall_buddy_size_t minsize);
UALL_INLINE uall_buddy_addr_t uall_buddy_alloc_(uall_buddy1_t *const buddy, uall_buddy_size_t size);
UALL_INLINE void uall_buddy_free_(uall_buddy1_t *const buddy, uall_buddy_addr_t block);
UALL_INLINE uall_buddy_size_t uall_buddy_msize(uall_buddy1_t *const buddy, uall_buddy_addr_t block);

/* Convert a global pointer (void*) to a local address (uall_buddy_addr_t).
   This macro works only if the global pointer is the member of the given
   buddy allocator base. */
#define uall_buddy_gp2la(_base_, _global_pointer_) \
	((uall_buddy_addr_t)( \
		((uall_buddy_byte_t*)(_global_pointer_)) - ((uall_buddy_byte_t*)(_base_)) \
	))

/* Converts a local address (uall_buddy_addr_t) to a global pointer (void*).
   Same restrictions apply as to uall_buddy_gp2la(). */
#define uall_buddy_la2gp(_base_, _local_address_) \
	((void*)( \
		((uall_buddy_byte_t*)(_base_)) + ((uall_buddy_addr_t)(_local_address_)) \
	))

typedef struct uall_buddy_s {
	/*** user configuration and callbacks ***/
	uall_sysalloc_t *sys;

	void *user_handle;                               /* optional: can be used by caller supplied ->grow() to keep track of allocation handle */
	size_t user_size;                                /* optional: can be used by caller supplied ->grow() to keep track of allocation size */

	void *user_data;                                 /* arbitrary data set by the caller */


	/*** internal state ***/
	uall_buddy1_t *_first;
	uall_buddy1_t *_last;
	buddy_segm_t _nsegments;
	size_t _maxsize;
} uall_buddy_t;

typedef struct uall_buddy_va_s {
	buddy_segm_t segm;
	uall_buddy_addr_t addr;
} uall_buddy_va_t;

void uall_buddy_init(uall_buddy_t *ctx);

/* restore a previously exported segment -- in fact, this is just appends
   to the list of known buddy allocator cores */
UALL_INLINE void uall_buddy_restore_segment(uall_buddy_t *ctx, void *base);

UALL_INLINE void *uall_buddy_alloc(uall_buddy_t *ctx, size_t size);
UALL_INLINE void uall_buddy_free(uall_buddy_t *ctx, void *ptr);

/* returns zero on success */
UALL_INLINE int uall_buddy_alloc_va(uall_buddy_t *ctx, size_t size, uall_buddy_va_t *const va);
UALL_INLINE int uall_buddy_free_va(uall_buddy_t *ctx, uall_buddy_va_t *const va);

UALL_INLINE void *uall_buddy_va2ptr(uall_buddy_t *ctx, const uall_buddy_va_t *const va);
UALL_INLINE int uall_buddy_ptr2va(uall_buddy_t *ctx, uall_buddy_va_t *const va, void *ptr);

#define UALL_BUDDY_RESERVED_BIT           0x80
#define UALL_BUDDY_LEVELS_MASK            0x7f

#endif /* BUDDY_H */
