/* EINA - EFL data type library * Copyright (C) 2007-2008 Cedric BAIL, Carsten Haitzler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; * if not, see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #ifdef HAVE_EVIL # include #endif #include "eina_config.h" #include "eina_private.h" #include "eina_magic.h" #include "eina_inlist.h" #include "eina_mempool.h" #include "eina_list.h" #include "eina_trash.h" #include "eina_log.h" /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ #include "eina_safety_checks.h" #include "eina_rectangle.h" /*============================================================================* * Local * *============================================================================*/ /** * @cond LOCAL */ #define EINA_RECTANGLE_POOL_MAGIC 0x1578FCB0 #define EINA_RECTANGLE_ALLOC_MAGIC 0x1578FCB1 #define BUCKET_THRESHOLD 110 typedef struct _Eina_Rectangle_Alloc Eina_Rectangle_Alloc; struct _Eina_Rectangle_Pool { Eina_Inlist *head; Eina_List *empty; void *data; Eina_Trash *bucket; unsigned int bucket_count; unsigned int references; int w; int h; Eina_Bool sorted; EINA_MAGIC }; struct _Eina_Rectangle_Alloc { EINA_INLIST; Eina_Rectangle_Pool *pool; EINA_MAGIC }; #define EINA_MAGIC_CHECK_RECTANGLE_POOL(d) \ do { \ if (!EINA_MAGIC_CHECK((d), EINA_RECTANGLE_POOL_MAGIC)) { \ EINA_MAGIC_FAIL((d), EINA_RECTANGLE_POOL_MAGIC); } \ } while (0) #define EINA_MAGIC_CHECK_RECTANGLE_ALLOC(d) \ do { \ if (!EINA_MAGIC_CHECK((d), EINA_RECTANGLE_ALLOC_MAGIC)) { \ EINA_MAGIC_FAIL((d), EINA_RECTANGLE_ALLOC_MAGIC); } \ } while (0) static Eina_Mempool *_eina_rectangle_alloc_mp = NULL; static Eina_Mempool *_eina_rectangle_mp = NULL; static Eina_Trash *_eina_rectangles = NULL; static unsigned int _eina_rectangles_count = 0; static int _eina_rectangle_log_dom = -1; #ifdef ERR #undef ERR #endif #define ERR(...) EINA_LOG_DOM_ERR(_eina_rectangle_log_dom, __VA_ARGS__) #ifdef DBG #undef DBG #endif #define DBG(...) EINA_LOG_DOM_DBG(_eina_rectangle_log_dom, __VA_ARGS__) static int _eina_rectangle_cmp(const Eina_Rectangle *r1, const Eina_Rectangle *r2) { return (r2->w * r2->h) - (r1->w * r1->h); } static Eina_List * _eina_rectangle_merge_list(Eina_List *empty, Eina_Rectangle *r) { Eina_Rectangle *match; Eina_List *l; int xw; int yh; if (r->w == 0 || r->h == 0) { eina_rectangle_free(r); return empty; } start_again: xw = r->x + r->w; yh = r->y + r->h; EINA_LIST_FOREACH(empty, l, match) { if (match->x == r->x && match->w == r->w && (match->y == yh || r->y == match->y + match->h)) { if (match->y > r->y) match->y = r->y; match->h += r->h; eina_rectangle_free(r); empty = eina_list_remove_list(empty, l); r = match; goto start_again; } else if (match->y == r->y && match->h == r->h && (match->x == xw || r->x == match->x + match->w)) { if (match->x > r->x) match->x = r->x; match->w += r->w; eina_rectangle_free(r); empty = eina_list_remove_list(empty, l); r = match; goto start_again; } } return eina_list_append(empty, r); } static Eina_List * _eina_rectangle_empty_space_find(Eina_List *empty, int w, int h, int *x, int *y) { Eina_Rectangle *r; Eina_List *l; EINA_LIST_FOREACH(empty, l, r) { if (r->w >= w && r->h >= h) { /* Remove l from empty */ empty = eina_list_remove_list(empty, l); /* Remember x and y */ *x = r->x; *y = r->y; /* Split r in 2 rectangle if needed (only the empty one) and insert them */ if (r->w == w) { r->y += h; r->h -= h; } else if (r->h == h) { r->x += w; r->w -= w; } else { int rx1, ry1, rw1, rh1; int x2, y2, w2, h2; rx1 = r->x + w; ry1 = r->y; rw1 = r->w - w; /* h1 could be h or r->h */ x2 = r->x; y2 = r->y + h; /* w2 could be w or r->w */ h2 = r->h - h; if (rw1 * r->h > h2 * r->w) { rh1 = r->h; w2 = w; } else { rh1 = h; w2 = r->w; } EINA_RECTANGLE_SET(r, rx1, ry1, rw1, rh1); empty = _eina_rectangle_merge_list(empty, r); r = eina_rectangle_new(x2, y2, w2, h2); } if (r) { empty = _eina_rectangle_merge_list(empty, r); /* Return empty */ } return empty; } } *x = -1; *y = -1; return empty; } /** * @endcond */ /*============================================================================* * Global * *============================================================================*/ Eina_Bool eina_rectangle_init(void) { const char *choice, *tmp; _eina_rectangle_log_dom = eina_log_domain_register("eina_rectangle", EINA_LOG_COLOR_DEFAULT); if (_eina_rectangle_log_dom < 0) { EINA_LOG_ERR("Could not register log domain: eina_rectangle"); return EINA_FALSE; } #ifdef EINA_DEFAULT_MEMPOOL choice = "pass_through"; #else choice = "chained_mempool"; #endif tmp = getenv("EINA_MEMPOOL"); if (tmp && tmp[0]) choice = tmp; _eina_rectangle_alloc_mp = eina_mempool_add (choice, "rectangle-alloc", NULL, sizeof(Eina_Rectangle_Alloc) + sizeof(Eina_Rectangle), 64); if (!_eina_rectangle_alloc_mp) { ERR("Mempool for rectangle cannot be allocated in rectangle init."); goto init_error; } _eina_rectangle_mp = eina_mempool_add (choice, "rectangle", NULL, sizeof(Eina_Rectangle), 32); if (!_eina_rectangle_mp) { ERR("Mempool for rectangle cannot be allocated in rectangle init."); goto init_error; } return EINA_TRUE; init_error: eina_log_domain_unregister(_eina_rectangle_log_dom); _eina_rectangle_log_dom = -1; return EINA_FALSE; } Eina_Bool eina_rectangle_shutdown(void) { Eina_Rectangle *del; while ((del = eina_trash_pop(&_eina_rectangles))) eina_mempool_free(_eina_rectangle_mp, del); _eina_rectangles_count = 0; eina_mempool_del(_eina_rectangle_alloc_mp); eina_mempool_del(_eina_rectangle_mp); eina_log_domain_unregister(_eina_rectangle_log_dom); _eina_rectangle_log_dom = -1; return EINA_TRUE; } /*============================================================================* * API * *============================================================================*/ EAPI Eina_Rectangle * eina_rectangle_new(int x, int y, int w, int h) { Eina_Rectangle *rect; if (_eina_rectangles) { rect = eina_trash_pop(&_eina_rectangles); _eina_rectangles_count--; } else rect = eina_mempool_malloc(_eina_rectangle_mp, sizeof (Eina_Rectangle)); if (!rect) return NULL; EINA_RECTANGLE_SET(rect, x, y, w, h); return rect; } EAPI void eina_rectangle_free(Eina_Rectangle *rect) { EINA_SAFETY_ON_NULL_RETURN(rect); if (_eina_rectangles_count > BUCKET_THRESHOLD) eina_mempool_free(_eina_rectangle_mp, rect); else { eina_trash_push(&_eina_rectangles, rect); _eina_rectangles_count++; } } EAPI Eina_Rectangle_Pool * eina_rectangle_pool_new(int w, int h) { Eina_Rectangle_Pool *new; if ((w <= 0) || (h <= 0)) return NULL; new = malloc(sizeof (Eina_Rectangle_Pool)); if (!new) return NULL; new->head = NULL; new->empty = eina_list_append(NULL, eina_rectangle_new(0, 0, w, h)); new->references = 0; new->sorted = EINA_FALSE; new->w = w; new->h = h; new->bucket = NULL; new->bucket_count = 0; EINA_MAGIC_SET(new, EINA_RECTANGLE_POOL_MAGIC); DBG("pool=%p, size=(%d, %d)", new, w, h); return new; } EAPI void eina_rectangle_pool_free(Eina_Rectangle_Pool *pool) { Eina_Rectangle_Alloc *del; EINA_SAFETY_ON_NULL_RETURN(pool); DBG("pool=%p, size=(%d, %d), references=%u", pool, pool->w, pool->h, pool->references); while (pool->head) { del = (Eina_Rectangle_Alloc *)pool->head; pool->head = (EINA_INLIST_GET(del))->next; EINA_MAGIC_SET(del, EINA_MAGIC_NONE); eina_mempool_free(_eina_rectangle_alloc_mp, del); } while (pool->bucket) { del = eina_trash_pop(&pool->bucket); eina_mempool_free(_eina_rectangle_alloc_mp, del); } MAGIC_FREE(pool); } EAPI int eina_rectangle_pool_count(Eina_Rectangle_Pool *pool) { EINA_SAFETY_ON_NULL_RETURN_VAL(pool, 0); return pool->references; } EAPI Eina_Rectangle * eina_rectangle_pool_request(Eina_Rectangle_Pool *pool, int w, int h) { Eina_Rectangle_Alloc *new; Eina_Rectangle *rect; int x; int y; EINA_SAFETY_ON_NULL_RETURN_VAL(pool, NULL); DBG("pool=%p, size=(%d, %d), references=%u", pool, pool->w, pool->h, pool->references); if (w <= 0 || h <= 0) return NULL; if (w > pool->w || h > pool->h) return NULL; /* Sort empty if dirty */ if (pool->sorted) { pool->empty = eina_list_sort(pool->empty, 0, EINA_COMPARE_CB(_eina_rectangle_cmp)); pool->sorted = EINA_TRUE; } pool->empty = _eina_rectangle_empty_space_find(pool->empty, w, h, &x, &y); if (x == -1) return NULL; pool->sorted = EINA_FALSE; if (pool->bucket_count > 0) { new = eina_trash_pop(&pool->bucket); pool->bucket_count--; } else new = eina_mempool_malloc(_eina_rectangle_alloc_mp, sizeof (Eina_Rectangle_Alloc) + sizeof (Eina_Rectangle)); if (!new) return NULL; rect = (Eina_Rectangle *)(new + 1); eina_rectangle_coords_from(rect, x, y, w, h); pool->head = eina_inlist_prepend(pool->head, EINA_INLIST_GET(new)); pool->references++; new->pool = pool; EINA_MAGIC_SET(new, EINA_RECTANGLE_ALLOC_MAGIC); DBG("rect=%p pool=%p, size=(%d, %d), references=%u", rect, pool, pool->w, pool->h, pool->references); return rect; } EAPI void eina_rectangle_pool_release(Eina_Rectangle *rect) { Eina_Rectangle_Alloc *era = ((Eina_Rectangle_Alloc *)rect) - 1; Eina_Rectangle *r; EINA_SAFETY_ON_NULL_RETURN(rect); EINA_MAGIC_CHECK_RECTANGLE_ALLOC(era); EINA_MAGIC_CHECK_RECTANGLE_POOL(era->pool); DBG("rect=%p pool=%p, size=(%d, %d), references=%u", rect, era->pool, era->pool->w, era->pool->h, era->pool->references); era->pool->references--; era->pool->head = eina_inlist_remove(era->pool->head, EINA_INLIST_GET(era)); r = eina_rectangle_new(rect->x, rect->y, rect->w, rect->h); if (r) { era->pool->empty = _eina_rectangle_merge_list(era->pool->empty, r); era->pool->sorted = EINA_FALSE; } if (era->pool->bucket_count < BUCKET_THRESHOLD) { Eina_Rectangle_Pool *pool; pool = era->pool; pool->bucket_count++; eina_trash_push(&pool->bucket, era); } else { EINA_MAGIC_SET(era, EINA_MAGIC_NONE); eina_mempool_free(_eina_rectangle_alloc_mp, era); } } EAPI Eina_Rectangle_Pool * eina_rectangle_pool_get(Eina_Rectangle *rect) { Eina_Rectangle_Alloc *era = ((Eina_Rectangle_Alloc *)rect) - 1; EINA_SAFETY_ON_NULL_RETURN_VAL(rect, NULL); EINA_MAGIC_CHECK_RECTANGLE_ALLOC(era); EINA_MAGIC_CHECK_RECTANGLE_POOL(era->pool); return era->pool; } EAPI void eina_rectangle_pool_data_set(Eina_Rectangle_Pool *pool, const void *data) { EINA_MAGIC_CHECK_RECTANGLE_POOL(pool); EINA_SAFETY_ON_NULL_RETURN(pool); DBG("data=%p pool=%p, size=(%d, %d), references=%u", data, pool, pool->w, pool->h, pool->references); pool->data = (void *)data; } EAPI void * eina_rectangle_pool_data_get(Eina_Rectangle_Pool *pool) { EINA_MAGIC_CHECK_RECTANGLE_POOL(pool); EINA_SAFETY_ON_NULL_RETURN_VAL(pool, NULL); return pool->data; } EAPI Eina_Bool eina_rectangle_pool_geometry_get(Eina_Rectangle_Pool *pool, int *w, int *h) { if (!pool) return EINA_FALSE; EINA_MAGIC_CHECK_RECTANGLE_POOL(pool); EINA_SAFETY_ON_NULL_RETURN_VAL(pool, EINA_FALSE); if (w) *w = pool->w; if (h) *h = pool->h; return EINA_TRUE; }