diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-10-16 15:49:30 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2010-10-16 15:52:29 +0200 |
commit | b7e3cf472340c1abb2aaa06768517ba3e8909d1d (patch) | |
tree | d6d83c71f0568edc8b943f9570753ddb2dae9492 /tests | |
parent | 896d2321f3196c5ab373f4be3764c4da754322f8 (diff) | |
download | gnutls-b7e3cf472340c1abb2aaa06768517ba3e8909d1d.tar.gz |
Added tests/suite which contains tests to be executed during
development time and will not be distributed (not included in make dist).
Added "ecore" and a new mini-eagain to test EAGAIN behavior.
Diffstat (limited to 'tests')
126 files changed, 44933 insertions, 3 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index 4a42a098d6..15d03a4427 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -96,3 +96,6 @@ TESTS_ENVIRONMENT = \ EXEEXT=$(EXEEXT) \ srcdir="$(srcdir)" \ $(VALGRIND) + +check-local: + [ -d suite ] && $(MAKE) -C suite check diff --git a/tests/safe-renegotiation/Makefile.am b/tests/safe-renegotiation/Makefile.am index e175c78c37..a76e437295 100644 --- a/tests/safe-renegotiation/Makefile.am +++ b/tests/safe-renegotiation/Makefile.am @@ -31,7 +31,5 @@ check_PROGRAMS = $(ctests) TESTS = $(ctests) TESTS_ENVIRONMENT = $(VALGRIND) -EXTRA_DIST = README params.dh +EXTRA_DIST = README -dist_check_SCRIPTS = testsrn -#TESTS = testsrn diff --git a/tests/suite/Makefile.am b/tests/suite/Makefile.am new file mode 100644 index 0000000000..184acbe759 --- /dev/null +++ b/tests/suite/Makefile.am @@ -0,0 +1,79 @@ +## Process this file with automake to produce Makefile.in +# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. +# +# Author: Simon Josefsson +# +# This file is part of GnuTLS. +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This file 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this file; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +EXTRA_DIST = params.dh + +AM_CFLAGS = $(WARN_CFLAGS) +AM_CPPFLAGS = \ + -I$(top_srcdir)/gl \ + -I$(top_builddir)/gl \ + -I$(top_srcdir)/lib/includes \ + -I$(top_builddir)/lib/includes \ + -I$(top_srcdir)/libextra/includes \ + -I$(top_builddir)/libextra/includes \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/doc/examples \ + -I$(top_srcdir)/tests/suite/ecore/src/include \ + -I$(top_srcdir)/tests/suite/ecore/src/lib + +AM_LDFLAGS = -no-install +LDADD = ../../lib/libgnutls.la \ + ../../gl/libgnu.la \ + ../libutils.la \ + $(LTLIBGCRYPT) \ + $(LIBSOCKET) $(INET_NTOP_LIB) $(INET_PTON_LIB) + +noinst_LTLIBRARIES = libecore.la + +libecore_la_CPPFLAGS = -I$(top_srcdir)/tests/suite/ecore/ -I$(top_srcdir)/tests/suite/ecore/src/include \ + -I$(top_srcdir)/tests/suite/ecore/src/lib -DHAVE_CONFIG_H -D__UNUSED__= -DVMAJ=1 \ + -D VMIC=0 -DVMIN=0 -DVREV=0 -DEFL_HAVE_POSIX_THREADS=1 -DEFL_HAVE_POSIX_THREADS_SPINLOCK=1 \ + -DEFL_HAVE_THREADS=1 -DEINA_BUILD_CHAINED_POOL=1 -DEINA_STATIC_BUILD_CHAINED_POOL=1 \ + -DHAVE_CLOCK_GETTIME -DHAVE_GETTIMEOFDAY -DPACKAGE_LIB_DIR=\"/usr/lib\" -DMODULE_ARCH=\"unix\" \ + -DSHARED_LIB_SUFFIX=\".so\" +libecore_la_CFLAGS = -w +libecore_la_SOURCES = ecore/src/lib/ecore_anim.c ecore/src/lib/ecore_app.c \ + ecore/src/lib/ecore.c ecore/src/lib/ecore_events.c ecore/src/lib/ecore_exe.c \ + ecore/src/lib/ecore_getopt.c ecore/src/lib/ecore_glib.c ecore/src/lib/ecore_idle_enterer.c \ + ecore/src/lib/ecore_idle_exiter.c ecore/src/lib/ecore_idler.c ecore/src/lib/ecore_job.c \ + ecore/src/lib/ecore_main.c ecore/src/lib/ecore_pipe.c ecore/src/lib/ecore_poll.c \ + ecore/src/lib/ecore_signal.c ecore/src/lib/ecore_thread.c ecore/src/lib/ecore_time.c \ + ecore/src/lib/ecore_timer.c ecore/src/lib/eina_accessor.c ecore/src/lib/eina_array.c \ + ecore/src/lib/eina_benchmark.c ecore/src/lib/eina_binshare.c ecore/src/lib/eina_chained_mempool.c \ + ecore/src/lib/eina_convert.c ecore/src/lib/eina_counter.c ecore/src/lib/eina_cpu.c ecore/src/lib/eina_error.c \ + ecore/src/lib/eina_file.c ecore/src/lib/eina_fp.c ecore/src/lib/eina_hamster.c ecore/src/lib/eina_hash.c \ + ecore/src/lib/eina_inlist.c ecore/src/lib/eina_iterator.c ecore/src/lib/eina_lalloc.c ecore/src/lib/eina_list.c \ + ecore/src/lib/eina_log.c ecore/src/lib/eina_magic.c ecore/src/lib/eina_main.c ecore/src/lib/eina_matrixsparse.c \ + ecore/src/lib/eina_mempool.c ecore/src/lib/eina_module.c ecore/src/lib/eina_quadtree.c ecore/src/lib/eina_rbtree.c \ + ecore/src/lib/eina_rectangle.c ecore/src/lib/eina_safety_checks.c ecore/src/lib/eina_sched.c ecore/src/lib/eina_share_common.c \ + ecore/src/lib/eina_strbuf.c ecore/src/lib/eina_strbuf_common.c ecore/src/lib/eina_str.c \ + ecore/src/lib/eina_stringshare.c ecore/src/lib/eina_tiler.c ecore/src/lib/eina_unicode.c \ + ecore/src/lib/eina_ustrbuf.c ecore/src/lib/eina_ustringshare.c ecore/src/lib/eina_value.c + +eagain_cli_LDADD = $(LDADD) libecore.la -lrt -lm +eagain_cli_SOURCES = mini-eagain2.c + +noinst_PROGRAMS = eagain-cli + +dist_check_SCRIPTS = eagain #testsrn + +TESTS = eagain #testsrn diff --git a/tests/suite/README b/tests/suite/README new file mode 100644 index 0000000000..ece7004188 --- /dev/null +++ b/tests/suite/README @@ -0,0 +1,2 @@ +Here are tests that will not be distributed with a release. Only +done during development. diff --git a/tests/suite/eagain b/tests/suite/eagain new file mode 100755 index 0000000000..0acf21bdd5 --- /dev/null +++ b/tests/suite/eagain @@ -0,0 +1,43 @@ +#!/bin/sh + +# Copyright (C) 2006, 2007, 2008, 2010 Free Software Foundation, Inc. +# +# Author: Simon Josefsson +# +# This file is part of GnuTLS. +# +# GnuTLS is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at +# your option) any later version. +# +# GnuTLS 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GnuTLS; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +srcdir="${srcdir:-.}" +SERV="${SERV:-../../src/gnutls-serv$EXEEXT} -q" +PORT="${PORT:-4445}" + + +$SERV -p $PORT --echo --priority "NORMAL:+ANON-DH" --dhparams $srcdir/params.dh >/dev/null 2>&1 & +pid=$! + +sleep 2 + +./eagain-cli +if [ $? != 0 ];then + exit 1 +fi + +if [ "$pid" != "" ];then +kill $pid +wait +fi + +exit 0 diff --git a/tests/suite/ecore/eina_config.h b/tests/suite/ecore/eina_config.h new file mode 100644 index 0000000000..f89327d6a6 --- /dev/null +++ b/tests/suite/ecore/eina_config.h @@ -0,0 +1,52 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_CONFIG_H_ +#define EINA_CONFIG_H_ + +#ifdef EINA_MAGIC_DEBUG +# undef EINA_MAGIC_DEBUG +#endif +#define EINA_MAGIC_DEBUG + +#ifdef EINA_DEFAULT_MEMPOOL +# undef EINA_DEFAULT_MEMPOOL +#endif + + +#ifdef EINA_SAFETY_CHECKS +# undef EINA_SAFETY_CHECKS +#endif +#define EINA_SAFETY_CHECKS + +#ifdef EINA_HAVE_INTTYPES_H +# undef EINA_HAVE_INTTYPES_H +#endif +#define EINA_HAVE_INTTYPES_H + +#ifdef EINA_HAVE_STDINT_H +# undef EINA_HAVE_STDINT_H +#endif +#define EINA_HAVE_STDINT_H + +#ifdef EINA_SIZEOF_WCHAR_T +# undef EINA_SIZEOF_WCHAR_T +#endif +#define EINA_SIZEOF_WCHAR_T 4 + +#endif /* EINA_CONFIG_H_ */ diff --git a/tests/suite/ecore/src/include/Eina.h b/tests/suite/ecore/src/include/Eina.h new file mode 100644 index 0000000000..eb0263f6c2 --- /dev/null +++ b/tests/suite/ecore/src/include/Eina.h @@ -0,0 +1,165 @@ +/* EINA - EFL data type library + * Copyright (C) 2008-2010 Enlightenment Developers: + * Albin "Lutin" Tonnerre <albin.tonnerre@gmail.com> + * Alexandre "diaxen" Becoulet <diaxen@free.fr> + * Andre Dieb <andre.dieb@gmail.com> + * Arnaud de Turckheim "quarium" <quarium@gmail.com> + * Carsten Haitzler <raster@rasterman.com> + * Cedric Bail <cedric.bail@free.fr> + * Corey "atmos" Donohoe <atmos@atmos.org> + * Fabiano Fidêncio <fidencio@profusion.mobi> + * Gustavo Chaves <glima@profusion.mobi> + * Gustavo Sverzut Barbieri <barbieri@gmail.com> + * Jorge Luis "turran" Zapata <jorgeluis.zapata@gmail.com> + * Peter "pfritz" Wehrfritz <peter.wehrfritz@web.de> + * Raphael Kubo da Costa <kubo@profusion.mobi> + * Tilman Sauerbeck <tilman@code-monkey.de> + * Vincent "caro" Torri <vtorri at univ-evry dot fr> + * Tom Hacohen <tom@stosb.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_H_ +#define EINA_H_ + +/** + * @mainpage Eina + * + * @author Albin "Lutin" Tonnerre <albin.tonnerre@@gmail.com> + * @author Alexandre "diaxen" Becoulet <diaxen@@free.fr> + * @author Andre Dieb <andre.dieb@@gmail.com> + * @author Arnaud de Turckheim "quarium" <quarium@@gmail.com> + * @author Carsten Haitzler <raster@@rasterman.com> + * @author Cedric Bail <cedric.bail@@free.fr> + * @author Corey "atmos" Donohoe <atmos@@atmos.org> + * @author Fabiano Fidêncio <fidencio@@profusion.mobi> + * @author Gustavo Chaves <glima@@profusion.mobi> + * @author Gustavo Sverzut Barbieri <barbieri@@profusion.mobi> + * @author Jorge Luis "turran" Zapata <jorgeluis.zapata@@gmail.com> + * @author Peter "pfritz" Wehrfritz <peter.wehrfritz@@web.de> + * @author Raphael Kubo da Costa <kubo@@profusion.mobi> + * @author Tilman Sauerbeck <tilman@@code-monkey.de> + * @author Vincent "caro" Torri <vtorri at univ-evry dot fr> + * @author Tom Hacohen <tom@@stosb.com> + * @date 2008-2010 + * + * @section eina_intro_sec Introduction + * + * The Eina library is a library that implements an API for data types + * in an efficient way. It also provides some useful tools like + * openin shared libraries, errors management, type conversion, + * time accounting and memory pool. + * + * This library is cross-platform and can be compiled and used on + * Linux, BSD, Opensolaris and Windows (XP and CE). + * + * The data types that are available are (see @ref Eina_Data_Types_Group): + * @li @ref Eina_Array_Group standard array of @c void* data. + * @li @ref Eina_Hash_Group standard hash of @c void* data. + * @li @ref Eina_Inline_List_Group list with nodes inlined into user type. + * @li @ref Eina_List_Group standard list of @c void* data. + * @li @ref Eina_Matrixsparse_Group sparse matrix of @c void* data. + * @li @ref Eina_Rbtree_Group red-black tree with nodes inlined into user type. + * @li @ref Eina_String_Buffer_Group mutable string to prepend, insert or append strings to a buffer. + * @li @ref Eina_Stringshare_Group saves memory by sharing read-only string references. + * @li @ref Eina_Tiler_Group split, merge and navigates into 2D tiled regions. + * @li @ref Eina_Trash_Group container of unused but allocated data. + * + * The tools that are available are (see @ref Eina_Tools_Group): + * @li @ref Eina_Benchmark_Group helper to write benchmarks. + * @li @ref Eina_Convert_Group faster conversion from strings to integers, double, etc. + * @li @ref Eina_Counter_Group measures number of calls and their time. + * @li @ref Eina_Error_Group error identifiers. + * @li @ref Eina_File_Group simple file list and path split. + * @li @ref Eina_Lalloc_Group simple lazy allocator. + * @li @ref Eina_Log_Group full-featured logging system. + * @li @ref Eina_Magic_Group provides runtime type checking. + * @li @ref Eina_Memory_Pool_Group abstraction for various memory allocators. + * @li @ref Eina_Module_Group lists, loads and share modules using Eina_Module standard. + * @li @ref Eina_Rectangle_Group rectangle structure and standard manipulation methods. + * @li @ref Eina_Safety_Checks_Group extra checks that will report unexpected conditions and can be disabled at compile time. + * @li @ref Eina_String_Group a set of functions that manages C strings. + * + * @defgroup Eina_Data_Types_Group Data types. + * + * Eina provide easy to use and optimized data types and structures. + * + * + * @defgroup Eina_Containers_Group Containers + * + * Containers are data types that hold data and allow iteration over + * their elements with an @ref Eina_Iterator_Group, or eventually an + * @ref Eina_Accessor_Group. + * + * + * @defgroup Eina_Tools_Group Tools + * + * Eina tools aims to help application development, providing ways to + * make it safer, log errors, manage memory more efficiently and more. + */ + +#include <dirent.h> + +#ifdef _WIN32 +# include <Evil.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "eina_config.h" +#include "eina_types.h" +#include "eina_main.h" +#include "eina_fp.h" +#include "eina_rectangle.h" +#include "eina_inlist.h" +#include "eina_file.h" +#include "eina_list.h" +#include "eina_hash.h" +#include "eina_trash.h" +#include "eina_lalloc.h" +#include "eina_module.h" +#include "eina_mempool.h" +#include "eina_error.h" +#include "eina_log.h" +#include "eina_array.h" +#include "eina_binshare.h" +#include "eina_stringshare.h" +#include "eina_ustringshare.h" +#include "eina_magic.h" +#include "eina_counter.h" +#include "eina_rbtree.h" +#include "eina_accessor.h" +#include "eina_iterator.h" +#include "eina_benchmark.h" +#include "eina_convert.h" +#include "eina_cpu.h" +#include "eina_sched.h" +#include "eina_tiler.h" +#include "eina_hamster.h" +#include "eina_matrixsparse.h" +#include "eina_str.h" +#include "eina_strbuf.h" +#include "eina_ustrbuf.h" +#include "eina_unicode.h" +#include "eina_quadtree.h" + +#ifdef __cplusplus +} +#endif + +#endif /* EINA_H */ diff --git a/tests/suite/ecore/src/include/eina_accessor.h b/tests/suite/ecore/src/include/eina_accessor.h new file mode 100644 index 0000000000..4d2f92e0a1 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_accessor.h @@ -0,0 +1,148 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_ACCESSOR_H__ +#define EINA_ACCESSOR_H__ + +#include "eina_config.h" + +#include "eina_types.h" +#include "eina_magic.h" + +/** + * @addtogroup Eina_Content_Access_Group Content Access + * + * @{ + */ + +/** + * @defgroup Eina_Accessor_Group Accessor Functions + * + * @{ + */ + +/** + * @typedef Eina_Accessor + * Type for accessors. + */ +typedef struct _Eina_Accessor Eina_Accessor; + +typedef Eina_Bool (*Eina_Accessor_Get_At_Callback)(Eina_Accessor *it, + unsigned int index, + void **data); +typedef void *(*Eina_Accessor_Get_Container_Callback)(Eina_Accessor *it); +typedef void (*Eina_Accessor_Free_Callback)(Eina_Accessor *it); +typedef Eina_Bool (*Eina_Accessor_Lock_Callback)(Eina_Accessor *it); + +struct _Eina_Accessor +{ +#define EINA_ACCESSOR_VERSION 1 + int version; + + Eina_Accessor_Get_At_Callback get_at EINA_ARG_NONNULL(1, 3) EINA_WARN_UNUSED_RESULT; + Eina_Accessor_Get_Container_Callback get_container EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + Eina_Accessor_Free_Callback free EINA_ARG_NONNULL(1); + + Eina_Accessor_Lock_Callback lock EINA_WARN_UNUSED_RESULT; + Eina_Accessor_Lock_Callback unlock EINA_WARN_UNUSED_RESULT; + +#define EINA_MAGIC_ACCESSOR 0x98761232 + EINA_MAGIC +}; + +#define FUNC_ACCESSOR_GET_AT(Function) ((Eina_Accessor_Get_At_Callback)Function) +#define FUNC_ACCESSOR_GET_CONTAINER(Function) ((Eina_Accessor_Get_Container_Callback)Function) +#define FUNC_ACCESSOR_FREE(Function) ((Eina_Accessor_Free_Callback)Function) +#define FUNC_ACCESSOR_LOCK(Function) ((Eina_Accessor_Lock_Callback)Function) + +EAPI void eina_accessor_free(Eina_Accessor *accessor) EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_accessor_data_get(Eina_Accessor *accessor, + unsigned int position, + void **data) EINA_ARG_NONNULL(1); +EAPI void * eina_accessor_container_get(Eina_Accessor *accessor) EINA_ARG_NONNULL(1) EINA_PURE; +EAPI void eina_accessor_over(Eina_Accessor *accessor, + Eina_Each_Cb cb, + unsigned int start, + unsigned int end, + const void *fdata) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_accessor_lock(Eina_Accessor *accessor) EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_accessor_unlock(Eina_Accessor *accessor) EINA_ARG_NONNULL(1); + +/** + * @def EINA_ACCESSOR_FOREACH + * @brief Macro to iterate over all elements easily. + * + * @param accessor The accessor to use. + * @param counter A counter used by eina_accessor_data_get() when + * iterating over the container. + * @param data Where to store * data, must be a pointer support getting + * its address since * eina_accessor_data_get() requires a pointer to + * pointer! + * + * This macro allows a convenient way to loop over all elements in an + * accessor, very similar to EINA_LIST_FOREACH(). + * + * This macro can be used for freeing the data of a list, like in the + * following example. It has the same goal as the one documented in + * EINA_LIST_FOREACH(), but using accessors: + * + * @code + * Eina_List *list; + * Eina_Accessor *accessor; + * unsigned int i; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings + * + * accessor = eina_list_accessor_new(list); + * EINA_ACCESSOR_FOREACH(accessor, i, data) + * free(data); + * eina_accessor_free(accessor); + * eina_list_free(list); + * @endcode + * + * @note if the datatype provides both iterators and accessors prefer + * to use iterators to iterate over, as they're likely to be more + * optimized for such task. + * + * @note this example is not optimal algorithm to release a list since + * it will walk the list twice, but it serves as an example. For + * optimized version use EINA_LIST_FREE() + * + * @warning unless explicitly stated in functions returning accessors, + * do not modify the accessed object while you walk it, in this + * example using lists, do not remove list nodes or you might + * crash! This is not a limitiation of accessors themselves, + * rather in the accessors implementations to keep them as simple + * and fast as possible. + */ +#define EINA_ACCESSOR_FOREACH(accessor, counter, data) \ + for ((counter) = 0; \ + eina_accessor_data_get((accessor), (counter), (void **)&(data)); \ + (counter)++) + +/** + * @} + */ + +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_array.h b/tests/suite/ecore/src/include/eina_array.h new file mode 100644 index 0000000000..cb0d12a1a8 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_array.h @@ -0,0 +1,153 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_ARRAY_H_ +#define EINA_ARRAY_H_ + +#include <stdlib.h> + +#include "eina_config.h" + +#include "eina_types.h" +#include "eina_error.h" +#include "eina_iterator.h" +#include "eina_accessor.h" +#include "eina_magic.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @addtogroup Eina_Containers_Group Containers + * + * @{ + */ + +/** + * @defgroup Eina_Array_Group Array + * + * @{ + */ + +/** + * @typedef Eina_Array + * Type for a generic vector. + */ +typedef struct _Eina_Array Eina_Array; + +/** + * @typedef Eina_Array_Iterator + * Type for an iterator on arrays, used with #EINA_ARRAY_ITER_NEXT. + */ +typedef void **Eina_Array_Iterator; + +/** + * @struct _Eina_Array + * Type for an array of data. + */ +struct _Eina_Array +{ +#define EINA_ARRAY_VERSION 1 + int version; /**< Should match EINA_ARRAY_VERSION used when compiled your apps, provided for ABI compatibility */ + + void **data; /**< Pointer to a vector of pointer to payload */ + unsigned int total; /**< Total number of slots in the vector */ + unsigned int count; /**< Number of active slots in the vector */ + unsigned int step; /**< How much must we grow the vector when it is full */ + EINA_MAGIC +}; + +EAPI Eina_Array * eina_array_new(unsigned int step) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI void eina_array_free(Eina_Array *array) EINA_ARG_NONNULL(1); +EAPI void eina_array_step_set(Eina_Array *array, + unsigned int sizeof_eina_array, + unsigned int step) EINA_ARG_NONNULL(1); +EAPI void eina_array_clean(Eina_Array *array) EINA_ARG_NONNULL(1); +EAPI void eina_array_flush(Eina_Array *array) EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_array_remove(Eina_Array *array, + Eina_Bool(*keep)(void *data, void *gdata), + void *gdata) EINA_ARG_NONNULL(1, 2); +static inline Eina_Bool eina_array_push(Eina_Array *array, + const void *data) EINA_ARG_NONNULL(1, 2); +static inline void * eina_array_pop(Eina_Array *array) EINA_ARG_NONNULL(1); +static inline void * eina_array_data_get(const Eina_Array *array, + unsigned int idx) EINA_ARG_NONNULL(1); +static inline void eina_array_data_set(const Eina_Array *array, + unsigned int idx, + const void *data) EINA_ARG_NONNULL(1, 3); +static inline unsigned int eina_array_count_get(const Eina_Array *array) EINA_ARG_NONNULL(1); +EAPI Eina_Iterator * eina_array_iterator_new(const Eina_Array *array) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Accessor * eina_array_accessor_new(const Eina_Array *array) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +static inline Eina_Bool eina_array_foreach(Eina_Array *array, + Eina_Each_Cb cb, + void *data); +/** + * @def EINA_ARRAY_ITER_NEXT + * @brief Macro to iterate over an array easily. + * + * @param array The array to iterate over. + * @param index The integer number that is increased while itareting. + * @param item The data + * @param iterator The iterator + * + * This macro allows the iteration over @p array in an easy way. It + * iterates from the first element to the last one. @p index is an + * integer that increases from 0 to the number of elements. @p item is + * the data of each element of @p array, so it is a pointer to a type + * chosen by the user. @p iterator is of type #Eina_Array_Iterator. + * + * This macro can be used for freeing the data of an array, like in + * the following example: + * + * @code + * Eina_Array *array; + * char *item; + * Eina_Array_Iterator iterator; + * unsigned int i; + * + * // array is already filled, + * // its elements are just duplicated strings, + * // EINA_ARRAY_ITER_NEXT will be used to free those strings + * + * EINA_ARRAY_ITER_NEXT(array, i, item, iterator) + * free(item); + * @endcode + */ +#define EINA_ARRAY_ITER_NEXT(array, index, item, iterator) \ + for (index = 0, iterator = (array)->data; \ + (index < eina_array_count_get(array)) && ((item = *((iterator)++))); \ + ++(index)) + +#include "eina_inline_array.x" + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_benchmark.h b/tests/suite/ecore/src/include/eina_benchmark.h new file mode 100644 index 0000000000..e0faa722df --- /dev/null +++ b/tests/suite/ecore/src/include/eina_benchmark.h @@ -0,0 +1,77 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_BENCHMARK_H_ +#define EINA_BENCHMARK_H_ + +#include "eina_array.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Benchmark_Group Benchmark + * + * @{ + */ + +/** + * @typedef Eina_Benchmark + * Type for a benchmark. + */ +typedef struct _Eina_Benchmark Eina_Benchmark; + +/** + * @typedef Eina_Benchmark_Specimens + * Type for a test function to be called when running a benchmark. + */ +typedef void (*Eina_Benchmark_Specimens)(int request); + +/** + * @def EINA_BENCHMARK + * @brief cast to an #Eina_Benchmark_Specimens. + * + * @param function The function to cast. + * + * This macro casts @p function to Eina_Benchmark_Specimens. + */ +#define EINA_BENCHMARK(function) ((Eina_Benchmark_Specimens)function) + +EAPI Eina_Benchmark * eina_benchmark_new(const char *name, + const char *run); +EAPI void eina_benchmark_free(Eina_Benchmark *bench); +EAPI Eina_Bool eina_benchmark_register(Eina_Benchmark *bench, + const char *name, + Eina_Benchmark_Specimens bench_cb, + int count_start, + int count_end, + int count_set); +EAPI Eina_Array * eina_benchmark_run(Eina_Benchmark *bench); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_BENCHMARK_H_ */ diff --git a/tests/suite/ecore/src/include/eina_binshare.h b/tests/suite/ecore/src/include/eina_binshare.h new file mode 100644 index 0000000000..e785ed0137 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_binshare.h @@ -0,0 +1,105 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (C) 2008 Peter Wehrfritz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software and its Copyright notices. In addition publicly + * documented acknowledgment must be given that this software has been used if no + * source code of this software is made available publicly. This includes + * acknowledgments in either Copyright notices, Manuals, Publicity and Marketing + * documents or any documentation provided with any product containing this + * software. This License does not apply to any software that links to the + * libraries provided by this software (statically or dynamically), but only to + * the software provided. + * + * Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice + * and it's intent. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef EINA_BINSHARE_H_ +#define EINA_BINSHARE_H_ + +#include "eina_types.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @defgroup Eina_Binshare_Group Binary Share + * + * @{ + */ + +EAPI Eina_Bool eina_binshare_init(void); +EAPI Eina_Bool eina_binshare_shutdown(void); +EAPI const void * eina_binshare_add_length(const void *obj, + unsigned int olen) EINA_PURE EINA_WARN_UNUSED_RESULT; +EAPI const void * eina_binshare_ref(const void *obj); +EAPI void eina_binshare_del(const void *obj); +EAPI int eina_binshare_length(const void *obj) EINA_WARN_UNUSED_RESULT; +EAPI void eina_binshare_dump(void); + +/** + * @brief Retrieve an instance of a blob for use in a program. + * + * @param ptr The binary blob to retrieve an instance of. + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This macro retrieves an instance of @p obj. If @p obj is + * @c NULL, then @c NULL is returned. If @p obj is already stored, it + * is just returned and its reference counter is increased. Otherwise + * it is added to the blobs to be searched and a duplicated blob + * of @p obj is returned. + * + * This macro essentially calls eina_binshare_add_length with ptr and sizeof(*ptr) + * as the parameters. It's useful for pointers to structures. + * + * @see eina_stringshare_add_length() + */ +#define eina_binshare_add(ptr) eina_binshare_add_length(ptr, sizeof(*ptr)) + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_STRINGSHARE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_config.h b/tests/suite/ecore/src/include/eina_config.h new file mode 100644 index 0000000000..f89327d6a6 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_config.h @@ -0,0 +1,52 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_CONFIG_H_ +#define EINA_CONFIG_H_ + +#ifdef EINA_MAGIC_DEBUG +# undef EINA_MAGIC_DEBUG +#endif +#define EINA_MAGIC_DEBUG + +#ifdef EINA_DEFAULT_MEMPOOL +# undef EINA_DEFAULT_MEMPOOL +#endif + + +#ifdef EINA_SAFETY_CHECKS +# undef EINA_SAFETY_CHECKS +#endif +#define EINA_SAFETY_CHECKS + +#ifdef EINA_HAVE_INTTYPES_H +# undef EINA_HAVE_INTTYPES_H +#endif +#define EINA_HAVE_INTTYPES_H + +#ifdef EINA_HAVE_STDINT_H +# undef EINA_HAVE_STDINT_H +#endif +#define EINA_HAVE_STDINT_H + +#ifdef EINA_SIZEOF_WCHAR_T +# undef EINA_SIZEOF_WCHAR_T +#endif +#define EINA_SIZEOF_WCHAR_T 4 + +#endif /* EINA_CONFIG_H_ */ diff --git a/tests/suite/ecore/src/include/eina_convert.h b/tests/suite/ecore/src/include/eina_convert.h new file mode 100644 index 0000000000..8cd64334f5 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_convert.h @@ -0,0 +1,80 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric BAIL, Vincent Torri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_CONVERT_H_ +#define EINA_CONVERT_H_ + +#include "eina_types.h" +#include "eina_error.h" + +#include "eina_fp.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Convert_Group Convert + * + * @{ + */ + +/** + * @var EINA_ERROR_CONVERT_P_NOT_FOUND + * Error identifier corresponding to string not containing 'p'. + */ +EAPI extern Eina_Error EINA_ERROR_CONVERT_P_NOT_FOUND; + +/** + * @var EINA_ERROR_CONVERT_0X_NOT_FOUND + * Error identifier corresponding to string not containing '0x'. + */ +EAPI extern Eina_Error EINA_ERROR_CONVERT_0X_NOT_FOUND; + +/** + * @var EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH + * Error identifier corresponding to length of the string being too small. + */ +EAPI extern Eina_Error EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH; + +EAPI int eina_convert_itoa(int n, char *s) EINA_ARG_NONNULL(2); +EAPI int eina_convert_xtoa(unsigned int n, char *s) EINA_ARG_NONNULL(2); + +EAPI int eina_convert_dtoa(double d, char *des) EINA_ARG_NONNULL(2); +EAPI Eina_Bool eina_convert_atod(const char *src, + int length, + long long *m, + long *e) EINA_ARG_NONNULL(1,3,4); + +EAPI int eina_convert_fptoa(Eina_F32p32 fp, + char *des) EINA_ARG_NONNULL(2); +EAPI Eina_Bool eina_convert_atofp(const char *src, + int length, + Eina_F32p32 *fp) EINA_ARG_NONNULL(1,3); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_CONVERT_H_ */ diff --git a/tests/suite/ecore/src/include/eina_counter.h b/tests/suite/ecore/src/include/eina_counter.h new file mode 100644 index 0000000000..fc7b23f533 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_counter.h @@ -0,0 +1,57 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_COUNTER_H_ +#define EINA_COUNTER_H_ + +#include "eina_types.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Counter_Group Counter + * + * @{ + */ + +/** + * @typedef Eina_Counter + * Counter type. + */ +typedef struct _Eina_Counter Eina_Counter; + +EAPI Eina_Counter * eina_counter_new(const char *name) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void eina_counter_free(Eina_Counter *counter) EINA_ARG_NONNULL(1); +EAPI void eina_counter_start(Eina_Counter *counter) EINA_ARG_NONNULL(1); +EAPI void eina_counter_stop(Eina_Counter *counter, + int specimen) EINA_ARG_NONNULL(1); +EAPI char * eina_counter_dump(Eina_Counter *counter) EINA_ARG_NONNULL(1); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_COUNTER_H_ */ diff --git a/tests/suite/ecore/src/include/eina_cpu.h b/tests/suite/ecore/src/include/eina_cpu.h new file mode 100644 index 0000000000..ac32e1db9b --- /dev/null +++ b/tests/suite/ecore/src/include/eina_cpu.h @@ -0,0 +1,39 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_CPU_H_ +#define EINA_CPU_H_ + +#include "eina_types.h" + +typedef enum _Eina_Cpu_Features +{ + EINA_CPU_MMX = 0x00000001, + EINA_CPU_SSE = 0x00000002, + EINA_CPU_SSE2 = 0x00000004, + EINA_CPU_SSE3 = 0x00000008, + /* TODO 3DNow! */ + EINA_CPU_ALTIVEC = 0x00000010, + EINA_CPU_VIS = 0x00000020, + EINA_CPU_NEON = 0x00000040, +} Eina_Cpu_Features; + +EAPI Eina_Cpu_Features eina_cpu_features_get(void); +EAPI int eina_cpu_count(void); + +#endif /* EINA_CPU_H_ */ diff --git a/tests/suite/ecore/src/include/eina_error.h b/tests/suite/ecore/src/include/eina_error.h new file mode 100644 index 0000000000..8c74b66f0f --- /dev/null +++ b/tests/suite/ecore/src/include/eina_error.h @@ -0,0 +1,66 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_ERROR_H_ +#define EINA_ERROR_H_ + +#include <stdarg.h> + +#include "eina_types.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Error_Group Error + * + * @{ + */ + +/** + * @typedef Eina_Error + * Error type. + */ +typedef int Eina_Error; + +/** + * @var EINA_ERROR_OUT_OF_MEMORY + * Error identifier corresponding to a lack of memory. + */ +EAPI extern Eina_Error EINA_ERROR_OUT_OF_MEMORY; + +EAPI Eina_Error eina_error_msg_register(const char *msg) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Error eina_error_msg_static_register(const char *msg) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool eina_error_msg_modify(Eina_Error error, + const char *msg) EINA_ARG_NONNULL(2); +EAPI Eina_Error eina_error_get(void); +EAPI void eina_error_set(Eina_Error err); +EAPI const char * eina_error_msg_get(Eina_Error error) EINA_PURE; + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_ERROR_H_ */ diff --git a/tests/suite/ecore/src/include/eina_file.h b/tests/suite/ecore/src/include/eina_file.h new file mode 100644 index 0000000000..4c3740ffa0 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_file.h @@ -0,0 +1,92 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_FILE_H_ +#define EINA_FILE_H_ + +#include <limits.h> + +#include "eina_types.h" +#include "eina_array.h" +#include "eina_iterator.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_File_Group File + * + * @{ + */ + +/** + * @typedef Eina_File_Direct_Info + * A typedef to #_Eina_File_Direct_Info. + */ +typedef struct _Eina_File_Direct_Info Eina_File_Direct_Info; + +/** + * @typedef Eina_File_Dir_List_Cb + * Type for a callback to be called when iterating over the files of a + * directory. + */ +typedef void (*Eina_File_Dir_List_Cb)(const char *name, const char *path, void *data); + +/** + * @struct _Eina_File_Direct_Info + * A structure to store informations of a path. + */ +struct _Eina_File_Direct_Info +{ + size_t path_length; /**< size of the whole path */ + size_t name_length; /**< size of the filename/basename component */ + size_t name_start; /**< where the filename/basename component starts */ + char path[PATH_MAX]; /**< the path */ + const struct dirent *dirent; /**< the dirent structure of the path */ +}; + +/** + * @def EINA_FILE_DIR_LIST_CB + * @brief cast to an #Eina_File_Dir_List_Cb. + * + * @param function The function to cast. + * + * This macro casts @p function to Eina_File_Dir_List_Cb. + */ +#define EINA_FILE_DIR_LIST_CB(function) ((Eina_File_Dir_List_Cb)function) + +EAPI Eina_Bool eina_file_dir_list(const char *dir, + Eina_Bool recursive, + Eina_File_Dir_List_Cb cb, + void *data) EINA_ARG_NONNULL(1, 3); +EAPI Eina_Array * eina_file_split(char *path) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; +EAPI Eina_Iterator * eina_file_ls(const char *dir) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; +EAPI Eina_Iterator * eina_file_direct_ls(const char *dir) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_FILE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_fp.h b/tests/suite/ecore/src/include/eina_fp.h new file mode 100644 index 0000000000..47dbaa486d --- /dev/null +++ b/tests/suite/ecore/src/include/eina_fp.h @@ -0,0 +1,111 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * Copyright (C) 2009 Cedric BAIL + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_FP_H_ +# define EINA_FP_H_ + +#include "eina_types.h" + +#ifdef _MSC_VER +typedef unsigned __int64 uint64_t; +typedef signed __int64 int64_t; +typedef signed int int32_t; +#else +# include <stdint.h> +#endif + +#define EINA_F32P32_PI 0x00000003243f6a89 + +typedef int64_t Eina_F32p32; +typedef int32_t Eina_F16p16; +typedef int32_t Eina_F8p24; + +static inline Eina_F32p32 eina_f32p32_int_from(int32_t v); +static inline int32_t eina_f32p32_int_to(Eina_F32p32 v); +static inline Eina_F32p32 eina_f32p32_double_from(double v); +static inline double eina_f32p32_double_to(Eina_F32p32 v); + +static inline Eina_F32p32 eina_f32p32_add(Eina_F32p32 a, + Eina_F32p32 b); +static inline Eina_F32p32 eina_f32p32_sub(Eina_F32p32 a, + Eina_F32p32 b); +static inline Eina_F32p32 eina_f32p32_mul(Eina_F32p32 a, + Eina_F32p32 b); +static inline Eina_F32p32 eina_f32p32_scale(Eina_F32p32 a, + int b); +static inline Eina_F32p32 eina_f32p32_div(Eina_F32p32 a, + Eina_F32p32 b); +static inline Eina_F32p32 eina_f32p32_sqrt(Eina_F32p32 a); +static inline unsigned int eina_f32p32_fracc_get(Eina_F32p32 v); + +// dont use llabs - issues if not on 64bit +#define eina_fp32p32_llabs(a) ((a < 0) ? -(a) : (a)) + +EAPI Eina_F32p32 eina_f32p32_cos(Eina_F32p32 a); +EAPI Eina_F32p32 eina_f32p32_sin(Eina_F32p32 a); + +static inline Eina_F16p16 eina_f16p16_int_from(int32_t v); +static inline int32_t eina_f16p16_int_to(Eina_F16p16 v); +static inline Eina_F16p16 eina_f16p16_float_from(float v); +static inline float eina_f16p16_float_to(Eina_F16p16 v); + +static inline Eina_F16p16 eina_f16p16_add(Eina_F16p16 a, + Eina_F16p16 b); +static inline Eina_F16p16 eina_f16p16_sub(Eina_F16p16 a, + Eina_F16p16 b); +static inline Eina_F16p16 eina_f16p16_mul(Eina_F16p16 a, + Eina_F16p16 b); +static inline Eina_F16p16 eina_f16p16_scale(Eina_F16p16 a, + int b); +static inline Eina_F16p16 eina_f16p16_div(Eina_F16p16 a, + Eina_F16p16 b); +static inline Eina_F16p16 eina_f16p16_sqrt(Eina_F16p16 a); +static inline unsigned int eina_f16p16_fracc_get(Eina_F16p16 v); + +static inline Eina_F8p24 eina_f8p24_int_from(int32_t v); +static inline int32_t eina_f8p24_int_to(Eina_F8p24 v); +static inline Eina_F8p24 eina_f8p24_float_from(float v); +static inline float eina_f8p24_float_to(Eina_F8p24 v); + +static inline Eina_F8p24 eina_f8p24_add(Eina_F8p24 a, + Eina_F8p24 b); +static inline Eina_F8p24 eina_f8p24_sub(Eina_F8p24 a, + Eina_F8p24 b); +static inline Eina_F8p24 eina_f8p24_mul(Eina_F8p24 a, + Eina_F8p24 b); +static inline Eina_F8p24 eina_f8p24_scale(Eina_F8p24 a, + int b); +static inline Eina_F8p24 eina_f8p24_div(Eina_F8p24 a, + Eina_F8p24 b); +static inline Eina_F8p24 eina_f8p24_sqrt(Eina_F8p24 a); +static inline unsigned int eina_f8p24_fracc_get(Eina_F8p24 v); + +static inline Eina_F32p32 eina_f16p16_to_f32p32(Eina_F16p16 a); +static inline Eina_F32p32 eina_f8p24_to_f32p32(Eina_F8p24 a); +static inline Eina_F16p16 eina_f32p32_to_f16p16(Eina_F32p32 a); +static inline Eina_F16p16 eina_f8p24_to_f16p16(Eina_F8p24 a); +static inline Eina_F8p24 eina_f32p32_to_f8p24(Eina_F32p32 a); +static inline Eina_F8p24 eina_f16p16_to_f8p24(Eina_F16p16 a); + +#include "eina_inline_f32p32.x" +#include "eina_inline_f16p16.x" +#include "eina_inline_f8p24.x" +#include "eina_inline_fp.x" + +#endif diff --git a/tests/suite/ecore/src/include/eina_hamster.h b/tests/suite/ecore/src/include/eina_hamster.h new file mode 100644 index 0000000000..0818b15616 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_hamster.h @@ -0,0 +1,44 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_HAMSTER_H_ +#define EINA_HAMSTER_H_ + +/** + * @addtogroup Eina_Core_Group Core + * + * @{ + */ + +/** + * @defgroup Eina_Hamster_Group Hamster + * + * @{ + */ + +EAPI int eina_hamster_count(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_HAMSTER_H_ */ diff --git a/tests/suite/ecore/src/include/eina_hash.h b/tests/suite/ecore/src/include/eina_hash.h new file mode 100644 index 0000000000..03451339a9 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_hash.h @@ -0,0 +1,168 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Gustavo Sverzut Barbieri, + * Vincent Torri, Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_HASH_H_ +#define EINA_HASH_H_ + +#include "eina_types.h" +#include "eina_iterator.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @addtogroup Eina_Containers_Group Containers + * + * @{ + */ + +/** + * @defgroup Eina_Hash_Group Hash Table + * + * @{ + */ + +/** + * @typedef Eina_Hash + * Type for a generic hash table. + */ +typedef struct _Eina_Hash Eina_Hash; + +typedef struct _Eina_Hash_Tuple Eina_Hash_Tuple; + +struct _Eina_Hash_Tuple +{ + const void *key; /**< The key */ + void *data; /**< The data associated to the key */ + unsigned int key_length; /**< The length of the key */ +}; + +typedef unsigned int (*Eina_Key_Length)(const void *key); +#define EINA_KEY_LENGTH(Function) ((Eina_Key_Length)Function) +typedef int (*Eina_Key_Cmp)(const void *key1, int key1_length, const void *key2, int key2_length); +#define EINA_KEY_CMP(Function) ((Eina_Key_Cmp)Function) +typedef int (*Eina_Key_Hash)(const void *key, int key_length); +#define EINA_KEY_HASH(Function) ((Eina_Key_Hash)Function) +typedef Eina_Bool (*Eina_Hash_Foreach)(const Eina_Hash *hash, const void *key, void *data, void *fdata); + +EAPI Eina_Hash * eina_hash_new(Eina_Key_Length key_length_cb, + Eina_Key_Cmp key_cmp_cb, + Eina_Key_Hash key_hash_cb, + Eina_Free_Cb data_free_cb, + int buckets_power_size) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(2, 3); +EAPI Eina_Hash * eina_hash_string_djb2_new(Eina_Free_Cb data_free_cb); +EAPI Eina_Hash * eina_hash_string_superfast_new(Eina_Free_Cb data_free_cb); +EAPI Eina_Hash * eina_hash_string_small_new(Eina_Free_Cb data_free_cb); +EAPI Eina_Hash * eina_hash_int32_new(Eina_Free_Cb data_free_cb); +EAPI Eina_Hash * eina_hash_int64_new(Eina_Free_Cb data_free_cb); +EAPI Eina_Hash * eina_hash_pointer_new(Eina_Free_Cb data_free_cb); +EAPI Eina_Hash * eina_hash_stringshared_new(Eina_Free_Cb data_free_cb); +EAPI Eina_Bool eina_hash_add(Eina_Hash *hash, + const void *key, + const void *data) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_hash_direct_add(Eina_Hash *hash, + const void *key, + const void *data) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_hash_del(Eina_Hash *hash, + const void *key, + const void *data) EINA_ARG_NONNULL(1); +EAPI void * eina_hash_find(const Eina_Hash *hash, + const void *key) EINA_ARG_NONNULL(1, 2); +EAPI void * eina_hash_modify(Eina_Hash *hash, + const void *key, + const void *data) EINA_ARG_NONNULL(1, 2, 3); +EAPI void * eina_hash_set(Eina_Hash *hash, + const void *key, + const void *data) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_hash_move(Eina_Hash *hash, + const void *old_key, + const void *new_key) EINA_ARG_NONNULL(1, 2, 3); +EAPI void eina_hash_free(Eina_Hash *hash) EINA_ARG_NONNULL(1); +EAPI void eina_hash_free_buckets(Eina_Hash *hash) EINA_ARG_NONNULL(1); +EAPI int eina_hash_population(const Eina_Hash *hash) EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_hash_add_by_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) EINA_ARG_NONNULL(1, 2, 5); +EAPI Eina_Bool eina_hash_direct_add_by_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) EINA_ARG_NONNULL(1, 2, 5); +EAPI Eina_Bool eina_hash_del_by_key_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_hash_del_by_key(Eina_Hash *hash, + const void *key) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_hash_del_by_data(Eina_Hash *hash, + const void *data) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_hash_del_by_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) EINA_ARG_NONNULL(1); +EAPI void * eina_hash_find_by_hash(const Eina_Hash *hash, + const void *key, + int key_length, + int key_hash) EINA_ARG_NONNULL(1, 2); +EAPI void * eina_hash_modify_by_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) EINA_ARG_NONNULL(1, 2, 5); +EAPI Eina_Iterator * eina_hash_iterator_key_new(const Eina_Hash *hash) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Iterator * eina_hash_iterator_data_new(const Eina_Hash *hash) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Iterator * eina_hash_iterator_tuple_new(const Eina_Hash *hash) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI void eina_hash_foreach(const Eina_Hash *hash, + Eina_Hash_Foreach cb, + const void *fdata) EINA_ARG_NONNULL(1, 2); +/* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html) hash function used by WebCore (http://webkit.org/blog/8/hashtables-part-2/) */ +EAPI int eina_hash_superfast(const char *key, + int len) EINA_ARG_NONNULL(1); +/* Hash function first reported by dan bernstein many years ago in comp.lang.c */ +static inline int eina_hash_djb2(const char *key, + int len) EINA_ARG_NONNULL(1); +static inline int eina_hash_djb2_len(const char *key, + int *plen) EINA_ARG_NONNULL(1, 2); +/* Hash function from http://www.concentric.net/~Ttwang/tech/inthash.htm */ +static inline int eina_hash_int32(const unsigned int *pkey, + int len) EINA_ARG_NONNULL(1); +static inline int eina_hash_int64(const unsigned long int *pkey, + int len) EINA_ARG_NONNULL(1); + +#include "eina_inline_hash.x" + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#endif /*EINA_HASH_H_*/ diff --git a/tests/suite/ecore/src/include/eina_inline_array.x b/tests/suite/ecore/src/include/eina_inline_array.x new file mode 100644 index 0000000000..883d728b8d --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_array.x @@ -0,0 +1,181 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_ARRAY_X_ +#define EINA_INLINE_ARRAY_X_ + +#include <stdio.h> + +/** + * @cond LOCAL + */ + +EAPI Eina_Bool eina_array_grow(Eina_Array *array); + +/** + * @endcond + */ + +/** + * @addtogroup Eina_Array_Group Array + * + * @brief These functions provide array management. + * + * @{ + */ + +/** + * @brief Append a data to an array. + * + * @param array The array. + * @param data The data to add. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function appends @p data to @p array. For performance + * reasons, there is no check of @p array. If it is @c NULL or + * invalid, the program may crash. If @p data is @c NULL, or if an + * allocation is necessary and fails, #EINA_FALSE is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, #EINA_TRUE is + * returned. + */ + +static inline Eina_Bool +eina_array_push(Eina_Array *array, const void *data) +{ + if (!data) return EINA_FALSE; + + if (EINA_UNLIKELY((array->count + 1) > array->total)) + if (!eina_array_grow(array)) + return EINA_FALSE; + + array->data[array->count++] = (void*) data; + + return EINA_TRUE; +} + +/** + * @brief Remove the last data of an array. + * + * @param array The array. + * @return The retrieved data. + * + * This function removes the last data of @p array, decreases the count + * of @p array and returns the data. For performance reasons, there + * is no check of @p array. If it is @c NULL or invalid, the program + * may crash. If the count member is less or equal than 0, @c NULL is + * returned. + */ +static inline void * +eina_array_pop(Eina_Array *array) +{ + void *ret = NULL; + + if (array->count <= 0) + goto on_empty; + + ret = array->data[--array->count]; + + on_empty: + return ret; +} + +/** + * @brief Return the data at a given position in an array. + * + * @param array The array. + * @param idx The potition of the data to retrieve. + * @return The retrieved data. + * + * This function returns the data at the position @p idx in @p + * array. For performance reasons, there is no check of @p array or @p + * idx. If it is @c NULL or invalid, the program may crash. + */ +static inline void * +eina_array_data_get(const Eina_Array *array, unsigned int idx) +{ + return array->data[idx]; +} + +/** + * @brief Set the data at a given position in an array. + * + * @param array The array. + * @param idx The potition of the data to set. + * @param data The data to set. + * + * This function sets the data at the position @p idx in @p + * array. For performance reasons, there is no check of @p array or @p + * idx. If it is @c NULL or invalid, the program may crash. + */ +static inline void +eina_array_data_set(const Eina_Array *array, unsigned int idx, const void *data) +{ + array->data[idx] = (void*) data; +} + +/** + * @brief Return the number of elements in an array. + * + * @param array The array. + * @return The number of elements. + * + * This function returns the number of elements in @p array. For + * performance reasons, there is no check of @p array. If it is + * @c NULL or invalid, the program may crash. + */ +static inline unsigned int +eina_array_count_get(const Eina_Array *array) +{ + return array->count; +} + +/** + * @brief Provide a safe way to iterate over an array + * + * @param array The array to iterate over. + * @param cb The callback to call for each item. + * @param fdata The user data to pass to the callback. + * @return EINA_TRUE if it successfully iterate all items of the array. + * + * This function provide a safe way to iterate over an array. @p cb should + * return EINA_TRUE as long as you want the function to continue iterating, + * by returning EINA_FALSE it will stop and return EINA_FALSE as a result. + */ +static inline Eina_Bool +eina_array_foreach(Eina_Array *array, Eina_Each_Cb cb, void *fdata) +{ + void *data; + Eina_Array_Iterator iterator; + unsigned int i; + Eina_Bool ret = EINA_TRUE; + + EINA_ARRAY_ITER_NEXT(array, i, data, iterator) + if (cb(array, data, fdata) != EINA_TRUE) + { + ret = EINA_FALSE; + break; + } + + return ret; +} + +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_f16p16.x b/tests/suite/ecore/src/include/eina_inline_f16p16.x new file mode 100644 index 0000000000..e16d188dce --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_f16p16.x @@ -0,0 +1,83 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * Copyright (C) 2009 Cedric BAIL + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_F16P16_X_ +#define EINA_INLINE_F16P16_X_ + +static inline Eina_F16p16 +eina_f16p16_add(Eina_F16p16 a, Eina_F16p16 b) +{ + return a + b; +} + +static inline Eina_F16p16 +eina_f16p16_sub(Eina_F16p16 a, Eina_F16p16 b) +{ + return a - b; +} + +static inline Eina_F16p16 +eina_f16p16_mul(Eina_F16p16 a, Eina_F16p16 b) +{ + return (Eina_F16p16)(((int64_t)a * (int64_t)b) >> 16); +} + +static inline Eina_F16p16 +eina_f16p16_scale(Eina_F16p16 a, int b) +{ + return a * b; +} + +static inline Eina_F16p16 +eina_f16p16_div(Eina_F16p16 a, Eina_F16p16 b) +{ + return (Eina_F16p16) ((((int64_t) a) << 16) / (int64_t) b); +} + +static inline Eina_F16p16 +eina_f16p16_sqrt(Eina_F16p16 a) +{ + unsigned int root, remHi, remLo, testDiv, count; + + root = 0; /* Clear root */ + remHi = 0; /* Clear high part of partial remainder */ + remLo = a; /* Get argument into low part of partial remainder */ + count = (15 + (16 >> 1)); /* Load loop counter */ + do { + remHi = (remHi << 2) | (remLo >> 30); + remLo <<= 2; /* get 2 bits of arg */ + root <<= 1; /* Get ready for the next bit in the root */ + testDiv = (root << 1) + 1; /* Test radical */ + if (remHi >= testDiv) + { + remHi -= testDiv; + root++; + } + } while (count-- != 0); + + return root; +} + +static inline unsigned int +eina_f16p16_fracc_get(Eina_F16p16 v) +{ + return (v & 0xffff); +} + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_f32p32.x b/tests/suite/ecore/src/include/eina_inline_f32p32.x new file mode 100644 index 0000000000..73480de02c --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_f32p32.x @@ -0,0 +1,110 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2009 Jorge Luis Zapata Muga, Cedric BAIL + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_F32P32_X_ +# define EINA_INLINE_F32P32_X_ + +#include <stdlib.h> + +static inline Eina_F32p32 +eina_f32p32_add(Eina_F32p32 a, Eina_F32p32 b) +{ + return a + b; +} + +static inline Eina_F32p32 +eina_f32p32_sub(Eina_F32p32 a, Eina_F32p32 b) +{ + return a - b; +} + +static inline Eina_F32p32 +eina_f32p32_mul(Eina_F32p32 a, Eina_F32p32 b) +{ + /* Prevent overflow and do '(a * b) >> 32' */ + /* Basically do: Eina_F16p16 * Eina_F16p16 = Eina_F32p32 */ + Eina_F32p32 up; + Eina_F32p32 down; + Eina_F32p32 result; + uint64_t as, bs; + Eina_F32p32 sign; + + sign = a ^ b; + as = eina_fp32p32_llabs(a); + bs = eina_fp32p32_llabs(b); + + up = (as >> 16) * (bs >> 16); + down = (as & 0xFFFF) * (bs & 0xFFFF); + + result = up + (down >> 32); + + return sign < 0 ? - result : result; +} + +static inline Eina_F32p32 +eina_f32p32_scale(Eina_F32p32 a, int b) +{ + return a * b; +} + +static inline Eina_F32p32 +eina_f32p32_div(Eina_F32p32 a, Eina_F32p32 b) +{ + Eina_F32p32 sign; + Eina_F32p32 result; + + sign = a ^ b; + + if (b == 0) + return sign < 0 ? (Eina_F32p32) 0x8000000000000000ull : (Eina_F32p32) 0x7FFFFFFFFFFFFFFFull; + + result = (eina_f32p32_mul(eina_fp32p32_llabs(a), (((uint64_t) 1 << 62) / ((uint64_t)(eina_fp32p32_llabs(b)) >> 2)))); + + return sign < 0 ? - result : result; +} + +static inline Eina_F32p32 +eina_f32p32_sqrt(Eina_F32p32 a) +{ + uint64_t root, remHi, remLo, testDiv, count; + + root = 0; /* Clear root */ + remHi = 0; /* Clear high part of partial remainder */ + remLo = a; /* Get argument into low part of partial remainder */ + count = (31 + (32 >> 1)); /* Load loop counter */ + do { + remHi = (remHi << 2) | (remLo >> 30); + remLo <<= 2; /* get 2 bits of arg */ + root <<= 1; /* Get ready for the next bit in the root */ + testDiv = (root << 1) + 1; /* Test radical */ + if (remHi >= testDiv) { + remHi -= testDiv; + root++; + } + } while (count-- != 0); + + return root; +} + +static inline unsigned int +eina_f32p32_fracc_get(Eina_F32p32 v) +{ + return (unsigned int)v; +} + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_f8p24.x b/tests/suite/ecore/src/include/eina_inline_f8p24.x new file mode 100644 index 0000000000..f80bf6149c --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_f8p24.x @@ -0,0 +1,82 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * Copyright (C) 2009 Cedric BAIL + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_F8P24_X_ +#define EINA_INLINE_F8P24_X_ + +static inline Eina_F8p24 +eina_f8p24_add(Eina_F8p24 a, Eina_F8p24 b) +{ + return a + b; +} + +static inline Eina_F8p24 +eina_f8p24_sub(Eina_F8p24 a, Eina_F8p24 b) +{ + return a - b; +} + +static inline Eina_F8p24 +eina_f8p24_mul(Eina_F8p24 a, Eina_F8p24 b) +{ + return (Eina_F8p24)(((int64_t) a * (int64_t) b) >> 24); +} + +static inline Eina_F8p24 +eina_f8p24_scale(Eina_F8p24 a, int b) +{ + return a * b; +} + +static inline Eina_F8p24 +eina_f8p24_div(Eina_F8p24 a, Eina_F8p24 b) +{ + return (Eina_F8p24) ((((int64_t) a) << 24) / (int64_t) b); +} + +static inline Eina_F8p24 +eina_f8p24_sqrt(Eina_F8p24 a) +{ + unsigned int root, remHi, remLo, testDiv, count; + + root = 0; /* Clear root */ + remHi = 0; /* Clear high part of partial remainder */ + remLo = a; /* Get argument into low part of partial remainder */ + count = (23 + (24 >> 1)); /* Load loop counter */ + do { + remHi = (remHi << 2) | (remLo >> 30); + remLo <<= 2; /* get 2 bits of arg */ + root <<= 1; /* Get ready for the next bit in the root */ + testDiv = (root << 1) + 1; /* Test radical */ + if (remHi >= testDiv) + { + remHi -= testDiv; + root++; + } + } while (count-- != 0); + return (root); +} + +static inline unsigned int +eina_f8p24_fracc_get(Eina_F8p24 v) +{ + return (v & 0xffffff); +} + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_fp.x b/tests/suite/ecore/src/include/eina_inline_fp.x new file mode 100644 index 0000000000..de44123b28 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_fp.x @@ -0,0 +1,153 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * Copyright (C) 2009 Cedric BAIL + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_FP_X_ +# define EINA_INLINE_FP_X_ + +static inline Eina_F32p32 +eina_f32p32_int_from(int32_t v) +{ + return (Eina_F32p32)(v) << 32; +} + +static inline int32_t +eina_f32p32_int_to(Eina_F32p32 v) +{ + return (int32_t)(v >> 32); +} + +static inline Eina_F32p32 +eina_f32p32_double_from(double v) +{ + Eina_F32p32 r; + r = (Eina_F32p32)(v * 4294967296.0 + (v < 0 ? -0.5 : 0.5)); + return r; +} + +static inline double +eina_f32p32_double_to(Eina_F32p32 v) +{ + double r; + r = v / 4294967296.0; + return r; +} + + + +static inline Eina_F16p16 +eina_f16p16_int_from(int32_t v) +{ + return v << 16; +} + +static inline int32_t +eina_f16p16_int_to(Eina_F16p16 v) +{ + return v >> 16; +} + +static inline Eina_F16p16 +eina_f16p16_float_from(float v) +{ + Eina_F16p16 r; + + r = (Eina_F16p16)(v * 65536.0f + (v < 0 ? -0.5f : 0.5f)); + return r; +} + +static inline float +eina_f16p16_float_to(Eina_F16p16 v) +{ + float r; + + r = v / 65536.0f; + return r; +} + + + +static inline Eina_F8p24 +eina_f8p24_int_from(int32_t v) +{ + return v << 24; +} + +static inline int32_t +eina_f8p24_int_to(Eina_F8p24 v) +{ + return v >> 24; +} + +static inline Eina_F8p24 +eina_f8p24_float_from(float v) +{ + Eina_F8p24 r; + + r = (Eina_F8p24)(v * 16777216.0f + (v < 0 ? -0.5f : 0.5f)); + return r; +} + +static inline float +eina_f8p24_float_to(Eina_F8p24 v) +{ + float r; + + r = v / 16777216.0f; + return r; +} + + + +static inline Eina_F32p32 +eina_f16p16_to_f32p32(Eina_F16p16 a) +{ + return ((Eina_F32p32) a) << 16; +} + +static inline Eina_F32p32 +eina_f8p24_to_f32p32(Eina_F8p24 a) +{ + return ((Eina_F32p32) a) << 8; +} + +static inline Eina_F16p16 +eina_f32p32_to_f16p16(Eina_F32p32 a) +{ + return (Eina_F16p16) (a >> 16); +} + +static inline Eina_F16p16 +eina_f8p24_to_f16p16(Eina_F8p24 a) +{ + return (Eina_F16p16) (a >> 8); +} + +static inline Eina_F8p24 +eina_f32p32_to_f8p24(Eina_F32p32 a) +{ + return (Eina_F8p24) (a >> 8); +} + +static inline Eina_F8p24 +eina_f16p16_to_f8p24(Eina_F16p16 a) +{ + return (Eina_F8p24) (a << 8); +} + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_hash.x b/tests/suite/ecore/src/include/eina_inline_hash.x new file mode 100644 index 0000000000..8aa7bec865 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_hash.x @@ -0,0 +1,88 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_HASH_X_ +#define EINA_INLINE_HASH_X_ + +/* + djb2 hash algorithm was first reported by dan bernstein, and was the old + default hash function for evas. + */ +static inline int +eina_hash_djb2(const char *key, int len) +{ + unsigned int hash_num = 5381; + const unsigned char *ptr; + + if (!key) return 0; + for (ptr = (unsigned char *)key; len; ptr++, len--) + hash_num = ((hash_num << 5) + hash_num) ^ *ptr; /* hash * 33 ^ c */ + + return (int)hash_num; +} + +static inline int +eina_hash_djb2_len(const char *key, int *plen) +{ + unsigned int hash_num = 5381; + int len = 0; + const unsigned char *ptr; + + if (!key) return 0; + + for (ptr = (unsigned char *)key; *ptr; ptr++, len++) + hash_num = ((hash_num << 5) + hash_num) ^ *ptr; /* hash * 33 ^ c */ + + *plen = len + 1; + + return (int)hash_num; +} + +static inline int +eina_hash_int32(const unsigned int *pkey, int len) +{ + unsigned int key = *pkey; + + (void) len; + + key = ~key + (key << 15); + key = key ^ (key >> 12); + key = key + (key << 2); + key = key ^ (key >> 4); + key = key * 2057; + key = key ^ (key >> 16); + return key; +} + +static inline int +eina_hash_int64(const unsigned long int *pkey, int len) +{ + unsigned long int key = *pkey; + + (void) len; + + key = (~key) + (key << 18); + key = key ^ (key >> 31); + key = key * 21; + key = key ^ (key >> 11); + key = key + (key << 6); + key = key ^ (key >> 22); + return (int) key; +} + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_list.x b/tests/suite/ecore/src/include/eina_inline_list.x new file mode 100644 index 0000000000..832537097e --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_list.x @@ -0,0 +1,145 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_LIST_INLINE_H_ +#define EINA_LIST_INLINE_H_ + +/** + * @addtogroup Eina_List_Group List + * + * @brief These functions provide list management. + * + * @{ + */ + +/** + * @brief Get the last list node in the list. + * + * @param list The list to get the last list node from. + * @return The last list node in the list. + * + * This function returns the last list node in the list @p list. If + * @p list is @c NULL or empty, @c NULL is returned. + * + * This is a order-1 operation (it takes the same short time + * regardless of the length of the list). + */ +static inline Eina_List * +eina_list_last(const Eina_List *list) +{ + if (!list) return NULL; + return list->accounting->last; +} + +/** + * @brief Get the next list node after the specified list node. + * + * @param list The list node to get the next list node from + * @return The next list node on success, @c NULL otherwise. + * + * This function returns the next list node after the current one in + * @p list. It is equivalent to list->next. If @p list is @c NULL or + * if no next list node exists, it returns @c NULL. + */ +static inline Eina_List * +eina_list_next(const Eina_List *list) +{ + if (!list) return NULL; + return list->next; +} + +/** + * @brief Get the previous list node before the specified list node. + * + * @param list The list node to get the previous list node from. + * @return The previous list node o success, @c NULL otherwise. + * if no previous list node exists + * + * This function returns the previous list node before the current one + * in @p list. It is equivalent to list->prev. If @p list is @c NULL or + * if no previous list node exists, it returns @c NULL. + */ +static inline Eina_List * +eina_list_prev(const Eina_List *list) +{ + if (!list) return NULL; + return list->prev; +} + +/** + * @brief Get the list node data member. + * + * @param list The list node to get the data member of. + * @return The data member from the list node. + * + * This function returns the data member of the specified list node @p + * list. It is equivalent to list->data. If @p list is @c NULL, this + * function returns @c NULL. + */ +static inline void * +eina_list_data_get(const Eina_List *list) +{ + if (!list) return NULL; + return list->data; +} + +/** + * @brief Set the list node data member. + * + * @param list The list node to get the data member of. + * @param data The data member to the list node. + * @return The previous data value. + * + * This function set the data member @p data of the specified list node + * @p list. It returns the previous data of the node. If @p list is + * @c NULL, this function returns @c NULL. + */ +static inline void * +eina_list_data_set(Eina_List *list, const void *data) +{ + void *tmp; + if (!list) return NULL; + tmp = list->data; + list->data = (void*) data; + return tmp; +} + +/** + * @brief Get the count of the number of items in a list. + * + * @param list The list whose count to return. + * @return The number of members in the list. + * + * This function returns how many members @p list contains. If the + * list is @c NULL, 0 is returned. + * + * NB: This is an order-1 operation and takes the same time regardless + * of the length of the list. + */ +static inline unsigned int +eina_list_count(const Eina_List *list) +{ + if (!list) return 0; + return list->accounting->count; +} + +/** + * @} + */ + +#endif /* EINA_LIST_INLINE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_inline_log.x b/tests/suite/ecore/src/include/eina_inline_log.x new file mode 100644 index 0000000000..4cdd7d8393 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_log.x @@ -0,0 +1,197 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Gustavo Sverzut Barbieri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_LOG_INLINE_H_ +#define EINA_LOG_INLINE_H_ + +/** + * @addtogroup Eina_Log_Group Log + * + * @{ + */ + +/** + * Checks whenever the given level should be printed out. + * + * This is useful to enable certain blocks of code just when given + * level is to be used. + * + * @code + * #include <Eina.h> + * + * void my_func(void *data) + * { + * if (eina_log_level_check(EINA_LOG_LEVEL_WARN)) + * expensive_debugging_code(data); + * + * my_func_code(data); + * } + * @endcode + * + * @return #EINA_TRUE if level is equal or smaller than the current global + * logging level. + */ +static inline Eina_Bool +eina_log_level_check(int level) +{ + return eina_log_level_get() <= level; +} + +/** + * Checks whenever the given level should be printed out. + * + * This is useful to enable certain blocks of code just when given + * level is to be used. + * + * @code + * #include <Eina.h> + * + * extern int _my_log_dom; + * + * void my_func(void *data) + * { + * if (eina_log_domain_level_check(_my_log_dom, EINA_LOG_LEVEL_WARN)) + * expensive_debugging_code(data); + * + * my_func_code(data); + * } + * @endcode + * + * @return #EINA_TRUE if level is equal or smaller than the current + * domain logging level. + */ +static inline Eina_Bool +eina_log_domain_level_check(int domain, int level) +{ + int dom_level = eina_log_domain_registered_level_get(domain); + if (EINA_UNLIKELY(dom_level == EINA_LOG_LEVEL_UNKNOWN)) + return EINA_FALSE; + return dom_level <= level; +} + +/** + * Function to format the level as a 3 character (+1 null byte) string. + * + * This function converts the given level to a known string name (CRI, + * ERR, WRN, INF or DBG) or a zero-padded 3-character string. In any + * case the last byte will contain a trailing null byte. + * + * If extreme level values are used (greater than 999 and smaller than + * -99), then the value will just consider the less significant + * part. This is so uncommon that users should handle this in their + * code. + * + * @param level what level value to use. + * @param name where to write the actual value. + * + * @return pointer to @p name. + */ +static inline const char * +eina_log_level_name_get(int level, char name[4]) +{ +#define BCPY(A, B, C) \ + do { name[0] = A; name[1] = B; name[2] = C; } while (0) + + if (EINA_UNLIKELY(level < 0)) + { + name[0] = '-'; + name[1] = '0' + (-level / 10) % 10; + name[2] = '0' + (-level % 10); + } + else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS)) + { + name[0] = '0' + (level / 100) % 10; + name[1] = '0' + (level / 10) % 10; + name[2] = '0' + level % 10; + } + else if (level == 0) + BCPY('C', 'R', 'I'); + else if (level == 1) + BCPY('E', 'R', 'R'); + else if (level == 2) + BCPY('W', 'R', 'N'); + else if (level == 3) + BCPY('I', 'N', 'F'); + else if (level == 4) + BCPY('D', 'B', 'G'); + else + BCPY('?', '?', '?'); + + name[3] = '\0'; + + return name; +} + +/** + * Function to get recommended color value for level. + * + * This function will not check if colors are enabled or not before + * returning the level color. If you desire such check, use + * eina_log_level_color_if_enabled_get(). + * + * @param level what level value to use. + * + * @return pointer to null byte terminated ANSI color string to be + * used in virtual terminals supporting VT100 color codes. + * + * @see eina_log_level_color_if_enabled_get() + */ +static inline const char * +eina_log_level_color_get(int level) +{ + if (level <= 0) + return EINA_COLOR_LIGHTRED; + else if (level == 1) + return EINA_COLOR_RED; + else if (level == 2) + return EINA_COLOR_YELLOW; + else if (level == 3) + return EINA_COLOR_GREEN; + else if (level == 4) + return EINA_COLOR_LIGHTBLUE; + else + return EINA_COLOR_BLUE; +} + +/** + * Function to get recommended color value for level, if colors are + * enabled. + * + * This function will check if colors are enabled or not before + * returning the level color. If colors are disabled, then empty + * string is returned. + * + * @param level what level value to use. + * + * @return pointer to null byte terminated ANSI color string to be + * used in virtual terminals supporting VT100 color codes. If + * colors are disabled, the empty string is returned. + */ +static inline const char * +eina_log_level_color_if_enabled_get(int level) +{ + if (eina_log_color_disable_get()) + return ""; + return eina_log_level_color_get(level); +} + +/** + * @} + */ + +#endif /* EINA_LOG_INLINE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_inline_mempool.x b/tests/suite/ecore/src/include/eina_inline_mempool.x new file mode 100644 index 0000000000..3f44b901d5 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_mempool.x @@ -0,0 +1,105 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_MEMPOOL_X_ +#define EINA_INLINE_MEMPOOL_X_ + +/** + * @addtogroup Eina_Memory_Pool_Group Memory Pool + * + * @{ + */ + +/* Memory Pool */ +struct _Eina_Mempool_Backend +{ + const char *name; + void *(*init)(const char *context, const char *options, va_list args); + void (*free)(void *data, void *element); + void *(*alloc)(void *data, unsigned int size); + void *(*realloc)(void *data, void *element, unsigned int size); + void (*garbage_collect)(void *data); + void (*statistics)(void *data); + void (*shutdown)(void *data); +}; + +struct _Eina_Mempool +{ + Eina_Mempool_Backend backend; + void *backend_data; +}; + +/** + * @brief Re-allocate a amount memory by the given mempool. + * + * @param mp The mempool. + * @param element The element to re-allocate. + * @param size The size in bytes to re-allocate. + * @return The newly re-allocated data. + * + * This function re-allocates @p element with @p size bytes, using the + * mempool @p mp and returns the allocated data. If not used anymore, + * the data must be freed with eina_mempool_free(). No check is done + * on @p mp, so it must be a valid mempool. + */ +static inline void * +eina_mempool_realloc(Eina_Mempool *mp, void *element, unsigned int size) +{ + return mp->backend.realloc(mp->backend_data, element, size); +} + +/** + * @brief Allocate a amount memory by the given mempool. + * + * @param mp The mempool. + * @param size The size in bytes to allocate. + * @return The newly allocated data. + * + * This function allocates @p size bytes, using the mempool @p mp and + * returns the allocated data. If not used anymore, the data must be + * freed with eina_mempool_free(). No check is done on @p mp, so it + * must be a valid mempool. + */ +static inline void * +eina_mempool_malloc(Eina_Mempool *mp, unsigned int size) +{ + return mp->backend.alloc(mp->backend_data, size); +} + +/** + * @brief Free the allocated ressources by the given mempool. + * + * @param mp The mempool. + * @param element The data to free. + * + * This function frees @p element allocated by @p mp. @p element must + * have been obtained by eina_mempool_malloc() or + * eina_mempool_realloc(). No check is done on @p mp, so it must be a + * valid mempool. + */ +static inline void +eina_mempool_free(Eina_Mempool *mp, void *element) +{ + mp->backend.free(mp->backend_data, element); +} + +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_rbtree.x b/tests/suite/ecore/src/include/eina_inline_rbtree.x new file mode 100644 index 0000000000..954774b851 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_rbtree.x @@ -0,0 +1,50 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Cedric BAIL + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_RBTREE_INLINE_H_ +#define EINA_RBTREE_INLINE_H_ + +/** + * @addtogroup Eina_Rbtree_Group Red-Black tree + * + * @brief These functions provide Red-Black trees management. + * + * @{ + */ + +static inline Eina_Rbtree * +eina_rbtree_inline_lookup(const Eina_Rbtree *root, const void *key, int length, Eina_Rbtree_Cmp_Key_Cb cmp, const void *data) +{ + int result; + + while (root) + { + result = cmp(root, key, length, (void*) data); + if (result == 0) return (Eina_Rbtree*) root; + + root = root->son[result < 0 ? 0 : 1]; + } + + return NULL; +} + +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_rectangle.x b/tests/suite/ecore/src/include/eina_inline_rectangle.x new file mode 100644 index 0000000000..29ad24b7ca --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_rectangle.x @@ -0,0 +1,254 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_RECTANGLE_H__ +#define EINA_INLINE_RECTANGLE_H__ + +/** + * @addtogroup Eina_Rectangle_Group Rectangle + * + * @brief These functions provide rectangle management. + * + * @{ + */ + +/** + * @brief Check if the given spans intersect. + * + * @param c1 The column of the first span. + * @param l1 The length of the first span. + * @param c2 The column of the second span. + * @param l2 The length of the second span. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function returns #EINA_TRUE if the given spans intersect, + * #EINA_FALSE otherwise. + */ +static inline int +eina_spans_intersect(int c1, int l1, int c2, int l2) +{ + return (!(((c2 + l2) <= c1) || (c2 >= (c1 + l1)))); +} + +/** + * @brief Check if the given rectangle is empty. + * + * @param r The rectangle to check. + * @return #EINA_TRUE if the rectangle is empty, #EINA_FALSE otherwise. + * + * This function returns #EINA_TRUE if @p r is empty, #EINA_FALSE + * otherwise. No check is done on @p r, so it must be a valid + * rectangle. + */ +static inline Eina_Bool +eina_rectangle_is_empty(const Eina_Rectangle *r) +{ + return ((r->w < 1) || (r->h < 1)) ? EINA_TRUE : EINA_FALSE; +} + +/** + * @brief Set the coordinates and size of the given rectangle. + * + * @param r The rectangle. + * @param x The top-left x coordinate of the rectangle. + * @param y The top-left y coordinate of the rectangle. + * @param w The width of the rectangle. + * @param h The height of the rectangle. + * + * This function sets its top-left x coordinate to @p x, its top-left + * y coordinate to @p y, its width to @p w and its height to @p h. No + * check is done on @p r, so it must be a valid rectangle. + */ +static inline void +eina_rectangle_coords_from(Eina_Rectangle *r, int x, int y, int w, int h) +{ + r->x = x; + r->y = y; + r->w = w; + r->h = h; +} + +/** + * @brief Check if the given rectangles intersect. + * + * @param r1 The first rectangle. + * @param r2 The second rectangle. + * @return #EINA_TRUE if the rectangles intersect, #EINA_FALSE otherwise. + * + * This function returns #EINA_TRUE if @p r1 and @p r2 intersect, + * #EINA_FALSE otherwise. No check is done on @p r1 and @p r2, so they + * must be valid rectangles. + */ +static inline Eina_Bool +eina_rectangles_intersect(const Eina_Rectangle *r1, const Eina_Rectangle *r2) +{ + return (eina_spans_intersect(r1->x, r1->w, r2->x, r2->w) && eina_spans_intersect(r1->y, r1->h, r2->y, r2->h)) ? EINA_TRUE : EINA_FALSE; +} + +/** + * @brief Check if the given x-coordinate is in the rectangle . + * + * @param r The rectangle. + * @param x The x coordinate. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function returns #EINA_TRUE if @p x is in @p r with respect to + * the horizontal direction, #EINA_FALSE otherwise. No check is done + * on @p r, so it must be a valid rectangle. + */ +static inline Eina_Bool +eina_rectangle_xcoord_inside(const Eina_Rectangle *r, int x) +{ + return ((x >= r->x) && (x < (r->x + r->w))) ? EINA_TRUE : EINA_FALSE; +} + +/** + * @brief Check if the given y-coordinate is in the rectangle . + * + * @param r The rectangle. + * @param y The y coordinate. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function returns #EINA_TRUE if @p y is in @p r with respect to + * the vertical direction, #EINA_FALSE otherwise. No check is done + * on @p r, so it must be a valid rectangle. + */ +static inline Eina_Bool +eina_rectangle_ycoord_inside(const Eina_Rectangle *r, int y) +{ + return ((y >= r->y) && (y < (r->y + r->h))) ? EINA_TRUE : EINA_FALSE; +} + +/** + * @brief Check if the given point is in the rectangle . + * + * @param r The rectangle. + * @param x The x coordinate of the point. + * @param y The y coordinate of the point. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function returns #EINA_TRUE if the point of coordinate (@p x, + * @p y) is in @p r, #EINA_FALSE otherwise. No check is done on @p r, + * so it must be a valid rectangle. + */ +static inline Eina_Bool +eina_rectangle_coords_inside(const Eina_Rectangle *r, int x, int y) +{ + return (eina_rectangle_xcoord_inside(r, x) && eina_rectangle_ycoord_inside(r, y)) ? EINA_TRUE : EINA_FALSE; +} + +/** + * @brief Get the union of two rectangles. + * + * @param dst The first rectangle. + * @param src The second rectangle. + * + * This function get the union of the rectangles @p dst and @p src. The + * result is stored in @p dst. No check is done on @p dst or @p src, + * so they must be valid rectangles. + */ +static inline void +eina_rectangle_union(Eina_Rectangle *dst, const Eina_Rectangle *src) +{ + /* left */ + if (dst->x > src->x) + { + dst->w += dst->x - src->x; + dst->x = src->x; + } + /* right */ + if ((dst->x + dst->w) < (src->x + src->w)) + dst->w = src->x + src->w; + /* top */ + if (dst->y > src->y) + { + dst->h += dst->y - src->y; + dst->y = src->y; + } + /* bottom */ + if ((dst->y + dst->h) < (src->y + src->h)) + dst->h = src->y + src->h; +} + +/** + * @brief Get the intersection of two rectangles. + * + * @param dst The first rectangle. + * @param src The second rectangle. + * @return #EINA_TRUE if the rectangles intersect, #EINA_FALSE + * otherwise. + * + * This function get the intersection of the rectangles @p dst and + * @p src. The result is stored in @p dst. No check is done on @p dst + * or @p src, so they must be valid rectangles. + */ +static inline Eina_Bool +eina_rectangle_intersection(Eina_Rectangle *dst, const Eina_Rectangle *src) +{ + if (!(eina_rectangles_intersect(dst, src))) + return EINA_FALSE; + + /* left */ + if (dst->x < src->x) + { + dst->w += dst->x - src->x; + dst->x = src->x; + if (dst->w < 0) + dst->w = 0; + } + /* right */ + if ((dst->x + dst->w) > (src->x + src->w)) + dst->w = src->x + src->w - dst->x; + /* top */ + if (dst->y < src->y) + { + dst->h += dst->y - src->y; + dst->y = src->y; + if (dst->h < 0) + dst->h = 0; + } + /* bottom */ + if ((dst->y + dst->h) > (src->y + src->h)) + dst->h = src->y + src->h - dst->y; + + return EINA_TRUE; +} + +static inline void +eina_rectangle_rescale_in(const Eina_Rectangle *out, const Eina_Rectangle *in, Eina_Rectangle *res) +{ + res->x = in->x - out->x; + res->y = in->y - out->y; + res->w = in->w; + res->h = in->h; +} + +static inline void +eina_rectangle_rescale_out(const Eina_Rectangle *out, const Eina_Rectangle *in, Eina_Rectangle *res) +{ + res->x = out->x + in->x; + res->y = out->y + in->y; + res->w = out->w; + res->h = out->h; +} + +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_str.x b/tests/suite/ecore/src/include/eina_inline_str.x new file mode 100644 index 0000000000..2daeb8524c --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_str.x @@ -0,0 +1,76 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Gustavo Sverzut Barbieri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_STR_INLINE_H_ +#define EINA_STR_INLINE_H_ + +/** + * @addtogroup Eina_String_Group String + * + * @{ + */ + +/** + * @brief Count up to a given amount of bytes of the given string. + * + * @param str The string pointer. + * @param maxlen The maximum length to allow. + * @return the string size or (size_t)-1 if greater than @a maxlen. + * + * This function returns the size of @p str, up to @p maxlen + * characters. It avoid needless iterations after that size. @p str + * must be a valid pointer and MUST not be @c NULL, otherwise this + * function will crash. This function returns the string size, or + * (size_t)-1 if the size is greater than @a maxlen. + */ +static inline size_t +eina_strlen_bounded(const char *str, size_t maxlen) +{ + const char *itr, *str_maxend = str + maxlen; + for (itr = str; *itr != '\0'; itr++) + if (itr == str_maxend) return (size_t)-1; + return itr - str; +} + +/** + * @brief Join two strings of known length. + * + * @param dst The buffer to store the result. + * @param size Size (in byte) of the buffer. + * @param sep The separator character to use. + * @param a First string to use, before @p sep. + * @param b Second string to use, after @p sep. + * @return The number of characters printed. + * + * This function is similar to eina_str_join_len(), but will compute + * the length of @p a and @p b using strlen(). + * + * @see eina_str_join_len() + * @see eina_str_join_static() + */ +static inline size_t +eina_str_join(char *dst, size_t size, char sep, const char *a, const char *b) +{ + return eina_str_join_len(dst, size, sep, a, strlen(a), b, strlen(b)); +} + +/** + * @} + */ + +#endif /* EINA_STR_INLINE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_inline_stringshare.x b/tests/suite/ecore/src/include/eina_inline_stringshare.x new file mode 100644 index 0000000000..bfd7677341 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_stringshare.x @@ -0,0 +1,91 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Gustavo Sverzut Barbieri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_STRINGSHARE_INLINE_H_ +#define EINA_STRINGSHARE_INLINE_H_ + +#include <string.h> +#include "eina_stringshare.h" +/** + * @addtogroup Eina_Stringshare_Group Stringshare + * + * @{ + */ + +/** + * Replace the previously stringshared pointer with new content. + * + * The string pointed by @a p_str should be previously stringshared or + * @c NULL and it will be eina_stringshare_del(). The new string will + * be passed to eina_stringshare_add() and then assigned to @c *p_str. + * + * @param p_str pointer to the stringhare to be replaced. Must not be + * @c NULL, but @c *p_str may be @c NULL as it is a valid + * stringshare handle. + * @param news new string to be stringshared, may be @c NULL. + * + * @return #EINA_TRUE if the strings were different and thus replaced, + * #EINA_FALSE if the strings were the same after shared. + */ +static inline Eina_Bool +eina_stringshare_replace(const char **p_str, const char *news) +{ + if (*p_str == news) return EINA_FALSE; + + news = eina_stringshare_add(news); + eina_stringshare_del(*p_str); + if (*p_str == news) + return EINA_FALSE; + *p_str = news; + return EINA_TRUE; +} + +/** + * Replace the previously stringshared pointer with a new content. + * + * The string pointed by @a p_str should be previously stringshared or + * @c NULL and it will be eina_stringshare_del(). The new string will + * be passed to eina_stringshare_add_length() and then assigned to @c *p_str. + * + * @param p_str pointer to the stringhare to be replaced. Must not be + * @c NULL, but @c *p_str may be @c NULL as it is a valid + * stringshare handle. + * @param news new string to be stringshared, may be @c NULL. + * @param slen The string size (<= strlen(str)). + * + * @return #EINA_TRUE if the strings were different and thus replaced, + * #EINA_FALSE if the strings were the same after shared. + */ +static inline Eina_Bool +eina_stringshare_replace_length(const char **p_str, const char *news, unsigned int slen) +{ + if (*p_str == news) return EINA_FALSE; + + news = eina_stringshare_add_length(news, slen); + eina_stringshare_del(*p_str); + if (*p_str == news) + return EINA_FALSE; + *p_str = news; + return EINA_TRUE; +} + +/** + * @} + */ + +#endif /* EINA_STRINGSHARE_INLINE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_inline_tiler.x b/tests/suite/ecore/src/include/eina_inline_tiler.x new file mode 100644 index 0000000000..ffc34c1cf6 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_tiler.x @@ -0,0 +1,182 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2009 Rafael Antognolli + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_TILER_INLINE_H_ +#define EINA_TILER_INLINE_H_ + +#include "eina_safety_checks.h" + +/** + * @cond LOCAL + * This struct should not be accessed directly, it is used by + * eina_tile_grid_slicer functions to maintain context and fill "info" + * member with correct values for given iteration. + */ +struct _Eina_Tile_Grid_Slicer +{ + unsigned long col1, col2, row1, row2; // initial and final col,row + int tile_w, tile_h; // tile width, height + int x_rel, y_rel; // starting x,y coordinates of the first col,row + int w1_rel, h1_rel; // width,height of the first col,row + int w2_rel, h2_rel; // width,height of the last col,row + struct Eina_Tile_Grid_Info info; // info about the current tile + Eina_Bool first; +}; + +/** + * @endcond + */ + +/** + * @brief Iterates over the tiles set by eina_tile_grid_slicer_setup(). + * + * @param slc Pointer to an Eina_Tile_Grid_Slicer struct. + * @param rect Pointer to a struct Eina_Tile_Grid_Info *. + * @return @c EINA_TRUE if the current rect is valid. + * @c EINA_FALSE if there is no more rects to iterate over (and + * thus the current one isn't valid). + * + * This functions iterates over each Eina_Tile_Grid_Info *rect of the grid. + * eina_tile_grid_slicer_setup() must be called first, and *rect is only valid + * if this function returns EINA_TRUE. Its content shouldn't be modified. + */ +static inline Eina_Bool +eina_tile_grid_slicer_next(Eina_Tile_Grid_Slicer *slc, const Eina_Tile_Grid_Info **rect) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(slc, 0); + + if (slc->first) + { + slc->first = 0; + *rect = &slc->info; + return EINA_TRUE; + } + + slc->info.col++; + + if (slc->info.col > slc->col2) + { + slc->info.row++; + if (slc->info.row > slc->row2) + return EINA_FALSE; + else if (slc->info.row < slc->row2) + slc->info.rect.h = slc->tile_h; + else + slc->info.rect.h = slc->h2_rel; + slc->info.rect.y = 0; + slc->info.col = slc->col1; + slc->info.rect.x = slc->x_rel; + slc->info.rect.w = slc->w1_rel; + } + else + { + slc->info.rect.x = 0; + if (slc->info.col < slc->col2) + slc->info.rect.w = slc->tile_w; + else + slc->info.rect.w = slc->w2_rel; + } + + if (slc->info.rect.w == slc->tile_w && slc->info.rect.h == slc->tile_h) + slc->info.full = EINA_TRUE; + else + slc->info.full = EINA_FALSE; + + *rect = &slc->info; + + return EINA_TRUE; +} + +/** + * @brief Setup an Eina_Tile_Grid_Slicer struct. + * + * @param slc Pointer to an Eina_Tile_Grid_Slicer struct. + * @param x X axis coordinate. + * @param y Y axis coordinate. + * @param w width. + * @param h height. + * @param tile_w tile width. + * @param tile_h tile height. + * @return A pointer to the Eina_Iterator. + * @c NULL on failure. + * + * This function splits the rectangle given as argument into tiles of size + * tile_w X tile_h, and returns an iterator to them. The geometry of each + * tile can be accessed with eina_tile_grid_slicer_next, where rect + * will be a pointer to a struct Eina_Tile_Grid_Info. + */ +static inline Eina_Bool +eina_tile_grid_slicer_setup(Eina_Tile_Grid_Slicer *slc, int x, int y, int w, int h, int tile_w, int tile_h) +{ + int tx1, tx2, ty1, ty2; + + EINA_SAFETY_ON_NULL_RETURN_VAL(slc, 0); + + tx1 = x; + ty1 = y; + tx2 = x + w - 1; + ty2 = y + h - 1; + + if (x < 0 || y < 0 || w <= 0 || h <= 0 || tile_w <= 0 || tile_h <= 0) + { + slc->first = 0; + slc->col1 = slc->row1 = 0; + slc->col2 = slc->row2 = 0; + slc->info.col = slc->col1; + slc->info.row = slc->row1; + return EINA_TRUE; + } + + slc->col1 = tx1 / tile_w; + slc->row1 = ty1 / tile_h; + slc->col2 = (tx2 - 0) / tile_w; + slc->row2 = (ty2 - 0) / tile_h; + slc->x_rel = tx1 % tile_w; + slc->y_rel = ty1 % tile_h; + slc->w1_rel = tile_w - slc->x_rel; + slc->h1_rel = tile_h - slc->y_rel; + slc->w2_rel = tx2 % tile_w + 1; + slc->h2_rel = ty2 % tile_h + 1; + + slc->tile_w = tile_w; + slc->tile_h = tile_h; + + slc->first = 1; + slc->info.col = slc->col1; + slc->info.row = slc->row1; + slc->info.rect.x = slc->x_rel; + slc->info.rect.y = slc->y_rel; + + if (slc->info.col == slc->col2) + slc->w1_rel = slc->w2_rel - slc->x_rel; + + if (slc->info.row == slc->row2) + slc->h1_rel = slc->h2_rel - slc->y_rel; + + slc->info.rect.w = slc->w1_rel; + slc->info.rect.h = slc->h1_rel; + + if (slc->info.rect.w == slc->tile_w && slc->info.rect.h == slc->tile_h) + slc->info.full = EINA_TRUE; + else + slc->info.full = EINA_FALSE; + + return EINA_TRUE; +} + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_trash.x b/tests/suite/ecore/src/include/eina_inline_trash.x new file mode 100644 index 0000000000..4a50611174 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_trash.x @@ -0,0 +1,90 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLINE_TRASH_X__ +#define EINA_INLINE_TRASH_X__ + +/** + * @brief Initialize a trash before using it. + * + * @param trash The trash. + * + * This function just set to zero the trash to correctly + * initialize it. + * + * @note You can just set *trash to NULL and you will have + * the same result. + */ +static inline void +eina_trash_init(Eina_Trash **trash) +{ + *trash = NULL; +} + +/** + * @brief Push an unused pointer in the trash instead of freeing it. + * + * @param trash A pointer to an Eina_Trash. + * @param data An unused pointer big enougth to put a (void*). + * + * Instead of freeing a pointer and put pressure on malloc/free + * you can push it in a trash for a later use. This function just + * provide a fast way to push a now unused pointer into a trash. + * + * @note Do never use the pointer after insertion or bad things will + * happens. + * + * @note This trash will not resize, nor do anything with the size of + * the region pointed by @p data, so it's your duty to manage the size. + */ +static inline void +eina_trash_push(Eina_Trash **trash, void *data) +{ + Eina_Trash *tmp; + + tmp = (Eina_Trash *)data; + tmp->next = *trash; + *trash = tmp; +} + +/** + * @brief Pop an available pointer from the trash if possible. + * + * @param trash A pointer to an Eina_Trash. + * + * Instead of calling malloc, and putting pressure on malloc/free + * you can recycle the content of the trash, if it's not empty. + * + * @note This trash will not resize, nor do anything with the size of + * the region pointed by pointer inside the trash, so it's your duty + * to manage the size of the returned pointer. + */ +static inline void* +eina_trash_pop(Eina_Trash **trash) +{ + void *tmp; + + tmp = *trash; + + if (*trash) + *trash = (*trash)->next; + + return tmp; +} + +#endif diff --git a/tests/suite/ecore/src/include/eina_inline_ustringshare.x b/tests/suite/ecore/src/include/eina_inline_ustringshare.x new file mode 100644 index 0000000000..ace6fdc325 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inline_ustringshare.x @@ -0,0 +1,93 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Gustavo Sverzut Barbieri + Tom Hacohen + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_USTRINGSHARE_INLINE_H_ +#define EINA_USTRINGSHARE_INLINE_H_ + +#include "eina_unicode.h" +#include "eina_ustringshare.h" + +/** + * @addtogroup Eina_UStringshare_Group Unicode Stringshare + * + * @{ + */ + +/** + * Replace the previously stringshared pointer with new content. + * + * The string pointed by @a p_str should be previously stringshared or + * @c NULL and it will be eina_ustringshare_del(). The new string will + * be passed to eina_ustringshare_add() and then assigned to @c *p_str. + * + * @param p_str pointer to the stringhare to be replaced. Must not be + * @c NULL, but @c *p_str may be @c NULL as it is a valid + * stringshare handle. + * @param news new string to be stringshared, may be @c NULL. + * + * @return #EINA_TRUE if the strings were different and thus replaced, + * #EINA_FALSE if the strings were the same after shared. + */ +static inline Eina_Bool +eina_ustringshare_replace(const Eina_Unicode **p_str, const Eina_Unicode *news) +{ + if (*p_str == news) return EINA_FALSE; + + news = eina_ustringshare_add(news); + eina_ustringshare_del(*p_str); + if (*p_str == news) + return EINA_FALSE; + *p_str = news; + return EINA_TRUE; +} + +/** + * Replace the previously stringshared pointer with a new content. + * + * The string pointed by @a p_str should be previously stringshared or + * @c NULL and it will be eina_ustringshare_del(). The new string will + * be passed to eina_ustringshare_add_length() and then assigned to @c *p_str. + * + * @param p_str pointer to the stringhare to be replaced. Must not be + * @c NULL, but @c *p_str may be @c NULL as it is a valid + * stringshare handle. + * @param news new string to be stringshared, may be @c NULL. + * @param slen The string size (<= strlen(str)). + * + * @return #EINA_TRUE if the strings were different and thus replaced, + * #EINA_FALSE if the strings were the same after shared. + */ +static inline Eina_Bool +eina_ustringshare_replace_length(const Eina_Unicode **p_str, const Eina_Unicode *news, unsigned int slen) +{ + if (*p_str == news) return EINA_FALSE; + + news = eina_ustringshare_add_length(news, slen); + eina_ustringshare_del(*p_str); + if (*p_str == news) + return EINA_FALSE; + *p_str = news; + return EINA_TRUE; +} + +/** + * @} + */ + +#endif /* EINA_USTRINGSHARE_INLINE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_inlist.h b/tests/suite/ecore/src/include/eina_inlist.h new file mode 100644 index 0000000000..34ee0ed522 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_inlist.h @@ -0,0 +1,115 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_INLIST_H_ +#define EINA_INLIST_H_ + +#include "eina_types.h" +#include "eina_iterator.h" +#include "eina_accessor.h" +#include <stddef.h> + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @addtogroup Eina_Containers_Group Containers + * + * @{ + */ + +/** + * @defgroup Eina_Inline_List_Group Inline List + * + * @{ + */ + +/** + * @typedef Eina_Inlist + * Inlined list type. + */ +typedef struct _Eina_Inlist Eina_Inlist; + +/** + * @struct _Eina_Inlist + * Inlined list type. + */ +struct _Eina_Inlist +{ + Eina_Inlist *next; /**< next node */ + Eina_Inlist *prev; /**< previous node */ + Eina_Inlist *last; /**< last node */ +}; + +#define EINA_INLIST Eina_Inlist __in_list +#define EINA_INLIST_GET(Inlist) (& ((Inlist)->__in_list)) +#define EINA_INLIST_CONTAINER_GET(ptr, \ + type) ((type *)((char *)ptr - \ + offsetof(type, __in_list))) + +EAPI Eina_Inlist * eina_inlist_append(Eina_Inlist *in_list, + Eina_Inlist *in_item) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Inlist * eina_inlist_prepend(Eina_Inlist *in_list, + Eina_Inlist *in_item) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Inlist * eina_inlist_append_relative(Eina_Inlist *in_list, + Eina_Inlist *in_item, + Eina_Inlist *in_relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Inlist * eina_inlist_prepend_relative(Eina_Inlist *in_list, + Eina_Inlist *in_item, + Eina_Inlist *in_relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Inlist * eina_inlist_remove(Eina_Inlist *in_list, + Eina_Inlist *in_item) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Inlist * eina_inlist_find(Eina_Inlist *in_list, + Eina_Inlist *in_item) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Inlist * eina_inlist_promote(Eina_Inlist *list, + Eina_Inlist *item) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Inlist * eina_inlist_demote(Eina_Inlist *list, + Eina_Inlist *item) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI unsigned int eina_inlist_count(const Eina_Inlist *list) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Iterator *eina_inlist_iterator_new(const Eina_Inlist *in_list) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI Eina_Accessor *eina_inlist_accessor_new(const Eina_Inlist *in_list) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + +/* This two macros are helpers for the _FOREACH ones, don't use them */ +#define _EINA_INLIST_OFFSET(ref) ((char *)&(ref)->__in_list - (char *)(ref)) +#define _EINA_INLIST_CONTAINER(ref, ptr) (void *)((char *)(ptr) - \ + _EINA_INLIST_OFFSET(ref)) + +#define EINA_INLIST_FOREACH(list, l) \ + for (l = NULL, l = (list ? _EINA_INLIST_CONTAINER(l, list) : NULL); l; \ + l = (EINA_INLIST_GET(l)->next ? _EINA_INLIST_CONTAINER(l, EINA_INLIST_GET(l)->next) : NULL)) +#define EINA_INLIST_REVERSE_FOREACH(list, l) \ + for (l = NULL, l = (list ? _EINA_INLIST_CONTAINER(l, list->last) : NULL); \ + l; l = (EINA_INLIST_GET(l)->prev ? _EINA_INLIST_CONTAINER(l, EINA_INLIST_GET(l)->prev) : NULL)) + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#endif /*EINA_INLIST_H_*/ diff --git a/tests/suite/ecore/src/include/eina_iterator.h b/tests/suite/ecore/src/include/eina_iterator.h new file mode 100644 index 0000000000..15188be0fa --- /dev/null +++ b/tests/suite/ecore/src/include/eina_iterator.h @@ -0,0 +1,141 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_ITERATOR_H__ +#define EINA_ITERATOR_H__ + +#include "eina_config.h" + +#include "eina_types.h" +#include "eina_magic.h" + +/** + * @addtogroup Eina_Content_Access_Group Content Access + * + * @{ + */ + +/** + * @defgroup Eina_Iterator_Group Iterator Functions + * + * @{ + */ + +/** + * @typedef Eina_Iterator + * Type for iterators. + */ +typedef struct _Eina_Iterator Eina_Iterator; + +typedef Eina_Bool (*Eina_Iterator_Next_Callback)(Eina_Iterator *it, void **data); +typedef void *(*Eina_Iterator_Get_Container_Callback)(Eina_Iterator *it); +typedef void (*Eina_Iterator_Free_Callback)(Eina_Iterator *it); +typedef Eina_Bool (*Eina_Iterator_Lock_Callback)(Eina_Iterator *it); + +struct _Eina_Iterator +{ +#define EINA_ITERATOR_VERSION 1 + int version; + + Eina_Iterator_Next_Callback next EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + Eina_Iterator_Get_Container_Callback get_container EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + Eina_Iterator_Free_Callback free EINA_ARG_NONNULL(1); + + Eina_Iterator_Lock_Callback lock EINA_WARN_UNUSED_RESULT; + Eina_Iterator_Lock_Callback unlock EINA_WARN_UNUSED_RESULT; + +#define EINA_MAGIC_ITERATOR 0x98761233 + EINA_MAGIC +}; + + +#define FUNC_ITERATOR_NEXT(Function) ((Eina_Iterator_Next_Callback)Function) +#define FUNC_ITERATOR_GET_CONTAINER(Function) (( \ + Eina_Iterator_Get_Container_Callback) \ + Function) +#define FUNC_ITERATOR_FREE(Function) ((Eina_Iterator_Free_Callback)Function) +#define FUNC_ITERATOR_LOCK(Function) ((Eina_Iterator_Lock_Callback)Function) + +EAPI void eina_iterator_free (Eina_Iterator *iterator) EINA_ARG_NONNULL(1); + +EAPI void * eina_iterator_container_get (Eina_Iterator *iterator) EINA_ARG_NONNULL(1) EINA_PURE; +EAPI Eina_Bool eina_iterator_next (Eina_Iterator *iterator, + void **data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI void eina_iterator_foreach (Eina_Iterator *iterator, + Eina_Each_Cb callback, + const void *fdata) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool eina_iterator_lock(Eina_Iterator *iterator) EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_iterator_unlock(Eina_Iterator *iterator) EINA_ARG_NONNULL(1); + +/** + * @def EINA_ITERATOR_FOREACH + * @brief Macro to iterate over all elements easily. + * + * @param itr The iterator to use. + * @param data Where to store * data, must be a pointer support getting + * its address since * eina_iterator_next() requires a pointer + * to pointer! + * + * This macro is a convenient way to use iterators, very similar to + * EINA_LIST_FOREACH(). + * + * This macro can be used for freeing the data of a list, like in the + * following example. It has the same goal as the one documented in + * EINA_LIST_FOREACH(), but using iterators: + * + * @code + * Eina_List *list; + * Eina_Iterator *itr; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings + * + * itr = eina_list_iterator_new(list); + * EINA_ITERATOR_FOREACH(itr, data) + * free(data); + * eina_iterator_free(itr); + * eina_list_free(list); + * @endcode + * + * @note this example is not optimal algorithm to release a list since + * it will walk the list twice, but it serves as an example. For + * optimized version use EINA_LIST_FREE() + * + * @warning unless explicitly stated in functions returning iterators, + * do not modify the iterated object while you walk it, in this + * example using lists, do not remove list nodes or you might + * crash! This is not a limitiation of iterators themselves, + * rather in the iterators implementations to keep them as simple + * and fast as possible. + */ +#define EINA_ITERATOR_FOREACH(itr, \ + data) while (eina_iterator_next((itr), \ + (void **)&(data))) + +/** + * @} + */ + +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_lalloc.h b/tests/suite/ecore/src/include/eina_lalloc.h new file mode 100644 index 0000000000..d41a9edb83 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_lalloc.h @@ -0,0 +1,60 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_LALLOC_H_ +#define EINA_LALLOC_H_ + +#include "eina_types.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Lalloc_Group Lazy allocator + * + * @{ + */ + +typedef Eina_Bool (*Eina_Lalloc_Alloc)(void *user_data, int num); +#define EINA_LALLOC_ALLOC(function) ((Eina_Lalloc_Alloc)function) +typedef void (*Eina_Lalloc_Free)(void *user_data); +#define EINA_LALLOC_FREE(function) ((Eina_Lalloc_Free)function) + +typedef struct _Eina_Lalloc Eina_Lalloc; + +EAPI Eina_Lalloc *eina_lalloc_new(void *data, + Eina_Lalloc_Alloc alloc_cb, + Eina_Lalloc_Free free_cb, + int num_init) EINA_ARG_NONNULL(2, 3); +EAPI void eina_lalloc_free(Eina_Lalloc *a) EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_lalloc_elements_add(Eina_Lalloc *a, + int num) EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_lalloc_element_add(Eina_Lalloc *a) EINA_ARG_NONNULL(1); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_LALLOC_H_ */ diff --git a/tests/suite/ecore/src/include/eina_list.h b/tests/suite/ecore/src/include/eina_list.h new file mode 100644 index 0000000000..835422d418 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_list.h @@ -0,0 +1,349 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_LIST_H_ +#define EINA_LIST_H_ + +#include <stdlib.h> + +#include "eina_config.h" + +#include "eina_types.h" +#include "eina_iterator.h" +#include "eina_accessor.h" +#include "eina_magic.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @addtogroup Eina_Containers_Group Containers + * + * @{ + */ + +/** + * @defgroup Eina_List_Group List + * + * @{ + */ + +/** + * @typedef Eina_List + * Type for a generic double linked list. + */ +typedef struct _Eina_List Eina_List; + +typedef struct _Eina_List_Accounting Eina_List_Accounting; + +/** + * @struct _Eina_List + * Type for a generic double linked list. + */ +struct _Eina_List +{ + void *data; /**< Pointer to list element payload */ + Eina_List *next; /**< Next member in the list */ + Eina_List *prev; /**< Previous member in the list */ + Eina_List_Accounting *accounting; /**< Private list accounting info - don't touch */ + + EINA_MAGIC +}; + +struct _Eina_List_Accounting +{ + Eina_List *last; + unsigned int count; + EINA_MAGIC +}; + +EAPI Eina_List * eina_list_append (Eina_List *list, const void *data) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_prepend (Eina_List *list, const void *data) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_append_relative (Eina_List *list, const void *data, const void *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_append_relative_list (Eina_List *list, const void *data, Eina_List *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_prepend_relative (Eina_List *list, const void *data, const void *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_prepend_relative_list (Eina_List *list, const void *data, Eina_List *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_sorted_insert(Eina_List *list, Eina_Compare_Cb func, const void *data) EINA_ARG_NONNULL(2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_remove (Eina_List *list, const void *data) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_remove_list (Eina_List *list, Eina_List *remove_list) EINA_ARG_NONNULL( 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_promote_list (Eina_List *list, Eina_List *move_list) EINA_ARG_NONNULL( 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_demote_list (Eina_List *list, Eina_List *move_list); +EAPI void * eina_list_data_find(const Eina_List *list, const void *data) EINA_PURE EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_data_find_list (const Eina_List *list, const void *data) EINA_PURE EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_free (Eina_List *list); +EAPI void * eina_list_nth(const Eina_List *list, unsigned int n) EINA_PURE EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_nth_list (const Eina_List *list, unsigned int n) EINA_PURE EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_reverse (Eina_List *list) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_reverse_clone(const Eina_List *list) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_clone(const Eina_List *list) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_sort (Eina_List *list, unsigned int size, Eina_Compare_Cb func) EINA_ARG_NONNULL(3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_merge (Eina_List *left, Eina_List *right) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_sorted_merge(Eina_List *left, Eina_List *right, Eina_Compare_Cb func) EINA_ARG_NONNULL(3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_List * eina_list_split_list(Eina_List *list, Eina_List *relative, Eina_List **right) EINA_WARN_UNUSED_RESULT; + + +EAPI Eina_List * eina_list_search_sorted_near_list(const Eina_List *list, Eina_Compare_Cb func, const void *data, int *result_cmp); +EAPI Eina_List * eina_list_search_sorted_list(const Eina_List *list, Eina_Compare_Cb func, const void *data); +EAPI void * eina_list_search_sorted(const Eina_List *list, Eina_Compare_Cb func, const void *data); +EAPI Eina_List * eina_list_search_unsorted_list(const Eina_List *list, Eina_Compare_Cb func, const void *data); +EAPI void * eina_list_search_unsorted(const Eina_List *list, Eina_Compare_Cb func, const void *data); + +static inline Eina_List * eina_list_last (const Eina_List *list) EINA_PURE EINA_WARN_UNUSED_RESULT; +static inline Eina_List * eina_list_next (const Eina_List *list) EINA_PURE EINA_WARN_UNUSED_RESULT; +static inline Eina_List * eina_list_prev (const Eina_List *list) EINA_PURE EINA_WARN_UNUSED_RESULT; +static inline void * eina_list_data_get(const Eina_List *list) EINA_PURE EINA_WARN_UNUSED_RESULT; +static inline unsigned int eina_list_count(const Eina_List *list) EINA_PURE; + +EAPI Eina_Iterator * eina_list_iterator_new(const Eina_List *list) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI Eina_Iterator * eina_list_iterator_reversed_new(const Eina_List *list) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI Eina_Accessor * eina_list_accessor_new(const Eina_List *list) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + +/** + * @def EINA_LIST_FOREACH + * @brief Macro to iterate over a list. + * + * @param list The list to iterate over. + * @param l A list that is used as an iterator and points to the current node. + * @param data Current item's data. + * + * This macro iterates over @p list from the first element to + * the last. @p data is the data related to the current element. + * @p l is an #Eina_List used as the list iterator. + * + * It can be used to free list data, as in the following example: + * + * @code + * Eina_List *list; + * Eina_List *l; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings, + * // EINA_LIST_FOREACH will be used to free those strings + * + * EINA_LIST_FOREACH(list, l, data) + * free(data); + * eina_list_free(list); + * @endcode + * + * @note This is not the optimal way to release memory allocated to + * a list, since it iterates over the list twice. + * For an optimized algorithm, use EINA_LIST_FREE(). + * + * @warning Be careful when deleting list nodes. + * If you remove the current node and continue iterating, + * the code will fail because the macro will not be able + * to get the next node. Notice that it's OK to remove any + * node if you stop the loop after that. + * For destructive operations such as this, consider + * using EINA_LIST_FOREACH_SAFE(). + */ +#define EINA_LIST_FOREACH(list, l, data) \ + for (l = list, \ + data = eina_list_data_get(l); \ + l; \ + l = eina_list_next(l), \ + data = eina_list_data_get(l)) + +/** + * @def EINA_LIST_REVERSE_FOREACH + * @brief Macro to iterate over a list in the reverse order. + * + * @param list The list to iterate over. + * @param l A list that is used as an iterator and points to the current node. + * @param data Current item's data. + * + * This macro works like EINA_LIST_FOREACH, but iterates from the + * last element of a list to the first. + * @p data is the data related to the current element, while @p l + * is an #Eina_List that is used as the list iterator. + * + * It can be used to free list data, as in the following example: + * + * @code + * Eina_List *list; + * Eina_List *l; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings, + * // EINA_LIST_REVERSE_FOREACH will be used to free those strings + * + * EINA_LIST_REVERSE_FOREACH(list, l, data) + * free(data); + * eina_list_free(list); + * @endcode + * + * @note This is not the optimal way to release memory allocated to + * a list, since it iterates over the list twice. + * For an optimized algorithm, use EINA_LIST_FREE(). + * + * @warning Be careful when deleting list nodes. + * If you remove the current node and continue iterating, + * the code will fail because the macro will not be able + * to get the next node. Notice that it's OK to remove any + * node if you stop the loop after that. + * For destructive operations such as this, consider + * using EINA_LIST_REVERSE_FOREACH_SAFE(). + */ +#define EINA_LIST_REVERSE_FOREACH(list, l, data) \ + for (l = eina_list_last(list), \ + data = eina_list_data_get(l); \ + l; \ + l = eina_list_prev(l), \ + data = eina_list_data_get(l)) + +/** + * @def EINA_LIST_FOREACH_SAFE + * @brief Macro to iterate over a list with support for node deletion. + * + * @param list The list to iterate over. + * @param l A list that is used as an iterator and points to the current node. + * @param l_next A list that is used as an iterator and points to the next node. + * @param data Current item's data. + * + * This macro iterates over @p list from the first element to + * the last. @p data is the data related to the current element. + * @p l is an #Eina_List used as the list iterator. + * + * Since this macro stores a pointer to the next list node in @p l_next, + * deleting the current node and continuing looping is safe. + * + * This macro can be used to free list nodes, as in the following example: + * + * @code + * Eina_List *list; + * Eina_List *l; + * Eina_List *l_next; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings, + * // EINA_LIST_FOREACH_SAFE will be used to free elements that match "key". + * + * EINA_LIST_FOREACH_SAFE(list, l, l_next, data) + * if (strcmp(data, "key") == 0) { + * free(data); + * list = eina_list_remove_list(list, l); + * } + * @endcode + */ +#define EINA_LIST_FOREACH_SAFE(list, l, l_next, data) \ + for (l = list, \ + l_next = eina_list_next(l), \ + data = eina_list_data_get(l); \ + l; \ + l = l_next, \ + l_next = eina_list_next(l), \ + data = eina_list_data_get(l)) + +/** + * @def EINA_LIST_REVERSE_FOREACH_SAFE + * @brief Macro to iterate over a list in the reverse order with support + * for deletion. + * + * @param list The list to iterate over. + * @param l A list that is used as an iterator and points to the current node. + * @param l_prev A list that is used as an iterator and points to the previous node. + * @param data Current item's data. + * + * This macro works like EINA_LIST_FOREACH_SAFE, but iterates from the + * last element of a list to the first. + * @p data is the data related to the current element, while @p l + * is an #Eina_List that is used as the list iterator. + * + * Since this macro stores a pointer to the previous list node in @p l_prev, + * deleting the current node and continuing looping is safe. + * + * This macro can be used to free list nodes, as in the following example: + * + * @code + * Eina_List *list; + * Eina_List *l; + * Eina_List *l_prev; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings, + * // EINA_LIST_REVERSE_FOREACH_SAFE will be used to free elements that match "key". + * + * EINA_LIST_REVERSE_FOREACH_SAFE(list, l, l_prev, data) + * if (strcmp(data, "key") == 0) { + * free(data); + * list = eina_list_remove_list(list, l); + * } + * @endcode + */ +#define EINA_LIST_REVERSE_FOREACH_SAFE(list, l, l_prev, data) \ + for (l = eina_list_last(list), \ + l_prev = eina_list_prev(l), \ + data = eina_list_data_get(l); \ + l; \ + l = l_prev, \ + l_prev = eina_list_prev(l), \ + data = eina_list_data_get(l)) + +/** + * @def EINA_LIST_FREE + * @brief Macro to remove each list node while having access to each node's data. + * + * @param list The list that will be cleared. + * @param data Current node's data. + * + * This macro will call #eina_list_remove_list for each list node, and store + * the data contained in the current node in @p data. + * + * If you do not need to release node data, it is easier to call #eina_list_free(). + * + * @code + * Eina_List *list; + * char *data; + * + * // list is already filled, + * // its elements are just duplicated strings, + * + * EINA_LIST_FREE(list, data) + * free(data); + * @endcode + * + * @see eina_list_free() + */ +#define EINA_LIST_FREE(list, data) \ + for (data = eina_list_data_get(list); \ + list; \ + list = eina_list_remove_list(list, list), \ + data = eina_list_data_get(list)) + +#include "eina_inline_list.x" + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_LIST_H_ */ diff --git a/tests/suite/ecore/src/include/eina_log.h b/tests/suite/ecore/src/include/eina_log.h new file mode 100644 index 0000000000..d0fd1594d0 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_log.h @@ -0,0 +1,387 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_LOG_H_ +#define EINA_LOG_H_ + +#include <stdlib.h> +#include <stdarg.h> + +#include "eina_types.h" + +#define EINA_COLOR_LIGHTRED "\033[31;1m" +#define EINA_COLOR_RED "\033[31m" +#define EINA_COLOR_LIGHTBLUE "\033[34;1m" +#define EINA_COLOR_BLUE "\033[34m" +#define EINA_COLOR_GREEN "\033[32;1m" +#define EINA_COLOR_YELLOW "\033[33;1m" +#define EINA_COLOR_ORANGE "\033[0;33m" +#define EINA_COLOR_WHITE "\033[37;1m" +#define EINA_COLOR_LIGHTCYAN "\033[36;1m" +#define EINA_COLOR_CYAN "\033[36m" +#define EINA_COLOR_RESET "\033[0m" +#define EINA_COLOR_HIGH "\033[1m" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Log_Group Log + * + * @{ + */ + +/** + * EINA_LOG_DOMAIN_GLOBAL is the general purpose log domain to be + * used, it is always registered and available everywhere. + */ +EAPI extern int EINA_LOG_DOMAIN_GLOBAL; + +#ifndef EINA_LOG_DOMAIN_DEFAULT + +/** + * @def EINA_LOG_DOMAIN_DEFAULT + * This macro defines the domain to use with the macros EINA_LOG_DOM_DBG(), + * EINA_LOG_DOM_INFO(), EINA_LOG_DOM_WARN(), EINA_LOG_DOM_ERR() and + * EINA_LOG_DOM_CRIT(). + * + * If not defined prior to the inclusion of this header, then it + * defaults to #EINA_LOG_DOMAIN_GLOBAL. + * + * @note One may like to redefine this in its code to avoid typing too + * much. In this case the recommended way is: + * + * @code + * #include <Eina.h> + * #undef EINA_LOG_DOMAIN_DEFAULT + * #define EINA_LOG_DOMAIN_DEFAULT _log_dom + * static int _log_dom = -1; + * + * int main(void) + * { + * eina_init(); + * _log_dom = eina_log_domain_register("mydom", EINA_COLOR_CYAN); + * EINA_LOG_ERR("using my own domain"); + * return 0; + * } + * @endcode + * + * @warning If one defines the domain prior to inclusion of this + * header, the defined log domain symbol must be defined + * prior as well, otherwise the inlined functions defined by + * Eina will fail to find the symbol, causing build failure. + * + * @code + * #define EINA_LOG_DOMAIN_DEFAULT _log_dom + * static int _log_dom = -1; // must come before inclusion of Eina.h! + * #include <Eina.h> + * + * int main(void) + * { + * eina_init(); + * _log_dom = eina_log_domain_register("mydom", EINA_COLOR_CYAN); + * EINA_LOG_ERR("using my own domain"); + * return 0; + * } + * @endcode + * + */ +# define EINA_LOG_DOMAIN_DEFAULT EINA_LOG_DOMAIN_GLOBAL + +#endif /* EINA_LOG_DOMAIN_DEFAULT */ + + +/** + * @def EINA_LOG(DOM, LEVEL, fmt, ...) + * Logs a message on the specified domain, level and format. + * + * @note if @c EINA_LOG_LEVEL_MAXIMUM is defined, then messages larger + * than this value will be ignored regardless of current domain + * level, the eina_log_print() is not even called! Most + * compilers will just detect the two integers make the branch + * impossible and remove the branch and function call all + * together. Take this as optimization tip and possible remove + * debug messages from binaries to be deployed, saving on hot + * paths. Never define @c EINA_LOG_LEVEL_MAXIMUM on public + * header files. + */ +#ifdef EINA_LOG_LEVEL_MAXIMUM +#define EINA_LOG(DOM, LEVEL, fmt, ...) \ + do { \ + if (LEVEL <= EINA_LOG_LEVEL_MAXIMUM) { \ + eina_log_print(DOM, LEVEL, __FILE__, __FUNCTION__, __LINE__, \ + fmt, ## __VA_ARGS__); } \ + } while (0) +#else +#define EINA_LOG(DOM, LEVEL, fmt, ...) \ + eina_log_print(DOM, \ + LEVEL, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + fmt, \ + ## __VA_ARGS__) +#endif + +/** + * @def EINA_LOG_DOM_CRIT(DOM, fmt, ...) + * Logs a message with level CRITICAL on the specified domain and format. + */ +#define EINA_LOG_DOM_CRIT(DOM, fmt, ...) \ + EINA_LOG(DOM, EINA_LOG_LEVEL_CRITICAL, fmt, ## __VA_ARGS__) + +/** + * @def EINA_LOG_DOM_ERR(DOM, fmt, ...) + * Logs a message with level ERROR on the specified domain and format. + */ +#define EINA_LOG_DOM_ERR(DOM, fmt, ...) \ + EINA_LOG(DOM, EINA_LOG_LEVEL_ERR, fmt, ## __VA_ARGS__) + +/** + * @def EINA_LOG_DOM_INFO(DOM, fmt, ...) + * Logs a message with level INFO on the specified domain and format. + */ +#define EINA_LOG_DOM_INFO(DOM, fmt, ...) \ + EINA_LOG(DOM, EINA_LOG_LEVEL_INFO, fmt, ## __VA_ARGS__) + +/** + * @def EINA_LOG_DOM_DBG(DOM, fmt, ...) + * Logs a message with level DEBUG on the specified domain and format. + */ +#define EINA_LOG_DOM_DBG(DOM, fmt, ...) \ + EINA_LOG(DOM, EINA_LOG_LEVEL_DBG, fmt, ## __VA_ARGS__) + +/** + * @def EINA_LOG_DOM_WARN(DOM, fmt, ...) + * Logs a message with level WARN on the specified domain and format. + */ +#define EINA_LOG_DOM_WARN(DOM, fmt, ...) \ + EINA_LOG(DOM, EINA_LOG_LEVEL_WARN, fmt, ## __VA_ARGS__) + +/** + * @def EINA_LOG_CRIT(fmt, ...) + * Logs a message with level CRITICAL on the default domain with the specified + * format. + */ +#define EINA_LOG_CRIT(fmt, ...) \ + EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, \ + EINA_LOG_LEVEL_CRITICAL, \ + fmt, \ + ## __VA_ARGS__) + +/** + * @def EINA_LOG_ERR(fmt, ...) + * Logs a message with level ERROR on the default domain with the specified + * format. + */ +#define EINA_LOG_ERR(fmt, ...) \ + EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, EINA_LOG_LEVEL_ERR, fmt, ## __VA_ARGS__) + +/** + * @def EINA_LOG_INFO(fmt, ...) + * Logs a message with level INFO on the default domain with the specified + * format. + */ +#define EINA_LOG_INFO(fmt, ...) \ + EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, EINA_LOG_LEVEL_INFO, fmt, ## __VA_ARGS__) + +/** + * @def EINA_LOG_WARN(fmt, ...) + * Logs a message with level WARN on the default domain with the specified + * format. + */ +#define EINA_LOG_WARN(fmt, ...) \ + EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, EINA_LOG_LEVEL_WARN, fmt, ## __VA_ARGS__) + +/** + * @def EINA_LOG_DBG(fmt, ...) + * Logs a message with level DEBUG on the default domain with the specified + * format. + */ +#define EINA_LOG_DBG(fmt, ...) \ + EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, EINA_LOG_LEVEL_DBG, fmt, ## __VA_ARGS__) + +/** + * @typedef Eina_Log_Domain + * The domain used for logging. + */ +typedef struct _Eina_Log_Domain Eina_Log_Domain; + +/** + * @struct _Eina_Log_Domain + * The domain used for logging. + */ +struct _Eina_Log_Domain +{ + int level; /**< Max level to log */ + const char *domain_str; /**< Formatted string with color to print */ + const char *name; /**< Domain name */ + size_t namelen; /**< strlen(name) */ + + /* Private */ + Eina_Bool deleted : 1; /**< Flags deletion of domain, a free slot */ +}; + +EAPI void eina_log_threads_enable(void); + +/** + * @enum _Eina_Log_Level + * List of available logging levels. + */ +typedef enum _Eina_Log_Level +{ + EINA_LOG_LEVEL_CRITICAL, /**< Critical log level */ + EINA_LOG_LEVEL_ERR, /**< Error log level */ + EINA_LOG_LEVEL_WARN, /**< Warning log level */ + EINA_LOG_LEVEL_INFO, /**< Information log level */ + EINA_LOG_LEVEL_DBG, /**< Debug log level */ + EINA_LOG_LEVELS, /**< Count of default log levels */ + EINA_LOG_LEVEL_UNKNOWN = (-2147483647 - 1) /**< Unknown level */ +} Eina_Log_Level; + +/** + * @typedef Eina_Log_Print_Cb + * Type for print callbacks. + */ +typedef void (*Eina_Log_Print_Cb)(const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, const char *fnc, int line, + const char *fmt, void *data, va_list args); + +/* + * Customization + */ +EAPI void +eina_log_print_cb_set(Eina_Log_Print_Cb cb, void *data) EINA_ARG_NONNULL(1); + +EAPI void +eina_log_level_set(int level); +EAPI int +eina_log_level_get(void) EINA_WARN_UNUSED_RESULT; + +static inline Eina_Bool +eina_log_level_check(int level); + +EAPI Eina_Bool +eina_log_main_thread_check(void) EINA_CONST EINA_WARN_UNUSED_RESULT; + +EAPI void +eina_log_color_disable_set(Eina_Bool disabled); +EAPI Eina_Bool +eina_log_color_disable_get(void) EINA_WARN_UNUSED_RESULT; +EAPI void +eina_log_file_disable_set(Eina_Bool disabled); +EAPI Eina_Bool +eina_log_file_disable_get(void) EINA_WARN_UNUSED_RESULT; +EAPI void +eina_log_function_disable_set(Eina_Bool disabled); +EAPI Eina_Bool +eina_log_function_disable_get(void) EINA_WARN_UNUSED_RESULT; +EAPI void +eina_log_abort_on_critical_set(Eina_Bool abort_on_critical); +EAPI Eina_Bool +eina_log_abort_on_critical_get(void) EINA_WARN_UNUSED_RESULT; +EAPI void +eina_log_abort_on_critical_level_set(int critical_level); +EAPI int +eina_log_abort_on_critical_level_get(void) EINA_WARN_UNUSED_RESULT; + +EAPI void +eina_log_domain_level_set(const char *domain_name, int level) EINA_ARG_NONNULL(1); +EAPI int +eina_log_domain_level_get(const char *domain_name) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI int +eina_log_domain_registered_level_get(int domain) EINA_WARN_UNUSED_RESULT; +static inline Eina_Bool +eina_log_domain_level_check(int domain, int level); + + +/* + * Logging domains + */ +EAPI int +eina_log_domain_register(const char *name, const char *color) EINA_ARG_NONNULL(1); +EAPI void +eina_log_domain_unregister(int domain); + +/* + * Logging functions. + */ +EAPI void +eina_log_print(int domain, + Eina_Log_Level level, + const char *file, + const char *function, + int line, + const char *fmt, + ...) EINA_ARG_NONNULL(3, 4, 6) EINA_PRINTF(6, 7) EINA_NOINSTRUMENT; +EAPI void +eina_log_vprint(int domain, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line, + const char *fmt, + va_list args) EINA_ARG_NONNULL(3, 4, 6) EINA_NOINSTRUMENT; + + +/* + * Logging methods (change how logging is done). + */ +EAPI void +eina_log_print_cb_stdout(const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line, + const char *fmt, + void *data, + va_list args); +EAPI void +eina_log_print_cb_stderr(const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line, + const char *fmt, + void *data, + va_list args); +EAPI void +eina_log_print_cb_file(const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line, + const char *fmt, + void *data, + va_list args); + +#include "eina_inline_log.x" + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_LOG_H_ */ diff --git a/tests/suite/ecore/src/include/eina_magic.h b/tests/suite/ecore/src/include/eina_magic.h new file mode 100644 index 0000000000..dd1a9f3a56 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_magic.h @@ -0,0 +1,153 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_MAGIC_H_ +#define EINA_MAGIC_H_ + + +#include "eina_config.h" +#include "eina_types.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Magic_Group Magic + * + * @{ + */ + +typedef unsigned int Eina_Magic; + +/** + * @typedef Eina_Magic + * An abstract type for a magic number. + */ +EAPI const char *eina_magic_string_get(Eina_Magic magic) EINA_PURE EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool eina_magic_string_set(Eina_Magic magic, + const char *magic_name) EINA_ARG_NONNULL(2); +EAPI Eina_Bool eina_magic_string_static_set(Eina_Magic magic, + const char *magic_name) EINA_ARG_NONNULL(2); + +/** + * @def EINA_MAGIC_NONE + * Random value for specifying that a structure using the magic + * feature has already been freed. It is used by eina_magic_fail(). + * + * If the magic feature of Eina is disabled, #EINA_MAGIC_NONE is just + * @c 0. + */ +#define EINA_MAGIC_NONE 0x1234fedc + +#ifdef EINA_MAGIC_DEBUG + +/** + * @def EINA_MAGIC + * Declaration of a variable of type #Eina_Magic. To put in a structure + * when one wants to use the magic feature of Eina with the functions + * of that structure, like that: + * + * @code + * struct Foo + * { + * int i; + * + * EINA_MAGIC + * }; + * @endcode + * + * If the magic feature of Eina is disabled, #EINA_MAGIC does nothing. + */ +#define EINA_MAGIC Eina_Magic __magic; + +/** + * @def EINA_MAGIC_SET(d, m) + * Set the magic number of @p d to @p m. @p d must be a valid pointer + * to a structure holding an Eina magic number declaration. Use + * #EINA_MAGIC to add such declaration. + * + * If the magic feature of Eina is disabled, #EINA_MAGIC_CHECK is just + * the value @c 0. + */ +#define EINA_MAGIC_SET(d, m) (d)->__magic = (m) + +/** + * @def EINA_MAGIC_CHECK(d, m) + * Test if @p d is @c NULL or not, and if not @c NULL, if + * @p d->__eina_magic is equal to @p m. @p d must be a structure that + * holds an Eina magic number declaration. Use #EINA_MAGIC to add such + * declaration. + * + * If the magic feature of Eina is disabled, #EINA_MAGIC_CHECK is just + * the value @c 1. + */ +#define EINA_MAGIC_CHECK(d, m) ((d) && ((d)->__magic == (m))) + +/** + * @def EINA_MAGIC_FAIL(d, m) + * Call eina_magic_fail() with the parameters @p d, @p d->__magic, @p + * m, __FILE__, __FUNCTION__ and __LINE__. @p d must be a structure that + * holds an Eina magic number declaration. Use #EINA_MAGIC to add such + * declaration. + * + * If the magic feature of Eina is disabled, #EINA_MAGIC_FAIL does + * nothing. + */ +#define EINA_MAGIC_FAIL(d, m) \ + eina_magic_fail((void *)(d), \ + (d) ? (d)->__magic : 0, \ + (m), \ + __FILE__, \ + __FUNCTION__, \ + __LINE__); + +EAPI void eina_magic_fail(void *d, Eina_Magic m, Eina_Magic req_m, + const char *file, const char *fnc, + int line) EINA_ARG_NONNULL(4, 5); + +#else + +/** + * @cond LOCAL + */ + +#define EINA_MAGIC +#define EINA_MAGIC_SET(d, m) ((void)0) +#define EINA_MAGIC_CHECK(d, m) (1) +#define EINA_MAGIC_FAIL(d, m) ((void)0) + +#define eina_magic_fail(d, m, req_m, file, fnx, line) ((void)0) + +/** + * @endcond + */ + +#endif + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_MAGIC_H_ */ diff --git a/tests/suite/ecore/src/include/eina_main.h b/tests/suite/ecore/src/include/eina_main.h new file mode 100644 index 0000000000..db80042465 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_main.h @@ -0,0 +1,75 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_MAIN_H_ +#define EINA_MAIN_H_ + +#include "eina_types.h" + +/** + * @addtogroup Eina_Core_Group Core + * + * @{ + */ + +/** + * @defgroup Eina_Main_Group Main + * + * @{ + */ + +/** + * @def EINA_VERSION_MAJOR + * @brief Major version of Eina + */ +#define EINA_VERSION_MAJOR 1 + +/** + * @def EINA_VERSION_MINOR + * @brief Minor version of Eina + */ +#define EINA_VERSION_MINOR 0 + +/** + * @typedef Eina_Version + * The version of Eina. + */ +typedef struct _Eina_Version +{ + int major; /**< Major component of the version */ + int minor; /**< Minor component of the version */ + int micro; /**< Micro component of the version */ + int revision; /**< Revision component of the version */ +} Eina_Version; + +EAPI extern Eina_Version *eina_version; + +EAPI int eina_init(void); +EAPI int eina_shutdown(void); +EAPI int eina_threads_init(void); +EAPI int eina_threads_shutdown(void); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_MAIN_H_ */ diff --git a/tests/suite/ecore/src/include/eina_matrixsparse.h b/tests/suite/ecore/src/include/eina_matrixsparse.h new file mode 100644 index 0000000000..cbf29ccb7e --- /dev/null +++ b/tests/suite/ecore/src/include/eina_matrixsparse.h @@ -0,0 +1,120 @@ +/* EINA - EFL data type library + * Copyright (C) 2009 Gustavo Sverzut Barbieri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_MATRIXSPARSE_H_ +#define EINA_MATRIXSPARSE_H_ + +#include <stdlib.h> + +#include "eina_config.h" + +#include "eina_types.h" +#include "eina_iterator.h" +#include "eina_accessor.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @addtogroup Eina_Containers_Group Containers + * + * @{ + */ + +/** + * @defgroup Eina_Matrixsparse_Group Sparse Matrix + * + * @{ + */ + +/** + * @typedef Eina_Matrixsparse + * Type for a generic sparse matrix. + */ +typedef struct _Eina_Matrixsparse Eina_Matrixsparse; + +/** + * @typedef Eina_Matrixsparse_Row + * Type for a generic sparse matrix row, opaque for users. + */ +typedef struct _Eina_Matrixsparse_Row Eina_Matrixsparse_Row; + +/** + * @typedef Eina_Matrixsparse_Cell + * Type for a generic sparse matrix cell, opaque for users. + */ +typedef struct _Eina_Matrixsparse_Cell Eina_Matrixsparse_Cell; + +typedef struct _Eina_Matrixsparse_Item_Cell Eina_Matrixsparse_Item_Cell; +typedef struct _Eina_Matrixsparse_Item_Row Eina_Matrixsparse_Item_Row; + + +/* constructors and destructors */ +EAPI Eina_Matrixsparse *eina_matrixsparse_new(unsigned long rows, + unsigned long cols, + void (*free_func)(void *user_data, + void *cell_data), + const void *user_data); +EAPI void eina_matrixsparse_free(Eina_Matrixsparse *m); + +/* size manipulation */ +EAPI void eina_matrixsparse_size_get(const Eina_Matrixsparse *m, + unsigned long *rows, + unsigned long *cols); +EAPI Eina_Bool eina_matrixsparse_size_set(Eina_Matrixsparse *m, + unsigned long rows, + unsigned long cols); + +/* data getting */ +EAPI Eina_Bool eina_matrixsparse_cell_idx_get(const Eina_Matrixsparse *m, unsigned long row, unsigned long col, Eina_Matrixsparse_Cell **cell); +EAPI void * eina_matrixsparse_cell_data_get(const Eina_Matrixsparse_Cell *cell); +EAPI void * eina_matrixsparse_data_idx_get(const Eina_Matrixsparse *m, unsigned long row, unsigned long col); +EAPI Eina_Bool eina_matrixsparse_cell_position_get(const Eina_Matrixsparse_Cell *cell, unsigned long *row, unsigned long *col); + +/* data setting */ +EAPI Eina_Bool eina_matrixsparse_cell_data_replace(Eina_Matrixsparse_Cell *cell, const void *data, void **p_old); +EAPI Eina_Bool eina_matrixsparse_cell_data_set(Eina_Matrixsparse_Cell *cell, const void *data); +EAPI Eina_Bool eina_matrixsparse_data_idx_replace(Eina_Matrixsparse *m, unsigned long row, unsigned long col, const void *data, void **p_old); +EAPI Eina_Bool eina_matrixsparse_data_idx_set(Eina_Matrixsparse *m, unsigned long row, unsigned long col, const void *data); + +/* data deleting */ +EAPI Eina_Bool eina_matrixsparse_row_idx_clear(Eina_Matrixsparse *m, unsigned long row); +EAPI Eina_Bool eina_matrixsparse_column_idx_clear(Eina_Matrixsparse *m, unsigned long col); +EAPI Eina_Bool eina_matrixsparse_cell_idx_clear(Eina_Matrixsparse *m, unsigned long row, unsigned long col); +EAPI Eina_Bool eina_matrixsparse_cell_clear(Eina_Matrixsparse_Cell *cell); + +/* iterators */ +EAPI Eina_Iterator *eina_matrixsparse_iterator_new(const Eina_Matrixsparse *m); +EAPI Eina_Iterator *eina_matrixsparse_iterator_complete_new(const Eina_Matrixsparse *m); + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_MATRIXSPARSE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_mempool.h b/tests/suite/ecore/src/include/eina_mempool.h new file mode 100644 index 0000000000..cc19e94018 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_mempool.h @@ -0,0 +1,77 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_MEMPOOL_H_ +#define EINA_MEMPOOL_H_ + +#include "eina_types.h" +#include "eina_error.h" +#include "eina_module.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Memory_Pool_Group Memory Pool + * + * @{ + */ + +/** + * @typedef Eina_Mempool + * Mempool type. + */ +typedef struct _Eina_Mempool Eina_Mempool; + +/** + * @typedef Eina_Mempool_Backend + * Mempool backend type. + */ +typedef struct _Eina_Mempool_Backend Eina_Mempool_Backend; + +EAPI extern Eina_Error EINA_ERROR_NOT_MEMPOOL_MODULE; + +EAPI Eina_Mempool * eina_mempool_add(const char *module,const char *context,const char *options,...) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void eina_mempool_del(Eina_Mempool *mp) EINA_ARG_NONNULL(1); + +static inline void * eina_mempool_realloc(Eina_Mempool *mp,void *element,unsigned int size) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +static inline void * eina_mempool_malloc(Eina_Mempool *mp,unsigned int size) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +static inline void eina_mempool_free(Eina_Mempool *mp,void *element) EINA_ARG_NONNULL(1); + +EAPI void eina_mempool_gc(Eina_Mempool *mp) EINA_ARG_NONNULL(1); +EAPI void eina_mempool_statistics(Eina_Mempool *mp) EINA_ARG_NONNULL(1); + +EAPI Eina_Bool eina_mempool_register(Eina_Mempool_Backend *be) EINA_ARG_NONNULL(1); +EAPI void eina_mempool_unregister(Eina_Mempool_Backend *be) EINA_ARG_NONNULL(1); + +EAPI unsigned int eina_mempool_alignof(unsigned int size); + +#include "eina_inline_mempool.x" + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_MEMPOOL_H_ */ diff --git a/tests/suite/ecore/src/include/eina_module.h b/tests/suite/ecore/src/include/eina_module.h new file mode 100644 index 0000000000..6618381c30 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_module.h @@ -0,0 +1,150 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_MODULE_H_ +#define EINA_MODULE_H_ + +#include "eina_types.h" +#include "eina_array.h" +#include "eina_error.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Module_Group Module + * + * Eina module provides some helpers over POSIX dlopen(). It is not + * meant to replace, abstract or make a "portable" version of the + * POSIX, but enhance its usage by defining some good practices. + * + * Modules are created with eina_module_new() and later loaded with + * eina_module_load(). Loads are reference counted and there must be + * the same number of eina_module_unload() in order to have it to call + * dlclose(). This makes simple to have different users for the same + * module. + * + * The loaded shared objects may have two visible functions that will + * be called and might provide initialization and shutdown + * proceedures. The symbols are @c __eina_module_init and + * @c __eina_module_shutdown and will be defined by the macros + * EINA_MODULE_INIT() and EINA_MODULE_SHUTDOWN(). + * + * There are some helpers to automatically create modules based on + * directory listing. See eina_module_arch_list_get(), + * eina_module_list_get() and eina_module_find(). + * + * @{ + */ + +/** + * @typedef Eina_Module + * Dynamic module loader handle. + */ +typedef struct _Eina_Module Eina_Module; + +typedef Eina_Bool (*Eina_Module_Cb)(Eina_Module *m, void *data); + +/** + * @typedef Eina_Module_Init + * If a function with such signature is exported by module as + * __eina_module_init, it will be called on the first load after + * dlopen() and if #EINA_FALSE is returned, load will fail, #EINA_TRUE + * means the module was successfully initialized. + * @see Eina_Module_Shutdown + */ +typedef Eina_Bool (*Eina_Module_Init)(void); + +/** + * @typedef Eina_Module_Shutdown + * If a function with such signature is exported by module as + * __eina_module_shutdown, it will be called before calling dlclose() + * @see Eina_Module_Init + */ +typedef void (*Eina_Module_Shutdown)(void); + +/** + * @def EINA_MODULE_INIT + * declares the given function as the module initializer (__eina_module_init). + * It must be of signature #Eina_Module_Init + */ +#define EINA_MODULE_INIT(f) EAPI Eina_Module_Init __eina_module_init = &f + +/** + * @def EINA_MODULE_SHUTDOWN + * declares the given function as the module shutdownializer + * (__eina_module_shutdown). It must be of signature + * #Eina_Module_Shutdown + */ +#define EINA_MODULE_SHUTDOWN(f) EAPI Eina_Module_Shutdown __eina_module_shutdown = &f + +/** + * @var EINA_ERROR_WRONG_MODULE + * Error identifier corresponding to a wrong module. + */ +extern EAPI Eina_Error EINA_ERROR_WRONG_MODULE; + +/** + * @var EINA_ERROR_MODULE_INIT_FAILED + * Error identifier corresponding to a failure during the initialisation of a module. + */ +extern EAPI Eina_Error EINA_ERROR_MODULE_INIT_FAILED; + +EAPI Eina_Module * +eina_module_new(const char *file) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI Eina_Bool +eina_module_free(Eina_Module *m) EINA_ARG_NONNULL(1); +EAPI Eina_Bool +eina_module_load(Eina_Module *module) EINA_ARG_NONNULL(1); +EAPI Eina_Bool +eina_module_unload(Eina_Module *m) EINA_ARG_NONNULL(1); +EAPI void * +eina_module_symbol_get(const Eina_Module *module, const char *symbol) EINA_PURE EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI const char * +eina_module_file_get(const Eina_Module *m) EINA_PURE EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +EAPI char * +eina_module_symbol_path_get(const void *symbol, const char *sub_dir) EINA_PURE EINA_MALLOC EINA_ARG_NONNULL(1, 2); +EAPI char * +eina_module_environment_path_get(const char *env, const char *sub_dir) EINA_PURE EINA_MALLOC EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Array * +eina_module_arch_list_get(Eina_Array *array, const char *path, const char *arch); +EAPI Eina_Array * +eina_module_list_get(Eina_Array *array, const char *path, Eina_Bool recursive, Eina_Module_Cb cb, void *data) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI void +eina_module_list_load(Eina_Array *list) EINA_ARG_NONNULL(1); +EAPI void +eina_module_list_unload(Eina_Array *list) EINA_ARG_NONNULL(1); +EAPI void +eina_module_list_free(Eina_Array *list) EINA_ARG_NONNULL(1); +EAPI Eina_Module * +eina_module_find(const Eina_Array *array, const char *module) EINA_ARG_NONNULL(1, 2); + +/** + * @} + */ + +/** + * @} + */ + +#endif /*EINA_MODULE_H_*/ diff --git a/tests/suite/ecore/src/include/eina_quadtree.h b/tests/suite/ecore/src/include/eina_quadtree.h new file mode 100644 index 0000000000..ad24c3ebb3 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_quadtree.h @@ -0,0 +1,53 @@ +/* EINA - EFL data type library + * Copyright (C) 2010 Cedric BAIL + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_QUADTREE_H_ +#define EINA_QUADTREE_H_ + +#include "eina_config.h" + +#include "eina_inlist.h" + +typedef struct _Eina_QuadTree Eina_QuadTree; +typedef struct _Eina_QuadTree_Item Eina_QuadTree_Item; + +typedef enum { + EINA_QUAD_LEFT, + EINA_QUAD_RIGHT, + EINA_QUAD_BOTH +} Eina_Quad_Direction; + +typedef Eina_Quad_Direction (*Eina_Quad_Callback)(const void *object, size_t middle); + +EAPI Eina_QuadTree * eina_quadtree_new(size_t w, size_t h, Eina_Quad_Callback vertical, Eina_Quad_Callback horizontal); +EAPI void eina_quadtree_free(Eina_QuadTree *q); +EAPI void eina_quadtree_resize(Eina_QuadTree *q, size_t w, size_t h); + +EAPI void eina_quadtree_cycle(Eina_QuadTree *q); +EAPI void eina_quadtree_increase(Eina_QuadTree_Item *object); + +EAPI Eina_QuadTree_Item *eina_quadtree_add(Eina_QuadTree *q, const void *object); +EAPI Eina_Bool eina_quadtree_del(Eina_QuadTree_Item *object); +EAPI Eina_Bool eina_quadtree_change(Eina_QuadTree_Item *object); +EAPI Eina_Bool eina_quadtree_hide(Eina_QuadTree_Item *object); +EAPI Eina_Bool eina_quadtree_show(Eina_QuadTree_Item *object); + +EAPI Eina_Inlist * eina_quadtree_collide(Eina_QuadTree *q, int x, int y, int w, int h); +EAPI void * eina_quadtree_object(Eina_Inlist *list); + +#endif diff --git a/tests/suite/ecore/src/include/eina_rbtree.h b/tests/suite/ecore/src/include/eina_rbtree.h new file mode 100644 index 0000000000..026db41ca1 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_rbtree.h @@ -0,0 +1,157 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_RBTREE_H__ +#define EINA_RBTREE_H__ + +#include <stdlib.h> + +#include "eina_types.h" +#include "eina_error.h" +#include "eina_iterator.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @addtogroup Eina_Containers_Group Containers + * + * @{ + */ + +/** + * @defgroup Eina_Rbtree_Group Red-Black tree + * + * @{ + */ + +/** + * @typedef Eina_Rbtree_Color + * node color. + */ +typedef enum { + EINA_RBTREE_RED, + EINA_RBTREE_BLACK +} Eina_Rbtree_Color; + +/** + * @typedef Eina_Rbtree_Direction + * walk direction. + */ +typedef enum { + EINA_RBTREE_LEFT = 0, + EINA_RBTREE_RIGHT = 1 +} Eina_Rbtree_Direction; + +/** + * @typedef Eina_Rbtree + * Type for a Red-Black tree node. It should be inlined into user's type. + */ +typedef struct _Eina_Rbtree Eina_Rbtree; +struct _Eina_Rbtree +{ + Eina_Rbtree *son[2]; + + Eina_Rbtree_Color color : 1; +}; + +/** + * @def EINA_RBTREE + * recommended way to declare the inlined Eina_Rbtree in your type. + * + * @code + * struct my_type { + * EINA_RBTREE; + * int my_value; + * char *my_name; + * }; + * @endcode + * + * @see EINA_RBTREE_GET() + */ +#define EINA_RBTREE Eina_Rbtree __rbtree + +/** + * @def EINA_RBTREE_GET + * access the inlined node if it was created with #EINA_RBTREE. + */ +#define EINA_RBTREE_GET(Rbtree) & ((Rbtree)->__rbtree) + +/** + * @typedef Eina_Rbtree_Cmp_Node_Cb + * Function used compare two nodes and see which direction to navigate. + */ +typedef Eina_Rbtree_Direction (*Eina_Rbtree_Cmp_Node_Cb)(const Eina_Rbtree *left, const Eina_Rbtree *right, void *data); + +/** + * @def EINA_RBTREE_CMP_NODE_CB + * Cast using #Eina_Rbtree_Cmp_Node_Cb + */ +#define EINA_RBTREE_CMP_NODE_CB(Function) ((Eina_Rbtree_Cmp_Node_Cb)Function) + +/** + * @typedef Eina_Rbtree_Cmp_Key_Cb + * Function used compare node with a given key of specified length. + */ +typedef int (*Eina_Rbtree_Cmp_Key_Cb)(const Eina_Rbtree *node, const void *key, int length, void *data); +/** + * @def EINA_RBTREE_CMP_KEY_CB + * Cast using #Eina_Rbtree_Cmp_Key_Cb + */ +#define EINA_RBTREE_CMP_KEY_CB(Function) ((Eina_Rbtree_Cmp_Key_Cb)Function) + +/** + * @typedef Eina_Rbtree_Free_Cb + * Function used free a node. + */ +typedef void (*Eina_Rbtree_Free_Cb)(Eina_Rbtree *node, void *data); +/** + * @def EINA_RBTREE_FREE_CB + * Cast using #Eina_Rbtree_Free_Cb + */ +#define EINA_RBTREE_FREE_CB(Function) ((Eina_Rbtree_Free_Cb)Function) + +EAPI Eina_Rbtree * eina_rbtree_inline_insert(Eina_Rbtree *root,Eina_Rbtree *node,Eina_Rbtree_Cmp_Node_Cb cmp,const void *data) EINA_ARG_NONNULL(2, 3) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Rbtree * eina_rbtree_inline_remove(Eina_Rbtree *root,Eina_Rbtree *node,Eina_Rbtree_Cmp_Node_Cb cmp,const void *data) EINA_ARG_NONNULL(2, 3) EINA_WARN_UNUSED_RESULT; +EAPI void eina_rbtree_delete(Eina_Rbtree *root,Eina_Rbtree_Free_Cb func,void *data) EINA_ARG_NONNULL(2); + +static inline Eina_Rbtree * eina_rbtree_inline_lookup(const Eina_Rbtree *root,const void *key,int length,Eina_Rbtree_Cmp_Key_Cb cmp,const void *data) EINA_PURE EINA_ARG_NONNULL(2, 4) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Iterator * eina_rbtree_iterator_prefix(const Eina_Rbtree *root) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI Eina_Iterator * eina_rbtree_iterator_infix(const Eina_Rbtree *root) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI Eina_Iterator * eina_rbtree_iterator_postfix(const Eina_Rbtree *root) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + +#include "eina_inline_rbtree.x" + + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_rectangle.h b/tests/suite/ecore/src/include/eina_rectangle.h new file mode 100644 index 0000000000..a9daf66672 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_rectangle.h @@ -0,0 +1,109 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_RECTANGLE_H_ +#define EINA_RECTANGLE_H_ + +#include "eina_types.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Rectangle_Group Rectangle + * + * @{ + */ + +/** + * @typedef Eina_Rectangle + * Simple rectangle structure. + */ +typedef struct _Eina_Rectangle +{ + int x; /**< top-left x co-ordinate of rectangle */ + int y; /**< top-left y co-ordinate of rectangle */ + int w; /**< width of rectangle */ + int h; /**< height of rectangle */ +} Eina_Rectangle; + +/** + * @typedef Eina_Rectangle_Pool + * Type for an opaque pool of rectangle. + */ +typedef struct _Eina_Rectangle_Pool Eina_Rectangle_Pool; + +static inline int eina_spans_intersect(int c1, int l1, int c2, int l2) EINA_WARN_UNUSED_RESULT; +static inline Eina_Bool eina_rectangle_is_empty(const Eina_Rectangle *r) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +static inline void eina_rectangle_coords_from(Eina_Rectangle *r, int x, int y, int w, int h) EINA_ARG_NONNULL(1); +static inline Eina_Bool eina_rectangles_intersect(const Eina_Rectangle *r1, const Eina_Rectangle *r2) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +static inline Eina_Bool eina_rectangle_xcoord_inside(const Eina_Rectangle *r, int x) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +static inline Eina_Bool eina_rectangle_ycoord_inside(const Eina_Rectangle *r, int y) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +static inline Eina_Bool eina_rectangle_coords_inside(const Eina_Rectangle *r, int x, int y) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +static inline void eina_rectangle_union(Eina_Rectangle *dst, const Eina_Rectangle *src) EINA_ARG_NONNULL(1, 2); +static inline Eina_Bool eina_rectangle_intersection(Eina_Rectangle *dst, const Eina_Rectangle *src) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +static inline void eina_rectangle_rescale_in(const Eina_Rectangle *out, const Eina_Rectangle *in, Eina_Rectangle *res) EINA_ARG_NONNULL(1, 2, 3); +static inline void eina_rectangle_rescale_out(const Eina_Rectangle *out, const Eina_Rectangle *in, Eina_Rectangle *res) EINA_ARG_NONNULL(1, 2, 3); + +EAPI Eina_Rectangle_Pool * eina_rectangle_pool_new(int w, int h) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI Eina_Rectangle_Pool * eina_rectangle_pool_get(Eina_Rectangle *rect) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_rectangle_pool_geometry_get(Eina_Rectangle_Pool *pool, int *w, int *h) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI void * eina_rectangle_pool_data_get(Eina_Rectangle_Pool *pool) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void eina_rectangle_pool_data_set(Eina_Rectangle_Pool *pool, const void *data) EINA_ARG_NONNULL(1); +EAPI void eina_rectangle_pool_free(Eina_Rectangle_Pool *pool) EINA_ARG_NONNULL(1); +EAPI int eina_rectangle_pool_count(Eina_Rectangle_Pool *pool) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Rectangle * eina_rectangle_pool_request(Eina_Rectangle_Pool *pool, int w, int h) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); +EAPI void eina_rectangle_pool_release(Eina_Rectangle *rect) EINA_ARG_NONNULL(1); + +/** + * @def EINA_RECTANGLE_SET + * @brief Macro to set the values of a #Eina_Rectangle. + * + * @param Rectangle The rectangle to set the values. + * @param X The X coordinate of the top left corner of the rectangle. + * @param Y The Y coordinate of the top left corner of the rectangle. + * @param W The width of the rectangle. + * @param H The height of the rectangle. + * + * This macro set the values of @p Rectangle. (@p X, @p Y) is the + * coordinates of the top left corner of @p Rectangle, @p W is its + * width and @p H is its height. + */ +#define EINA_RECTANGLE_SET(Rectangle, X, Y, W, H) \ + (Rectangle)->x = X; \ + (Rectangle)->y = Y; \ + (Rectangle)->w = W; \ + (Rectangle)->h = H; + +EAPI Eina_Rectangle *eina_rectangle_new(int x, int y, int w, int h) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI void eina_rectangle_free(Eina_Rectangle *rect) EINA_ARG_NONNULL(1); + +#include "eina_inline_rectangle.x" + +/** + * @} + */ + +/** + * @} + */ + +#endif /*_EINA_RECTANGLE_H_*/ diff --git a/tests/suite/ecore/src/include/eina_safety_checks.h b/tests/suite/ecore/src/include/eina_safety_checks.h new file mode 100644 index 0000000000..29ebba07d3 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_safety_checks.h @@ -0,0 +1,234 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Gustavo Sverzut Barbieri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_SAFETY_CHECKS_H_ +#define EINA_SAFETY_CHECKS_H_ + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_Safety_Checks_Group Safety Checks + * + * @warning @c eina_safety_checks.h should only be included by source + * files, after all other includes and before the source file + * specific includes. By source file specific includes we + * mean those that define the functions that are being + * checked. The reason for such complexity is the trick to + * avoid compiler optimizations. If compilers are told that + * some given function will never receive @c NULL + * (EINA_ARG_NONNULL(), then compiler will emit a warning if + * it detects so (good!) but will remove any checks for that + * condition as it believes it will never happen, removing + * all safety checks! By including @c eina_safety_checks.h it + * will redefine EINA_ARG_NONNULL() to void and compiler + * warning will not be emitted, but checks will be there. The + * files already processed with the old macro + * EINA_ARG_NONNULL() will still work and emit the warnings. + * + * + * @code + * + * // all these files will emit warning from EINA_ARG_NONNULL() + * #include <Evas.h> // third party headers + * #include <Ecore.h> + * #include <eina_error.h> // eina own header + * + * #include <eina_safety_checks.h> + * // all these files below will NOT emit warning from EINA_ARG_NONNULL(), + * // but this is required to have the functions defined there to be checked + * // for NULL pointers + * #include "my_functions1.h" + * #include "my_functions2.h" + * + * @endcode + * + * @{ + */ + + +#include "eina_config.h" +#include "eina_error.h" + +/** + * @var EINA_ERROR_SAFETY_FAILED + * Error identifier corresponding to safety check failure. + */ +EAPI extern Eina_Error EINA_ERROR_SAFETY_FAILED; + +#ifdef EINA_SAFETY_CHECKS + +#include "eina_log.h" + +#define EINA_SAFETY_ON_NULL_RETURN(exp) \ + do \ + { \ + if (EINA_UNLIKELY((exp) == NULL)) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " == NULL"); \ + return; \ + } \ + } \ + while (0) + +#define EINA_SAFETY_ON_NULL_RETURN_VAL(exp, val) \ + do \ + { \ + if (EINA_UNLIKELY((exp) == NULL)) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " == NULL"); \ + return (val); \ + } \ + } \ + while (0) + +#define EINA_SAFETY_ON_NULL_GOTO(exp, label) \ + do \ + { \ + if (EINA_UNLIKELY((exp) == NULL)) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " == NULL"); \ + goto label; \ + } \ + } \ + while (0) + +#define EINA_SAFETY_ON_TRUE_RETURN(exp) \ + do \ + { \ + if (EINA_UNLIKELY(exp)) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " is true"); \ + return; \ + } \ + } \ + while (0) + +#define EINA_SAFETY_ON_TRUE_RETURN_VAL(exp, val) \ + do \ + { \ + if (EINA_UNLIKELY(exp)) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " is true"); \ + return val; \ + } \ + } \ + while (0) + +#define EINA_SAFETY_ON_TRUE_GOTO(exp, label) \ + do \ + { \ + if (EINA_UNLIKELY(exp)) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " is true"); \ + goto label; \ + } \ + } \ + while (0) + +#define EINA_SAFETY_ON_FALSE_RETURN(exp) \ + do \ + { \ + if (EINA_UNLIKELY(!(exp))) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " is false"); \ + return; \ + } \ + } \ + while (0) + +#define EINA_SAFETY_ON_FALSE_RETURN_VAL(exp, val) \ + do \ + { \ + if (EINA_UNLIKELY(!(exp))) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " is false"); \ + return val; \ + } \ + } \ + while (0) + +#define EINA_SAFETY_ON_FALSE_GOTO(exp, label) \ + do \ + { \ + if (EINA_UNLIKELY(!(exp))) \ + { \ + eina_error_set(EINA_ERROR_SAFETY_FAILED); \ + EINA_LOG_ERR("%s", "safety check failed: " # exp " is false"); \ + goto label; \ + } \ + } \ + while (0) + +#ifdef EINA_ARG_NONNULL +/* make EINA_ARG_NONNULL void so GCC does not optimize safety checks */ +#undef EINA_ARG_NONNULL +#define EINA_ARG_NONNULL(idx, ...) +#endif + + +#else /* no safety checks */ + +#define EINA_SAFETY_ON_NULL_RETURN(exp) \ + do { (void)(!(exp)); } while (0) + +#define EINA_SAFETY_ON_NULL_RETURN_VAL(exp, val) \ + do { if (0 && !(exp)) { (void)val; } } while (0) + +#define EINA_SAFETY_ON_NULL_GOTO(exp, label) \ + do { if (0 && (exp) == NULL) { goto label; } } while (0) + +#define EINA_SAFETY_ON_TRUE_RETURN(exp) \ + do { (void)(exp); } while (0) + +#define EINA_SAFETY_ON_TRUE_RETURN_VAL(exp, val) \ + do { if (0 && (exp)) { (void)val; } } while (0) + +#define EINA_SAFETY_ON_TRUE_GOTO(exp, label) \ + do { if (0 && (exp)) { goto label; } } while (0) + +#define EINA_SAFETY_ON_FALSE_RETURN(exp) \ + do { (void)(!(exp)); } while (0) + +#define EINA_SAFETY_ON_FALSE_RETURN_VAL(exp, val) \ + do { if (0 && !(exp)) { (void)val; } } while (0) + +#define EINA_SAFETY_ON_FALSE_GOTO(exp, label) \ + do { if (0 && !(exp)) { goto label; } } while (0) + +#endif /* safety checks macros */ +#endif /* EINA_SAFETY_CHECKS_H_ */ + +/** + * @} + */ + +/** + * @} + */ diff --git a/tests/suite/ecore/src/include/eina_sched.h b/tests/suite/ecore/src/include/eina_sched.h new file mode 100644 index 0000000000..607b9f5b85 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_sched.h @@ -0,0 +1,26 @@ +/* EINA - EFL data type library + * Copyright (C) 2010 ProFUSION embedded systems + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_SCHED_H_ +#define EINA_SCHED_H_ + +#include "eina_types.h" + +EAPI void eina_sched_prio_drop(void); + +#endif /* EINA_SCHED_H_ */ diff --git a/tests/suite/ecore/src/include/eina_str.h b/tests/suite/ecore/src/include/eina_str.h new file mode 100644 index 0000000000..1d12615e5a --- /dev/null +++ b/tests/suite/ecore/src/include/eina_str.h @@ -0,0 +1,73 @@ +#ifndef _EINA_STR_H +#define _EINA_STR_H + +#include <stddef.h> +#include <string.h> + +#include "eina_types.h" + +/** + * @addtogroup Eina_Tools_Group Tools + * + * @{ + */ + +/** + * @defgroup Eina_String_Group String + * + * @{ + */ + +/* strlcpy implementation for libc's lacking it */ +EAPI size_t eina_strlcpy(char *dst, const char *src, size_t siz) EINA_ARG_NONNULL(1, 2); +EAPI size_t eina_strlcat(char *dst, const char *src, size_t siz) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool eina_str_has_prefix(const char *str, const char *prefix) EINA_PURE EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool eina_str_has_suffix(const char *str, const char *suffix) EINA_PURE EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool eina_str_has_extension(const char *str, const char *ext) EINA_PURE EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI char ** eina_str_split(const char *string, const char *delimiter, int max_tokens) EINA_ARG_NONNULL(1, 2) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI char ** eina_str_split_full(const char *string, const char *delimiter, int max_tokens, unsigned int *elements) EINA_ARG_NONNULL(1, 2, 4) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + +EAPI size_t eina_str_join_len(char *dst, size_t size, char sep, const char *a, size_t a_len, const char *b, size_t b_len) EINA_ARG_NONNULL(1, 4, 6); + +EAPI char * eina_str_convert(const char *enc_from, const char *enc_to, const char *text) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_ARG_NONNULL(1, 2, 3); + +EAPI char * eina_str_escape(const char *str) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_ARG_NONNULL(1); + +EAPI void eina_str_tolower(char **str); +EAPI void eina_str_toupper(char **str); + +static inline size_t eina_str_join(char *dst, size_t size, char sep, const char *a, const char *b) EINA_ARG_NONNULL(1, 4, 5); + +/** + * @def eina_str_join_static(dst, sep, a, b) + * @brief Join two static strings and store the result in a static buffer. + * + * @param dst The buffer to store the result. + * @param sep The separator character to use. + * @param a First string to use, before @p sep. + * @param b Second string to use, after @p sep. + * @return The number of characters printed. + * + * This function is similar to eina_str_join_len(), but will assume + * string sizes are know using sizeof(X). + * + * @see eina_str_join() + * @see eina_str_join_static() + */ +#define eina_str_join_static(dst, sep, a, b) eina_str_join_len(dst, sizeof(dst), sep, a, (sizeof(a) > 0) ? sizeof(a) - 1 : 0, b, (sizeof(b) > 0) ? sizeof(b) - 1 : 0) + +static inline size_t eina_strlen_bounded(const char *str, size_t maxlen) EINA_PURE EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +#include "eina_inline_str.x" + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_STR_H */ diff --git a/tests/suite/ecore/src/include/eina_strbuf.h b/tests/suite/ecore/src/include/eina_strbuf.h new file mode 100644 index 0000000000..4424eb4852 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_strbuf.h @@ -0,0 +1,477 @@ +#ifndef EINA_STRBUF_H +#define EINA_STRBUF_H + +#include <stddef.h> +#include <stdarg.h> + +#include "eina_types.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @defgroup Eina_String_Buffer_Group String Buffer + * + * @{ + */ + +/** + * @typedef Eina_Strbuf + * Type for a string buffer. + */ +typedef struct _Eina_Strbuf Eina_Strbuf; + +/** + * @brief Create a new string buffer. + * + * @return Newly allocated string buffer instance. + * + * This function creates a new string buffer. On error, @c NULL is + * returned and Eina error is set to #EINA_ERROR_OUT_OF_MEMORY. To + * free the resources, use eina_strbuf_free(). + * + * @see eina_strbuf_free() + * @see eina_strbuf_append() + * @see eina_strbuf_string_get() + */ +EAPI Eina_Strbuf *eina_strbuf_new(void) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + +/** + * @brief Free a string buffer. + * + * @param buf The string buffer to free. + * + * This function frees the memory of @p buf. @p buf must have been + * created by eina_strbuf_new(). + */ +EAPI void eina_strbuf_free(Eina_Strbuf *buf) EINA_ARG_NONNULL(1); + +/** + * @brief Reset a string buffer. + * + * @param buf The string buffer to reset. + * + * This function reset @p buf: the buffer len is set to 0, and the + * string is set to '\\0'. No memory is free'd. + */ +EAPI void eina_strbuf_reset(Eina_Strbuf *buf) EINA_ARG_NONNULL(1); + +/** + * @brief Append a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends @p str to @p buf. It computes the length of + * @p str, so is slightly slower than eina_strbuf_append_length(). If + * the length is known beforehand, consider using that variant. If + * @p buf can't append it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + * + * @see eina_strbuf_append() + * @see eina_strbuf_append_length() + */ +EAPI Eina_Bool eina_strbuf_append(Eina_Strbuf *buf, const char *str) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Append an escaped string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends the escaped string @p str to @p buf. If @p + * str can not be appended, #EINA_FALSE is returned, otherwise, + * #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_strbuf_append_escaped(Eina_Strbuf *buf, const char *str) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Append a string to a buffer, reallocating as necessary, + * limited by the given length. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @param maxlen The maximum number of characters to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends at most @p maxlen characters of @p str to + * @p buf. It can't appends more than the length of @p str. It + * computes the length of @p str, so is slightly slower than + * eina_strbuf_append_length(). If the length is known beforehand, + * consider using that variant (@p maxlen should then be checked so + * that it is greater than the size of @p str). If @p str can not be + * appended, #EINA_FALSE is returned, otherwise, #EINA_TRUE is + * returned. + * + * @see eina_strbuf_append() + * @see eina_strbuf_append_length() + */ +EAPI Eina_Bool eina_strbuf_append_n(Eina_Strbuf *buf, const char *str, size_t maxlen) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Append a string of exact length to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @param length The exact length to use. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends @p str to @p buf. @p str must be of size at + * most @p length. It is slightly faster than eina_strbuf_append() as + * it does not compute the size of @p str. It is useful when dealing + * with strings of known size, such as eina_strngshare. If @p buf + * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + * + * @see eina_stringshare_length() + * @see eina_strbuf_append() + * @see eina_strbuf_append_n() + */ +EAPI Eina_Bool eina_strbuf_append_length(Eina_Strbuf *buf, const char *str, size_t length) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Append a character to a string buffer, reallocating as + * necessary. + * + * @param buf The string buffer to append to. + * @param c The char to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p c to @p buf. If it can not insert it, + * #EINA_FALSE is returned, otherwise #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_strbuf_append_char(Eina_Strbuf *buf, char c) EINA_ARG_NONNULL(1); + +/** + * @brief Append a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param fmt The string to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * @see eina_strbuf_append() + */ +EAPI Eina_Bool eina_strbuf_append_printf(Eina_Strbuf *buf, const char *fmt, ...) EINA_ARG_NONNULL(1, 2) EINA_PRINTF(2, 3); + +/** + * @brief Append a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param fmt The string to append. + * @param args The variable arguments. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * @see eina_strbuf_append() + */ +EAPI Eina_Bool eina_strbuf_append_vprintf(Eina_Strbuf *buf, const char *fmt, va_list args) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to insert. + * @param str The string to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str to @p buf at position @p pos. It + * computes the length of @p str, so is slightly slower than + * eina_strbuf_insert_length(). If the length is known beforehand, + * consider using that variant. If @p buf can't insert it, #EINA_FALSE + * is returned, otherwise #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_strbuf_insert(Eina_Strbuf *buf, const char *str, size_t pos) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert an escaped string to a buffer, reallocating as + * necessary. + * + * @param buf The string buffer to insert to. + * @param str The string to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts the escaped string @p str to @p buf at + * position @p pos. If @p buf can't insert @p str, #EINA_FALSE is + * returned, otherwise #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_strbuf_insert_escaped(Eina_Strbuf *buf, const char *str, size_t pos) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert a string to a buffer, reallocating as necessary. Limited by maxlen. + * + * @param buf The string buffer to insert to. + * @param str The string to insert. + * @param maxlen The maximum number of chars to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str ot @p buf at position @p pos, with at + * most @p maxlen bytes. The number of inserted characters can not be + * greater than the length of @p str. It computes the length of + * @p str, so is slightly slower than eina_strbuf_insert_length(). If the + * length is known beforehand, consider using that variant (@p maxlen + * should then be checked so that it is greater than the size of + * @p str). If @p str can not be inserted, #EINA_FALSE is returned, + * otherwise, #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_strbuf_insert_n(Eina_Strbuf *buf, const char *str, size_t maxlen, size_t pos) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert a string of exact length to a buffer, reallocating as necessary. + * + * @param buf The string buffer to insert to. + * @param str The string to insert. + * @param length The exact length to use. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str to @p buf. @p str must be of size at + * most @p length. It is slightly faster than eina_strbuf_insert() as + * it does not compute the size of @p str. It is useful when dealing + * with strings of known size, such as eina_strngshare. If @p buf + * can't insert it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + * + * @see eina_stringshare_length() + * @see eina_strbuf_insert() + * @see eina_strbuf_insert_n() + */ +EAPI Eina_Bool eina_strbuf_insert_length(Eina_Strbuf *buf, const char *str, size_t length, size_t pos) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert a character to a string buffer, reallocating as + * necessary. + * + * @param buf The string buffer to insert to. + * @param c The char to insert. + * @param pos The position to insert the char. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p c to @p buf at position @p pos. If @p buf + * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +EAPI Eina_Bool eina_strbuf_insert_char(Eina_Strbuf *buf, char c, size_t pos) EINA_ARG_NONNULL(1); + +/** + * @brief Insert a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to insert. + * @param fmt The string to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +EAPI Eina_Bool eina_strbuf_insert_printf(Eina_Strbuf *buf, const char *fmt, size_t pos, ...) EINA_ARG_NONNULL(1, 2) EINA_PRINTF(2, 4); + +/** + * @brief Insert a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to insert. + * @param fmt The string to insert. + * @param pos The position to insert the string. + * @param args The variable arguments. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +EAPI Eina_Bool eina_strbuf_insert_vprintf(Eina_Strbuf *buf, const char *fmt, size_t pos, va_list args) EINA_ARG_NONNULL(1, 2); + +/** + * @def eina_strbuf_prepend(buf, str) + * @brief Prepend the given string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param str The string to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_strbuf_insert() at position 0.If @p buf + * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +#define eina_strbuf_prepend(buf, str) eina_strbuf_insert(buf, str, 0) + +/** + * @def eina_strbuf_prepend_escaped(buf, str) + * @brief Prepend the given escaped string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param str The string to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_strbuf_insert_escaped() at position 0. If + * @p buf can't prepend it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + */ +#define eina_strbuf_prepend_escaped(buf, str) eina_strbuf_insert_escaped(buf, str, 0) + +/** + * @def eina_strbuf_prepend_n(buf, str) + * @brief Prepend the given escaped string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param str The string to prepend. + * @param maxlen The maximum number of chars to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_strbuf_insert_n() at position 0. If + * @p buf can't prepend it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + */ +#define eina_strbuf_prepend_n(buf, str, maxlen) eina_strbuf_insert_n(buf, str, maxlen, 0) + +/** + * @def eina_strbuf_prepend_length(buf, str) + * @brief Prepend the given escaped string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param str The string to prepend. + * @param length The exact length to use. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_strbuf_insert_length() at position 0. If + * @p buf can't prepend it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + */ +#define eina_strbuf_prepend_length(buf, str, length) eina_strbuf_insert_length( buf, str, length, 0) + +/** + * @def eina_strbuf_prepend_char(buf, str) + * @brief Prepend the given character to the given buffer + * + * @param buf The string buffer to prepend to. + * @param c The character to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_strbuf_insert_char() at position 0. If + * @p buf can't prepend it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + */ +#define eina_strbuf_prepend_char(buf, c) eina_strbuf_insert_char(buf, c, 0) + +/** + * @def eina_strbuf_prepend_printf(buf, fmt, ...) + * @brief Prepend the given string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param fmt The string to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_strbuf_insert_printf() at position 0.If @p buf + * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +#define eina_strbuf_prepend_printf(buf, fmt, ...) eina_strbuf_insert_printf(buf, fmt, 0, ## __VA_ARGS__) + +/** + * @def eina_strbuf_prepend_vprintf(buf, fmt, args) + * @brief Prepend the given string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param fmt The string to prepend. + * @param args The variable arguments. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_strbuf_insert_vprintf() at position 0.If @p buf + * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +#define eina_strbuf_prepend_vprintf(buf, fmt, args) eina_strbuf_insert_vprintf(buf, fmt, 0, args) + +/** + * @brief Remove a slice of the given string buffer. + * + * @param buf The string buffer to remove a slice. + * @param start The initial (inclusive) slice position to start + * removing, in bytes. + * @param end The final (non-inclusive) slice position to finish + * removing, in bytes. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function removes a slice of @p buf, starting at @p start + * (inclusive) and ending at @p end (non-inclusive). Both values are + * in bytes. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise. + */ + +EAPI Eina_Bool eina_strbuf_remove(Eina_Strbuf *buf, size_t start, size_t end) EINA_ARG_NONNULL(1); + +/** + * @brief Retrieve a pointer to the contents of a string buffer + * + * @param buf The string buffer. + * @return The current string in the string buffer. + * + * This function returns the string contained in @p buf. The returned + * value must not be modified and will no longer be valid if @p buf is + * modified. In other words, any eina_strbuf_append() or similar will + * make that pointer invalid. + * + * @see eina_strbuf_string_steal() + */ +EAPI const char *eina_strbuf_string_get(const Eina_Strbuf *buf) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @brief Steal the contents of a string buffer. + * + * @param buf The string buffer to steal. + * @return The current string in the string buffer. + * + * This function returns the string contained in @p buf. @p buf is + * then initialized and does not own the returned string anymore. The + * caller must release the memory of the returned string by calling + * free(). + * + * @see eina_strbuf_string_get() + */ +EAPI char *eina_strbuf_string_steal(Eina_Strbuf *buf) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * @brief Free the contents of a string buffer but not the buffer. + * + * @param buf The string buffer to free the string of. + * + * This function frees the string contained in @p buf without freeing + * @p buf. + */ +EAPI void eina_strbuf_string_free(Eina_Strbuf *buf) EINA_ARG_NONNULL(1); + +/** + * @brief Retrieve the length of the string buffer content. + * + * @param buf The string buffer. + * @return The current length of the string, in bytes. + * + * This function returns the length of @p buf. + */ +EAPI size_t eina_strbuf_length_get(const Eina_Strbuf *buf) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool eina_strbuf_replace(Eina_Strbuf *buf, const char *str, const char *with, unsigned int n) EINA_ARG_NONNULL(1, 2, 3); + +/** + * @def eina_strbuf_replace_first(buf, str, with) + * @brief Prepend the given character to the given buffer + * + * @param buf The string buffer to work with. + * @param str The string to replace. + * @param with The replaceing string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_strbuf_replace() with the n-th occurrence + * equal to @c 1. If @p buf can't replace it, #EINA_FALSE is returned, + * otherwise #EINA_TRUE is returned. + */ +#define eina_strbuf_replace_first(buf, str, with) eina_strbuf_replace(buf, str, with, 1) + +EAPI int eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, const char *with) EINA_ARG_NONNULL(1, 2, 3); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_STRBUF_H */ diff --git a/tests/suite/ecore/src/include/eina_stringshare.h b/tests/suite/ecore/src/include/eina_stringshare.h new file mode 100644 index 0000000000..4df5b40ab9 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_stringshare.h @@ -0,0 +1,93 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (C) 2008 Peter Wehrfritz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software and its Copyright notices. In addition publicly + * documented acknowledgment must be given that this software has been used if no + * source code of this software is made available publicly. This includes + * acknowledgments in either Copyright notices, Manuals, Publicity and Marketing + * documents or any documentation provided with any product containing this + * software. This License does not apply to any software that links to the + * libraries provided by this software (statically or dynamically), but only to + * the software provided. + * + * Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice + * and it's intent. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef EINA_STRINGSHARE_H_ +#define EINA_STRINGSHARE_H_ + +#include <stdarg.h> + +#include "eina_types.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @defgroup Eina_Stringshare_Group Stringshare + * + * @{ + */ + +EAPI const char * eina_stringshare_add_length(const char *str, unsigned int slen) EINA_WARN_UNUSED_RESULT; +EAPI const char * eina_stringshare_add(const char *str) EINA_WARN_UNUSED_RESULT; +EAPI const char * eina_stringshare_printf(const char *fmt, ...) EINA_WARN_UNUSED_RESULT EINA_PRINTF(1, 2); +EAPI const char * eina_stringshare_vprintf(const char *fmt, va_list args) EINA_WARN_UNUSED_RESULT; +EAPI const char * eina_stringshare_nprintf(unsigned int len, const char *fmt, ...) EINA_WARN_UNUSED_RESULT EINA_PRINTF(2, 3); +EAPI const char * eina_stringshare_ref(const char *str); +EAPI void eina_stringshare_del(const char *str); +EAPI int eina_stringshare_strlen(const char *str) EINA_PURE EINA_WARN_UNUSED_RESULT; +EAPI void eina_stringshare_dump(void); + +static inline Eina_Bool eina_stringshare_replace(const char **p_str, const char *news) EINA_ARG_NONNULL(1); +static inline Eina_Bool eina_stringshare_replace_length(const char **p_str, const char *news, unsigned int slen) EINA_ARG_NONNULL(1); + +#include "eina_inline_stringshare.x" + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_STRINGSHARE_H_ */ diff --git a/tests/suite/ecore/src/include/eina_tiler.h b/tests/suite/ecore/src/include/eina_tiler.h new file mode 100644 index 0000000000..19762a5cf8 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_tiler.h @@ -0,0 +1,86 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_TILER_H_ +#define EINA_TILER_H_ + +#include "eina_types.h" +#include "eina_iterator.h" +#include "eina_rectangle.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @defgroup Eina_Tiler_Group Tiler + * + * @{ + */ + +/** + * @typedef Eina_Tiler + * Tiler type. + */ +typedef struct _Eina_Tiler Eina_Tiler; + +/** + * @typedef Eina_Tile_Grid_Info + * Grid type of a tiler. + */ +typedef struct Eina_Tile_Grid_Info Eina_Tile_Grid_Info; + +/** + * @struct Eina_Tile_Grid_Info + * Grid type of a tiler. + */ +struct Eina_Tile_Grid_Info +{ + unsigned long col; /**< column of the tiler grid */ + unsigned long row; /**< row of the tiler grid*/ + Eina_Rectangle rect; /**< rectangle of the tiler grid*/ + Eina_Bool full; /**< whether the grid is full or not */ +}; + +typedef struct _Eina_Tile_Grid_Slicer Eina_Tile_Grid_Slicer; + +EAPI Eina_Tiler * eina_tiler_new(int w, int h); +EAPI void eina_tiler_free(Eina_Tiler *t); +EAPI void eina_tiler_tile_size_set(Eina_Tiler *t, int w, int h); +EAPI Eina_Bool eina_tiler_rect_add(Eina_Tiler *t, const Eina_Rectangle *r); +EAPI void eina_tiler_rect_del(Eina_Tiler *t, const Eina_Rectangle *r); +EAPI void eina_tiler_clear(Eina_Tiler *t); +EAPI Eina_Iterator *eina_tiler_iterator_new(const Eina_Tiler *t); +EAPI Eina_Iterator *eina_tile_grid_slicer_iterator_new(int x, int y, int w, int h, int tile_w, int tile_h); +static inline Eina_Bool eina_tile_grid_slicer_next(Eina_Tile_Grid_Slicer *slc, const Eina_Tile_Grid_Info **rect); +static inline Eina_Bool eina_tile_grid_slicer_setup(Eina_Tile_Grid_Slicer *slc, int x, int y, int w, int h, int tile_w, int tile_h); + +#include "eina_inline_tiler.x" + + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_TILER_H_ */ diff --git a/tests/suite/ecore/src/include/eina_trash.h b/tests/suite/ecore/src/include/eina_trash.h new file mode 100644 index 0000000000..0d165f1931 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_trash.h @@ -0,0 +1,100 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_TRASH_H__ +#define EINA_TRASH_H__ + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @addtogroup Eina_Containers_Group Containers + * + * @{ + */ + +/** + * @defgroup Eina_Trash_Group Trash + * + * @{ + */ + +/** + * @typedef Eina_Trash + * Type for a generic container of unused allocated pointer. + */ +typedef struct _Eina_Trash Eina_Trash; + +/** + * @struct _Eina_Trash + * Type for a generic container of unused allocated pointer. + */ +struct _Eina_Trash +{ + Eina_Trash *next; /**< next item in trash. */ +}; + +static inline void eina_trash_init(Eina_Trash **trash) EINA_ARG_NONNULL(1); +static inline void eina_trash_push(Eina_Trash **trash, void *data) EINA_ARG_NONNULL(1); +static inline void *eina_trash_pop(Eina_Trash **trash) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @def EINA_TRASH_CLEAN + * @brief Macro to remove all pointer from the trash. + * + * @param trash The trash to clean. + * @param data The pointer extracted from the trash. + * + * This macro allow the cleaning of @p trash in an easy way. It will + * remove all pointers from @p trash until it's empty. + * + * This macro can be used for freeing the data in the trash, like in + * the following example: + * + * @code + * Eina_Trash *trash = NULL; + * char *data; + * + * // trash is filled with pointer to some duped strings. + * + * EINA_TRASH_CLEAN(&trash, data) + * free(data); + * @endcode + * + * @note this macro is useful when you implement some memory pool. + */ +#define EINA_TRASH_CLEAN(trash, data) while ((data = eina_trash_pop(trash)) + +#include "eina_inline_trash.x" + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_TRASH_H_ */ diff --git a/tests/suite/ecore/src/include/eina_types.h b/tests/suite/ecore/src/include/eina_types.h new file mode 100644 index 0000000000..c94983f618 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_types.h @@ -0,0 +1,284 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_TYPES_H_ +#define EINA_TYPES_H_ + +/** + * @addtogroup Eina_Core_Group Core + * + * @{ + */ + +/** + * @defgroup Eina_Types_Group Types + * + * @{ + */ + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_EINA_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_EINA_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif + +#include "eina_config.h" + +#ifdef EINA_WARN_UNUSED_RESULT +# undef EINA_WARN_UNUSED_RESULT +#endif +#ifdef EINA_ARG_NONNULL +# undef EINA_ARG_NONNULL +#endif +#ifdef EINA_DEPRECATED +# undef EINA_DEPRECATED +#endif +#ifdef EINA_MALLOC +# undef EINA_MALLOC +#endif +#ifdef EINA_PURE +# undef EINA_PURE +#endif +#ifdef EINA_PRINTF +# undef EINA_PRINTF +#endif +#ifdef EINA_SCANF +# undef EINA_SCANF +#endif +#ifdef EINA_FORMAT +# undef EINA_FORMAT +#endif +#ifdef EINA_CONST +# undef EINA_CONST +#endif +#ifdef EINA_NOINSTRUMENT +# undef EINA_NOINSTRUMENT +#endif +#ifdef EINA_UNLIKELY +# undef EINA_UNLIKELY +#endif +#ifdef EINA_LIKELY +# undef EINA_LIKELY +#endif + + +#ifdef __GNUC__ +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# define EINA_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) +# else +# define EINA_WARN_UNUSED_RESULT +# endif + +# if (!defined(EINA_SAFETY_CHECKS)) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) +# define EINA_ARG_NONNULL(idx, ...) __attribute__ ((nonnull(idx, ## __VA_ARGS__))) +# else +# define EINA_ARG_NONNULL(idx, ...) +# endif + +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define EINA_DEPRECATED __attribute__ ((__deprecated__)) +# else +# define EINA_DEPRECATED +# endif + +# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) +# define EINA_MALLOC __attribute__ ((malloc)) +# define EINA_PURE __attribute__ ((pure)) +# else +# define EINA_MALLOC +# define EINA_PURE +# endif + +# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define EINA_PRINTF(fmt, arg) __attribute__((format (printf, fmt, arg))) +# define EINA_SCANF(fmt, arg) __attribute__((format (scanf, fmt, arg))) +# define EINA_FORMAT(fmt) __attribute__((format_arg(fmt))) +# define EINA_CONST __attribute__((const)) +# define EINA_NOINSTRUMENT __attribute__((no_instrument_function)) +# define EINA_UNLIKELY(exp) __builtin_expect((exp), 0) +# define EINA_LIKELY(exp) __builtin_expect((exp), 1) +# else +# define EINA_PRINTF(fmt, arg) +# define EINA_SCANF(fmt, arg) +# define EINA_FORMAT(fmt) +# define EINA_CONST +# define EINA_NOINSTRUMENT +# define EINA_UNLIKELY(exp) exp +# define EINA_LIKELY(exp) exp +# endif + +#elif defined(_WIN32) +# define EINA_WARN_UNUSED_RESULT +# define EINA_ARG_NONNULL(idx, ...) +# if defined(_MSC_VER) && _MSC_VER >= 1300 +# define EINA_DEPRECATED __declspec(deprecated) +# else +# define EINA_DEPRECATED +# endif +# define EINA_MALLOC +# define EINA_PURE +# define EINA_PRINTF(fmt, arg) +# define EINA_SCANF(fmt, arg) +# define EINA_FORMAT(fmt) +# define EINA_CONST +# define EINA_NOINSTRUMENT +# define EINA_UNLIKELY(exp) exp +# define EINA_LIKELY(exp) exp + +#elif defined(__SUNPRO_C) +# define EINA_WARN_UNUSED_RESULT +# define EINA_ARG_NONNULL(...) +# define EINA_DEPRECATED +# if __SUNPRO_C >= 0x590 +# define EINA_MALLOC __attribute__ ((malloc)) +# define EINA_PURE __attribute__ ((pure)) +# else +# define EINA_MALLOC +# define EINA_PURE +# endif +# define EINA_PRINTF(fmt, arg) +# define EINA_SCANF(fmt, arg) +# define EINA_FORMAT(fmt) +# if __SUNPRO_C >= 0x590 +# define EINA_CONST __attribute__ ((const)) +# else +# define EINA_CONST +# endif +# define EINA_NOINSTRUMENT +# define EINA_UNLIKELY(exp) exp +# define EINA_LIKELY(exp) exp + +#else /* ! __GNUC__ && ! _WIN32 && ! __SUNPRO_C */ + +/** + * @def EINA_WARN_UNUSED_RESULT + * Used to warn when the returned value of the function is not used. + */ +# define EINA_WARN_UNUSED_RESULT + +/** + * @def EINA_ARG_NONNULL + * Used to warn when the specified arguments of the function are @c NULL. + */ +# define EINA_ARG_NONNULL(idx, ...) + +/** + * @def EINA_DEPRECATED + * Used to warn when the function is considered as deprecated. + */ +# define EINA_DEPRECATED +# define EINA_MALLOC +# define EINA_PURE +# define EINA_PRINTF(fmt, arg) +# define EINA_SCANF(fmt, arg) +# define EINA_FORMAT(fmt) +# define EINA_CONST +# define EINA_NOINSTRUMENT +# define EINA_UNLIKELY(exp) exp +# define EINA_LIKELY(exp) exp +#endif /* ! __GNUC__ && ! _WIN32 && ! __SUNPRO_C */ + + +/** + * @typedef Eina_Bool + * Type to mimic a boolean. + * + * @note it differs from stdbool.h as this is defined as an unsigned + * char to make it usable by bitfields (Eina_Bool name:1) and + * also take as few bytes as possible. + */ +typedef unsigned char Eina_Bool; + +/** + * @def EINA_FALSE + * boolean value FALSE (numerical value 0) + */ +#define EINA_FALSE ((Eina_Bool)0) + +/** + * @def EINA_TRUE + * boolean value TRUE (numerical value 1) + */ +#define EINA_TRUE ((Eina_Bool)1) + +EAPI extern const unsigned int eina_prime_table[]; + +/** + * @typedef Eina_Compare_Cb + * Function used in functions using sorting. It compares @p data1 and + * @p data2. If @p data1 is 'less' than @p data2, -1 must be returned, + * if it is 'greater', 1 must be returned, and if they are equal, 0 + * must be returned. + */ +typedef int (*Eina_Compare_Cb)(const void *data1, const void *data2); + +/** + * @def EINA_COMPARE_CB + * Macro to cast to Eina_Compare_Cb. + */ +#define EINA_COMPARE_CB(function) ((Eina_Compare_Cb)function) + +typedef Eina_Bool (*Eina_Each_Cb)(const void *container, void *data, void *fdata); + +/** + * @def EINA_EACH_CB + * Macro to cast to Eina_Each. + */ +#define EINA_EACH_CB(Function) ((Eina_Each_Cb)Function) + +/** + * @typedef Eina_Free_Cb + * A callback type used to free data when iterating over a container. + */ +typedef void (*Eina_Free_Cb)(void *data); + +/** + * @def EINA_FREE_CB + * Macro to cast to Eina_Free_Cb. + */ +#define EINA_FREE_CB(Function) ((Eina_Free_Cb)Function) + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_TYPES_H_ */ diff --git a/tests/suite/ecore/src/include/eina_unicode.h b/tests/suite/ecore/src/include/eina_unicode.h new file mode 100644 index 0000000000..06a2af1752 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_unicode.h @@ -0,0 +1,67 @@ +#ifndef EINA_UNICODE_H +#define EINA_UNICODE_H + +#include <stdlib.h> + +#include "eina_config.h" +#include "eina_types.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ +/** + * @addtogroup Eina_Unicode_String Unicode String + * + * @brief These functions provide basic unicode string handling + * + * Eina_Unicode is a type that holds unicode codepoints. + * + * @{ + */ + +/** + * @typedef Eina_Unicode + * A type that holds Unicode codepoints. + */ +#if EINA_SIZEOF_WCHAR_T >= 4 +# include <wchar.h> +typedef wchar_t Eina_Unicode; +#elif defined(EINA_HAVE_INTTYPES_H) +# include <inttypes.h> +typedef uint32_t Eina_Unicode; +#elif defined(EINA_HAVE_STDINT_H) +# include <stdint.h> +typedef uint32_t Eina_Unicode; +#else +/* Hope that int is big enough */ +typedef unsigned int Eina_Unicode; +#endif + +EAPI extern const Eina_Unicode *EINA_UNICODE_EMPTY_STRING; + +EAPI size_t eina_unicode_strlen(const Eina_Unicode *ustr) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE; +EAPI size_t eina_unicode_strnlen(const Eina_Unicode *ustr, int n) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE; + + +EAPI Eina_Unicode *eina_unicode_strdup(const Eina_Unicode *text) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; + +EAPI int eina_unicode_strcmp(const Eina_Unicode *a, const Eina_Unicode *b) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2) EINA_PURE; + +EAPI Eina_Unicode *eina_unicode_strcpy(Eina_Unicode *dest, const Eina_Unicode *source) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Unicode *eina_unicode_strstr(const Eina_Unicode *haystack, const Eina_Unicode *needle) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2) EINA_PURE; + +EAPI Eina_Unicode *eina_unicode_strncpy(Eina_Unicode *dest, const Eina_Unicode *source, size_t n) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Unicode *eina_unicode_escape(const Eina_Unicode *str) EINA_ARG_NONNULL(1) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + +/** + * @} + */ +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/include/eina_ustrbuf.h b/tests/suite/ecore/src/include/eina_ustrbuf.h new file mode 100644 index 0000000000..e042b5df9d --- /dev/null +++ b/tests/suite/ecore/src/include/eina_ustrbuf.h @@ -0,0 +1,418 @@ +#ifndef EINA_USTRBUF_H +#define EINA_USTRBUF_H + +#include <stddef.h> + +#include "eina_types.h" +#include "eina_unicode.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @defgroup Eina_Unicode_String_Buffer_Group Unicode String Buffer + * + * @{ + */ + +/** + * @typedef Eina_UStrbuf + * Type for a string buffer. + */ +typedef struct _Eina_Strbuf Eina_UStrbuf; + +/** + * @brief Create a new string buffer. + * + * @return Newly allocated string buffer instance. + * + * This function creates a new string buffer. On error, @c NULL is + * returned and Eina error is set to #EINA_ERROR_OUT_OF_MEMORY. To + * free the resources, use eina_ustrbuf_free(). + * + * @see eina_ustrbuf_free() + * @see eina_ustrbuf_append() + * @see eina_ustrbuf_string_get() + */ +EAPI Eina_UStrbuf *eina_ustrbuf_new(void) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + +/** + * @brief Free a string buffer. + * + * @param buf The string buffer to free. + * + * This function frees the memory of @p buf. @p buf must have been + * created by eina_ustrbuf_new(). + */ +EAPI void eina_ustrbuf_free(Eina_UStrbuf *buf) EINA_ARG_NONNULL(1); + +/** + * @brief Reset a string buffer. + * + * @param buf The string buffer to reset. + * + * This function reset @p buf: the buffer len is set to 0, and the + * string is set to '\\0'. No memory is free'd. + */ +EAPI void eina_ustrbuf_reset(Eina_UStrbuf *buf) EINA_ARG_NONNULL(1); + +/** + * @brief Append a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends @p str to @p buf. It computes the length of + * @p str, so is slightly slower than eina_ustrbuf_append_length(). If + * the length is known beforehand, consider using that variant. If + * @p buf can't append it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + * + * @see eina_ustrbuf_append() + * @see eina_ustrbuf_append_length() + */ +EAPI Eina_Bool eina_ustrbuf_append(Eina_UStrbuf *buf, const Eina_Unicode *str) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Append an escaped string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends the escaped string @p str to @p buf. If @p + * str can not be appended, #EINA_FALSE is returned, otherwise, + * #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_ustrbuf_append_escaped(Eina_UStrbuf *buf, const Eina_Unicode *str) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Append a string to a buffer, reallocating as necessary, + * limited by the given length. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @param maxlen The maximum number of characters to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends at most @p maxlen characters of @p str to + * @p buf. It can't appends more than the length of @p str. It + * computes the length of @p str, so is slightly slower than + * eina_ustrbuf_append_length(). If the length is known beforehand, + * consider using that variant (@p maxlen should then be checked so + * that it is greater than the size of @p str). If @p str can not be + * appended, #EINA_FALSE is returned, otherwise, #EINA_TRUE is + * returned. + * + * @see eina_ustrbuf_append() + * @see eina_ustrbuf_append_length() + */ +EAPI Eina_Bool eina_ustrbuf_append_n(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t maxlen) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Append a string of exact length to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @param length The exact length to use. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends @p str to @p buf. @p str must be of size at + * most @p length. It is slightly faster than eina_ustrbuf_append() as + * it does not compute the size of @p str. It is useful when dealing + * with strings of known size, such as eina_strngshare. If @p buf + * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + * + * @see eina_stringshare_length() + * @see eina_ustrbuf_append() + * @see eina_ustrbuf_append_n() + */ +EAPI Eina_Bool eina_ustrbuf_append_length(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t length) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Append a character to a string buffer, reallocating as + * necessary. + * + * @param buf The string buffer to append to. + * @param c The char to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p c to @p buf. If it can not insert it, + * #EINA_FALSE is returned, otherwise #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_ustrbuf_append_char(Eina_UStrbuf *buf, Eina_Unicode c) EINA_ARG_NONNULL(1); + +/** + * @brief Insert a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to insert. + * @param str The string to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str to @p buf at position @p pos. It + * computes the length of @p str, so is slightly slower than + * eina_ustrbuf_insert_length(). If the length is known beforehand, + * consider using that variant. If @p buf can't insert it, #EINA_FALSE + * is returned, otherwise #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_ustrbuf_insert(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t pos) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert an escaped string to a buffer, reallocating as + * necessary. + * + * @param buf The string buffer to insert to. + * @param str The string to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts the escaped string @p str to @p buf at + * position @p pos. If @p buf can't insert @p str, #EINA_FALSE is + * returned, otherwise #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_ustrbuf_insert_escaped(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t pos) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert a string to a buffer, reallocating as necessary. Limited by maxlen. + * + * @param buf The string buffer to insert to. + * @param str The string to insert. + * @param maxlen The maximum number of chars to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str ot @p buf at position @p pos, with at + * most @p maxlen bytes. The number of inserted characters can not be + * greater than the length of @p str. It computes the length of + * @p str, so is slightly slower than eina_ustrbuf_insert_length(). If the + * length is known beforehand, consider using that variant (@p maxlen + * should then be checked so that it is greater than the size of + * @p str). If @p str can not be inserted, #EINA_FALSE is returned, + * otherwise, #EINA_TRUE is returned. + */ +EAPI Eina_Bool eina_ustrbuf_insert_n(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t maxlen, size_t pos) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert a string of exact length to a buffer, reallocating as necessary. + * + * @param buf The string buffer to insert to. + * @param str The string to insert. + * @param length The exact length to use. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str to @p buf. @p str must be of size at + * most @p length. It is slightly faster than eina_ustrbuf_insert() as + * it does not compute the size of @p str. It is useful when dealing + * with strings of known size, such as eina_strngshare. If @p buf + * can't insert it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + * + * @see eina_stringshare_length() + * @see eina_ustrbuf_insert() + * @see eina_ustrbuf_insert_n() + */ +EAPI Eina_Bool eina_ustrbuf_insert_length(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t length, size_t pos) EINA_ARG_NONNULL(1, 2); + +/** + * @brief Insert a character to a string buffer, reallocating as + * necessary. + * + * @param buf The string buffer to insert to. + * @param c The char to insert. + * @param pos The position to insert the char. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p c to @p buf at position @p pos. If @p buf + * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +EAPI Eina_Bool eina_ustrbuf_insert_char(Eina_UStrbuf *buf, Eina_Unicode c, size_t pos) EINA_ARG_NONNULL(1); + +/** + * @def eina_ustrbuf_prepend(buf, str) + * @brief Prepend the given string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param str The string to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_ustrbuf_insert() at position 0.If @p buf + * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +#define eina_ustrbuf_prepend(buf, str) eina_ustrbuf_insert(buf, str, 0) + +/** + * @def eina_ustrbuf_prepend_escaped(buf, str) + * @brief Prepend the given escaped string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param str The string to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_ustrbuf_insert_escaped() at position 0. If + * @p buf can't prepend it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + */ +#define eina_ustrbuf_prepend_escaped(buf, str) eina_ustrbuf_insert_escaped(buf, str, 0) + +/** + * @def eina_ustrbuf_prepend_n(buf, str) + * @brief Prepend the given escaped string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param str The string to prepend. + * @param maxlen The maximum number of Eina_Unicode *s to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_ustrbuf_insert_n() at position 0. If + * @p buf can't prepend it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + */ +#define eina_ustrbuf_prepend_n(buf, str, maxlen) eina_ustrbuf_insert_n(buf, str, maxlen, 0) + +/** + * @def eina_ustrbuf_prepend_length(buf, str) + * @brief Prepend the given escaped string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param str The string to prepend. + * @param length The exact length to use. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_ustrbuf_insert_length() at position 0. If + * @p buf can't prepend it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + */ +#define eina_ustrbuf_prepend_length(buf, str, length) eina_ustrbuf_insert_length(buf, str, length, 0) + +/** + * @def eina_ustrbuf_prepend_Eina_Unicode *(buf, str) + * @brief Prepend the given Eina_Unicode *acter to the given buffer + * + * @param buf The string buffer to prepend to. + * @param c The Eina_Unicode *acter to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_ustrbuf_insert_Eina_Unicode *() at position 0. If + * @p buf can't prepend it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + */ +#define eina_ustrbuf_prepend_Eina_Unicode *(buf, c)eina_ustrbuf_insert_Eina_Unicode * (buf, c, 0) + +/** + * @def eina_ustrbuf_prepend_printf(buf, fmt, ...) + * @brief Prepend the given string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param fmt The string to prepend. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_ustrbuf_insert_printf() at position 0.If @p buf + * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +#define eina_ustrbuf_prepend_printf(buf, fmt, ...) eina_ustrbuf_insert_printf(buf, fmt, 0, ## __VA_ARGS__) + +/** + * @def eina_ustrbuf_prepend_vprintf(buf, fmt, args) + * @brief Prepend the given string to the given buffer + * + * @param buf The string buffer to prepend to. + * @param fmt The string to prepend. + * @param args The variable arguments. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This macro is calling eina_ustrbuf_insert_vprintf() at position 0.If @p buf + * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +#define eina_ustrbuf_prepend_vprintf(buf, fmt, args) eina_ustrbuf_insert_vprintf(buf, fmt, 0, args) + +/** + * @brief Remove a slice of the given string buffer. + * + * @param buf The string buffer to remove a slice. + * @param start The initial (inclusive) slice position to start + * removing, in bytes. + * @param end The final (non-inclusive) slice position to finish + * removing, in bytes. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function removes a slice of @p buf, starting at @p start + * (inclusive) and ending at @p end (non-inclusive). Both values are + * in bytes. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise. + */ +EAPI Eina_Bool +eina_ustrbuf_remove(Eina_UStrbuf *buf, size_t start, size_t end) EINA_ARG_NONNULL(1); + +/** + * @brief Retrieve a pointer to the contents of a string buffer + * + * @param buf The string buffer. + * @return The current string in the string buffer. + * + * This function returns the string contained in @p buf. The returned + * value must not be modified and will no longer be valid if @p buf is + * modified. In other words, any eina_ustrbuf_append() or similar will + * make that pointer invalid. + * + * @see eina_ustrbuf_string_steal() + */ +EAPI const Eina_Unicode * +eina_ustrbuf_string_get(const Eina_UStrbuf *buf) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @brief Steal the contents of a string buffer. + * + * @param buf The string buffer to steal. + * @return The current string in the string buffer. + * + * This function returns the string contained in @p buf. @p buf is + * then initialized and does not own the returned string anymore. The + * caller must release the memory of the returned string by calling + * free(). + * + * @see eina_ustrbuf_string_get() + */ +EAPI Eina_Unicode * +eina_ustrbuf_string_steal(Eina_UStrbuf *buf) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); + +/** + * @brief Free the contents of a string buffer but not the buffer. + * + * @param buf The string buffer to free the string of. + * + * This function frees the string contained in @p buf without freeing + * @p buf. + */ +EAPI void +eina_ustrbuf_string_free(Eina_UStrbuf *buf) EINA_ARG_NONNULL(1); + +/** + * @brief Retrieve the length of the string buffer content. + * + * @param buf The string buffer. + * @return The current length of the string, in bytes. + * + * This function returns the length of @p buf. + */ +EAPI size_t +eina_ustrbuf_length_get(const Eina_UStrbuf *buf) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_STRBUF_H */ diff --git a/tests/suite/ecore/src/include/eina_ustringshare.h b/tests/suite/ecore/src/include/eina_ustringshare.h new file mode 100644 index 0000000000..ac8c8896e2 --- /dev/null +++ b/tests/suite/ecore/src/include/eina_ustringshare.h @@ -0,0 +1,89 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (C) 2008 Peter Wehrfritz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software and its Copyright notices. In addition publicly + * documented acknowledgment must be given that this software has been used if no + * source code of this software is made available publicly. This includes + * acknowledgments in either Copyright notices, Manuals, Publicity and Marketing + * documents or any documentation provided with any product containing this + * software. This License does not apply to any software that links to the + * libraries provided by this software (statically or dynamically), but only to + * the software provided. + * + * Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice + * and it's intent. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef EINA_USTRINGSHARE_H_ +#define EINA_USTRINGSHARE_H_ + +#include "eina_types.h" +#include "eina_unicode.h" + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @{ + */ + +/** + * @defgroup Eina_UStringshare_Group Unicode Stringshare + * + * @{ + */ + +EAPI const Eina_Unicode *eina_ustringshare_add_length(const Eina_Unicode *str, unsigned int slen) EINA_WARN_UNUSED_RESULT; +EAPI const Eina_Unicode *eina_ustringshare_add(const Eina_Unicode *str) EINA_WARN_UNUSED_RESULT; +EAPI const Eina_Unicode *eina_ustringshare_ref(const Eina_Unicode *str); +EAPI void eina_ustringshare_del(const Eina_Unicode *str); +EAPI int eina_ustringshare_strlen(const Eina_Unicode *str) EINA_PURE EINA_WARN_UNUSED_RESULT; +EAPI void eina_ustringshare_dump(void); + +static inline Eina_Bool eina_ustringshare_replace(const Eina_Unicode **p_str, const Eina_Unicode *news) EINA_ARG_NONNULL(1); +static inline Eina_Bool eina_ustringshare_replace_length(const Eina_Unicode **p_str, const Eina_Unicode *news, unsigned int slen) EINA_ARG_NONNULL(1); + +#include "eina_inline_ustringshare.x" + +/** + * @} + */ + +/** + * @} + */ + +#endif /* EINA_STRINGSHARE_H_ */ diff --git a/tests/suite/ecore/src/lib/Ecore.h b/tests/suite/ecore/src/lib/Ecore.h new file mode 100644 index 0000000000..a70fa21bfb --- /dev/null +++ b/tests/suite/ecore/src/lib/Ecore.h @@ -0,0 +1,484 @@ +#ifndef _ECORE_H +#define _ECORE_H + +#ifdef _MSC_VER +# include <Evil.h> +#endif + +#include <Eina.h> + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_ECORE_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_ECORE_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif /* ! _WIN32 */ + +/** + * @file Ecore.h + * @brief The file that provides the program utility, main loop and timer + * functions. + * + * This header provides the Ecore event handling loop. For more + * details, see @ref Ecore_Main_Loop_Group. + * + * For the main loop to be of any use, you need to be able to add events + * and event handlers. Events for file descriptor events are covered in + * @ref Ecore_FD_Handler_Group. + * + * Time functions are covered in @ref Ecore_Time_Group. + * + * There is also provision for callbacks for when the loop enters or + * exits an idle state. See @ref Idle_Group for more information. + * + * Functions are also provided for spawning child processes using fork. + * See @ref Ecore_Exe_Basic_Group and @ref Ecore_Exe_Signal_Group for + * more details. + */ + +#ifdef _WIN32 +# include <winsock2.h> +#elif (defined (__FreeBSD__) && (__FreeBSD_version >= 420001)) || defined (__OpenBSD__) +# include <sys/select.h> +# include <signal.h> +#else +# include <sys/time.h> +# include <signal.h> +#endif + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define ECORE_VERSION_MAJOR 1 +#define ECORE_VERSION_MINOR 0 + + typedef struct _Ecore_Version + { + int major; + int minor; + int micro; + int revision; + } Ecore_Version; + + EAPI extern Ecore_Version *ecore_version; + +#define ECORE_CALLBACK_CANCEL EINA_FALSE /**< Return value to remove a callback */ +#define ECORE_CALLBACK_RENEW EINA_TRUE /**< Return value to keep a callback */ + +#define ECORE_CALLBACK_PASS_ON EINA_TRUE /**< Return value to pass event to next handler */ +#define ECORE_CALLBACK_DONE EINA_FALSE /**< Return value to stop event handling */ + +#define ECORE_EVENT_NONE 0 +#define ECORE_EVENT_SIGNAL_USER 1 /**< User signal event */ +#define ECORE_EVENT_SIGNAL_HUP 2 /**< Hup signal event */ +#define ECORE_EVENT_SIGNAL_EXIT 3 /**< Exit signal event */ +#define ECORE_EVENT_SIGNAL_POWER 4 /**< Power signal event */ +#define ECORE_EVENT_SIGNAL_REALTIME 5 /**< Realtime signal event */ +#define ECORE_EVENT_COUNT 6 + +#define ECORE_EXE_PRIORITY_INHERIT 9999 + + EAPI extern int ECORE_EXE_EVENT_ADD; /**< A child process has been added */ + EAPI extern int ECORE_EXE_EVENT_DEL; /**< A child process has been deleted (it exited, naming consistent with the rest of ecore). */ + EAPI extern int ECORE_EXE_EVENT_DATA; /**< Data from a child process. */ + EAPI extern int ECORE_EXE_EVENT_ERROR; /**< Errors from a child process. */ + + enum _Ecore_Fd_Handler_Flags + { + ECORE_FD_READ = 1, /**< Fd Read mask */ + ECORE_FD_WRITE = 2, /**< Fd Write mask */ + ECORE_FD_ERROR = 4 /**< Fd Error mask */ + }; + typedef enum _Ecore_Fd_Handler_Flags Ecore_Fd_Handler_Flags; + + enum _Ecore_Exe_Flags /* flags for executing a child with its stdin and/or stdout piped back */ + { + ECORE_EXE_PIPE_READ = 1, /**< Exe Pipe Read mask */ + ECORE_EXE_PIPE_WRITE = 2, /**< Exe Pipe Write mask */ + ECORE_EXE_PIPE_ERROR = 4, /**< Exe Pipe error mask */ + ECORE_EXE_PIPE_READ_LINE_BUFFERED = 8, /**< Reads are buffered until a newline and delivered 1 event per line */ + ECORE_EXE_PIPE_ERROR_LINE_BUFFERED = 16, /**< Errors are buffered until a newline and delivered 1 event per line */ + ECORE_EXE_PIPE_AUTO = 32, /**< stdout and stderr are buffered automatically */ + ECORE_EXE_RESPAWN = 64, /**< FIXME: Exe is restarted if it dies */ + ECORE_EXE_USE_SH = 128, /**< Use /bin/sh to run the command. */ + ECORE_EXE_NOT_LEADER = 256 /**< Do not use setsid() to have the executed process be its own session leader */ + }; + typedef enum _Ecore_Exe_Flags Ecore_Exe_Flags; + + enum _Ecore_Exe_Win32_Priority + { + ECORE_EXE_WIN32_PRIORITY_IDLE, /**< Idle priority, for monitoring the system */ + ECORE_EXE_WIN32_PRIORITY_BELOW_NORMAL, /**< Below default priority */ + ECORE_EXE_WIN32_PRIORITY_NORMAL, /**< Default priority */ + ECORE_EXE_WIN32_PRIORITY_ABOVE_NORMAL, /**< Above default priority */ + ECORE_EXE_WIN32_PRIORITY_HIGH, /**< High priority, use with care as other threads in the system will not get processor time */ + ECORE_EXE_WIN32_PRIORITY_REALTIME /**< Realtime priority, should be almost never used as it can interrupt system threads that manage mouse input, keyboard input, and background disk flushing */ + }; + typedef enum _Ecore_Exe_Win32_Priority Ecore_Exe_Win32_Priority; + + enum _Ecore_Poller_Type /* Poller types */ + { + ECORE_POLLER_CORE = 0 /**< The core poller interval */ + }; + typedef enum _Ecore_Poller_Type Ecore_Poller_Type; + + typedef struct _Ecore_Exe Ecore_Exe; /**< A handle for spawned processes */ + typedef struct _Ecore_Timer Ecore_Timer; /**< A handle for timers */ + typedef struct _Ecore_Idler Ecore_Idler; /**< A handle for idlers */ + typedef struct _Ecore_Idle_Enterer Ecore_Idle_Enterer; /**< A handle for idle enterers */ + typedef struct _Ecore_Idle_Exiter Ecore_Idle_Exiter; /**< A handle for idle exiters */ + typedef struct _Ecore_Fd_Handler Ecore_Fd_Handler; /**< A handle for Fd handlers */ + typedef struct _Ecore_Win32_Handler Ecore_Win32_Handler; /**< A handle for HANDLE handlers on Windows */ + typedef struct _Ecore_Event_Handler Ecore_Event_Handler; /**< A handle for an event handler */ + typedef struct _Ecore_Event_Filter Ecore_Event_Filter; /**< A handle for an event filter */ + typedef struct _Ecore_Event Ecore_Event; /**< A handle for an event */ + typedef struct _Ecore_Animator Ecore_Animator; /**< A handle for animators */ + typedef struct _Ecore_Pipe Ecore_Pipe; /**< A handle for pipes */ + typedef struct _Ecore_Poller Ecore_Poller; /**< A handle for pollers */ + typedef struct _Ecore_Event_Signal_User Ecore_Event_Signal_User; /**< User signal event */ + typedef struct _Ecore_Event_Signal_Hup Ecore_Event_Signal_Hup; /**< Hup signal event */ + typedef struct _Ecore_Event_Signal_Exit Ecore_Event_Signal_Exit; /**< Exit signal event */ + typedef struct _Ecore_Event_Signal_Power Ecore_Event_Signal_Power; /**< Power signal event */ + typedef struct _Ecore_Event_Signal_Realtime Ecore_Event_Signal_Realtime; /**< Realtime signal event */ + typedef struct _Ecore_Exe_Event_Add Ecore_Exe_Event_Add; /**< Spawned Exe add event */ + typedef struct _Ecore_Exe_Event_Del Ecore_Exe_Event_Del; /**< Spawned Exe exit event */ + typedef struct _Ecore_Exe_Event_Data_Line Ecore_Exe_Event_Data_Line; /**< Lines from a child process */ + typedef struct _Ecore_Exe_Event_Data Ecore_Exe_Event_Data; /**< Data from a child process */ + typedef struct _Ecore_Thread Ecore_Thread; + + /** + * @typedef Ecore_Data_Cb Ecore_Data_Cb + * A callback which is used to return data to the main function + */ + typedef void *(*Ecore_Data_Cb) (void *data); + /** + * @typedef Ecore_Filter_Cb + * A callback used for filtering events from the main loop. + */ + typedef Eina_Bool (*Ecore_Filter_Cb) (void *data, void *loop_data, int type, void *event); + /** + * @typedef Ecore_Eselect_Function Ecore_Eselect_Function + * A function which can be used to replace select() in the main loop + */ + typedef int (*Ecore_Select_Function)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + /** + * @typedef Ecore_End_Cb Ecore_End_Cb + * This is the callback which is called at the end of a function, usually for cleanup purposes. + */ + typedef void (*Ecore_End_Cb) (void *user_data, void *func_data); + /** + * @typedef Ecore_Pipe_Cb Ecore_Pipe_Cb + * The callback that data written to the pipe is sent to. + */ + typedef void (*Ecore_Pipe_Cb) (void *data, void *buffer, unsigned int nbyte); + /** + * @typedef Ecore_Exe_Cb Ecore_Exe_Cb + * A callback to run with the associated @ref Ecore_Exe, usually for cleanup purposes. + */ + typedef void (*Ecore_Exe_Cb)(void *data, const Ecore_Exe *exe); + /** + * @typedef Ecore_Event_Handler_Cb Ecore_Event_Handler_Cb + * A callback used by the main loop to handle events of a specified type. + */ + typedef Eina_Bool (*Ecore_Event_Handler_Cb) (void *data, int type, void *event); + /** + * @typedef Ecore_Thread_Heavy_Cb Ecore_Thread_Heavy_Cb + * A callback used to run cpu intensive or blocking I/O operations. + */ + typedef void (*Ecore_Thread_Heavy_Cb) (Ecore_Thread *thread, void *data); + /** + * @typedef Ecore_Thread_Notify_Cb Ecore_Thread_Notify_Cb + * A callback used by the main loop to receive data sent by an @ref Ecore_Thread. + */ + typedef void (*Ecore_Thread_Notify_Cb) (Ecore_Thread *thread, void *msg_data, void *data); + /** + * @typedef Ecore_Task_Cb Ecore_Task_Cb + * A callback run for a task (timer, idler, poller, animater, etc) + */ + typedef Eina_Bool (*Ecore_Task_Cb) (void *data); + /** + * @typedef Ecore_Cb Ecore_Cb + * A generic callback called as a hook when a certain point in execution is reached. + */ + typedef void (*Ecore_Cb) (void *data); + /** + * @typedef Ecore_Fd_Cb Ecore_Fd_Cb + * A callback used by an @ref Ecore_Fd_Handler. + */ + typedef Eina_Bool (*Ecore_Fd_Cb) (void *data, Ecore_Fd_Handler *fd_handler); + /** + * @typedef Ecore_Fd_Prep_Cb Ecore_Fd_Prep_Cb + * A callback used by an @ref Ecore_Fd_Handler. + */ + typedef void (*Ecore_Fd_Prep_Cb) (void *data, Ecore_Fd_Handler *fd_handler); + /** + * @typedef Ecore_Fd_Win32_Cb Ecore_Fd_Win32_Cb + * A callback used by an @ref Ecore_Win32_Handler. + */ + typedef Eina_Bool (*Ecore_Fd_Win32_Cb) (void *data, Ecore_Win32_Handler *wh); + + + typedef struct _Ecore_Job Ecore_Job; /**< A job handle */ + + struct _Ecore_Event_Signal_User /** User signal event */ + { + int number; /**< The signal number. Either 1 or 2 */ + void *ext_data; /**< Extension data - not used */ + +#ifndef _WIN32 + siginfo_t data; /**< Signal info */ +#endif + }; + + struct _Ecore_Event_Signal_Hup /** Hup signal event */ + { + void *ext_data; /**< Extension data - not used */ + +#ifndef _WIN32 + siginfo_t data; /**< Signal info */ +#endif + }; + + struct _Ecore_Event_Signal_Exit /** Exit request event */ + { + unsigned int interrupt : 1; /**< Set if the exit request was an interrupt signal*/ + unsigned int quit : 1; /**< set if the exit request was a quit signal */ + unsigned int terminate : 1; /**< Set if the exit request was a terminate singal */ + void *ext_data; /**< Extension data - not used */ + +#ifndef _WIN32 + siginfo_t data; /**< Signal info */ +#endif + }; + + struct _Ecore_Event_Signal_Power /** Power event */ + { + void *ext_data; /**< Extension data - not used */ + +#ifndef _WIN32 + siginfo_t data; /**< Signal info */ +#endif + }; + + struct _Ecore_Event_Signal_Realtime /** Realtime event */ + { + int num; /**< The realtime signal's number */ + +#ifndef _WIN32 + siginfo_t data; /**< Signal info */ +#endif + }; + + struct _Ecore_Exe_Event_Add /** Process add event */ + { + Ecore_Exe *exe; /**< The handle to the added process */ + void *ext_data; /**< Extension data - not used */ + }; + + struct _Ecore_Exe_Event_Del /** Process exit event */ + { + pid_t pid; /**< The process ID of the process that exited */ + int exit_code; /**< The exit code of the process */ + Ecore_Exe *exe; /**< The handle to the exited process, or NULL if not found */ + int exit_signal; /** < The signal that caused the process to exit */ + unsigned int exited : 1; /** < set to 1 if the process exited of its own accord */ + unsigned int signalled : 1; /** < set to 1 id the process exited due to uncaught signal */ + void *ext_data; /**< Extension data - not used */ +#ifndef _WIN32 + siginfo_t data; /**< Signal info */ +#endif + }; + + struct _Ecore_Exe_Event_Data_Line /**< Lines from a child process */ + { + char *line; + int size; + }; + + struct _Ecore_Exe_Event_Data /** Data from a child process event */ + { + Ecore_Exe *exe; /**< The handle to the process */ + void *data; /**< the raw binary data from the child process that was received */ + int size; /**< the size of this data in bytes */ + Ecore_Exe_Event_Data_Line *lines; /**< an array of line data if line buffered, the last one has it's line member set to NULL */ + }; + + EAPI int ecore_init(void); + EAPI int ecore_shutdown(void); + + EAPI void ecore_app_args_set(int argc, const char **argv); + EAPI void ecore_app_args_get(int *argc, char ***argv); + EAPI void ecore_app_restart(void); + + EAPI Ecore_Event_Handler *ecore_event_handler_add(int type, Ecore_Event_Handler_Cb func, const void *data); + EAPI void *ecore_event_handler_del(Ecore_Event_Handler *event_handler); + EAPI Ecore_Event *ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data); + EAPI void *ecore_event_del(Ecore_Event *event); + EAPI int ecore_event_type_new(void); + EAPI Ecore_Event_Filter *ecore_event_filter_add(Ecore_Data_Cb func_start, Ecore_Filter_Cb func_filter, Ecore_End_Cb func_end, const void *data); + EAPI void *ecore_event_filter_del(Ecore_Event_Filter *ef); + EAPI int ecore_event_current_type_get(void); + EAPI void *ecore_event_current_event_get(void); + + + EAPI void ecore_exe_run_priority_set(int pri); + EAPI int ecore_exe_run_priority_get(void); + EAPI Ecore_Exe *ecore_exe_run(const char *exe_cmd, const void *data); + EAPI Ecore_Exe *ecore_exe_pipe_run(const char *exe_cmd, Ecore_Exe_Flags flags, const void *data); + EAPI void ecore_exe_callback_pre_free_set(Ecore_Exe *exe, Ecore_Exe_Cb func); + EAPI Eina_Bool ecore_exe_send(Ecore_Exe *exe, const void *data, int size); + EAPI void ecore_exe_close_stdin(Ecore_Exe *exe); + EAPI void ecore_exe_auto_limits_set(Ecore_Exe *exe, int start_bytes, int end_bytes, int start_lines, int end_lines); + EAPI Ecore_Exe_Event_Data *ecore_exe_event_data_get(Ecore_Exe *exe, Ecore_Exe_Flags flags); + EAPI void ecore_exe_event_data_free(Ecore_Exe_Event_Data *data); + EAPI void *ecore_exe_free(Ecore_Exe *exe); + EAPI pid_t ecore_exe_pid_get(const Ecore_Exe *exe); + EAPI void ecore_exe_tag_set(Ecore_Exe *exe, const char *tag); + EAPI const char *ecore_exe_tag_get(const Ecore_Exe *exe); + EAPI const char *ecore_exe_cmd_get(const Ecore_Exe *exe); + EAPI void *ecore_exe_data_get(const Ecore_Exe *exe); + EAPI Ecore_Exe_Flags ecore_exe_flags_get(const Ecore_Exe *exe); + EAPI void ecore_exe_pause(Ecore_Exe *exe); + EAPI void ecore_exe_continue(Ecore_Exe *exe); + EAPI void ecore_exe_interrupt(Ecore_Exe *exe); + EAPI void ecore_exe_quit(Ecore_Exe *exe); + EAPI void ecore_exe_terminate(Ecore_Exe *exe); + EAPI void ecore_exe_kill(Ecore_Exe *exe); + EAPI void ecore_exe_signal(Ecore_Exe *exe, int num); + EAPI void ecore_exe_hup(Ecore_Exe *exe); + + EAPI Ecore_Idler *ecore_idler_add(Ecore_Task_Cb func, const void *data); + EAPI void *ecore_idler_del(Ecore_Idler *idler); + + EAPI Ecore_Idle_Enterer *ecore_idle_enterer_add(Ecore_Task_Cb func, const void *data); + EAPI Ecore_Idle_Enterer *ecore_idle_enterer_before_add(Ecore_Task_Cb func, const void *data); + EAPI void *ecore_idle_enterer_del(Ecore_Idle_Enterer *idle_enterer); + + EAPI Ecore_Idle_Exiter *ecore_idle_exiter_add(Ecore_Task_Cb func, const void *data); + EAPI void *ecore_idle_exiter_del(Ecore_Idle_Exiter *idle_exiter); + + EAPI void ecore_main_loop_iterate(void); + + EAPI void ecore_main_loop_select_func_set(Ecore_Select_Function func); + EAPI void *ecore_main_loop_select_func_get(void); + + EAPI Eina_Bool ecore_main_loop_glib_integrate(void); + EAPI void ecore_main_loop_glib_always_integrate_disable(void); + + EAPI void ecore_main_loop_begin(void); + EAPI void ecore_main_loop_quit(void); + EAPI Ecore_Fd_Handler *ecore_main_fd_handler_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, + Ecore_Fd_Cb buf_func, const void *buf_data); + EAPI void ecore_main_fd_handler_prepare_callback_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Prep_Cb func, const void *data); + EAPI void *ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler); + EAPI int ecore_main_fd_handler_fd_get(Ecore_Fd_Handler *fd_handler); + EAPI Eina_Bool ecore_main_fd_handler_active_get(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags); + EAPI void ecore_main_fd_handler_active_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags); + + EAPI Ecore_Win32_Handler *ecore_main_win32_handler_add(void *h, Ecore_Fd_Win32_Cb func, const void *data); + EAPI void *ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler); + + EAPI Ecore_Pipe *ecore_pipe_add(Ecore_Pipe_Cb handler, const void *data); + EAPI void *ecore_pipe_del(Ecore_Pipe *p); + EAPI Eina_Bool ecore_pipe_write(Ecore_Pipe *p, const void *buffer, unsigned int nbytes); + EAPI void ecore_pipe_write_close(Ecore_Pipe *p); + EAPI void ecore_pipe_read_close(Ecore_Pipe *p); + + + + EAPI Ecore_Thread *ecore_thread_run(Ecore_Cb, + Ecore_Cb, + Ecore_Cb, + const void *data); + EAPI Ecore_Thread *ecore_thread_feedback_run(Ecore_Thread_Heavy_Cb, + Ecore_Thread_Notify_Cb, + Ecore_Cb, + Ecore_Cb, + const void *data, + Eina_Bool try_no_queue); + EAPI Eina_Bool ecore_thread_cancel(Ecore_Thread *thread); + EAPI Eina_Bool ecore_thread_check(Ecore_Thread *thread); + EAPI Eina_Bool ecore_thread_feedback(Ecore_Thread *thread, const void *msg_data); + EAPI int ecore_thread_active_get(void); + EAPI int ecore_thread_pending_get(void); + EAPI int ecore_thread_pending_feedback_get(void); + EAPI int ecore_thread_pending_total_get(void); + EAPI int ecore_thread_max_get(void); + EAPI void ecore_thread_max_set(int num); + EAPI void ecore_thread_max_reset(void); + EAPI int ecore_thread_available_get(void); + + EAPI Eina_Bool ecore_thread_local_data_add(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct); + EAPI void *ecore_thread_local_data_set(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb); + EAPI void *ecore_thread_local_data_find(Ecore_Thread *thread, const char *key); + EAPI Eina_Bool ecore_thread_local_data_del(Ecore_Thread *thread, const char *key); + + EAPI Eina_Bool ecore_thread_global_data_add(const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct); + EAPI void *ecore_thread_global_data_set(const char *key, void *value, Eina_Free_Cb cb); + EAPI void *ecore_thread_global_data_find(const char *key); + EAPI Eina_Bool ecore_thread_global_data_del(const char *key); + EAPI void *ecore_thread_global_data_wait(const char *key, double seconds); + + + + + EAPI double ecore_time_get(void); + EAPI double ecore_time_unix_get(void); + EAPI double ecore_loop_time_get(void); + + EAPI Ecore_Timer *ecore_timer_add(double in, Ecore_Task_Cb func, const void *data); + EAPI Ecore_Timer *ecore_timer_loop_add(double in, Ecore_Task_Cb func, const void *data); + EAPI void *ecore_timer_del(Ecore_Timer *timer); + EAPI void ecore_timer_interval_set(Ecore_Timer *timer, double in); + EAPI double ecore_timer_interval_get(Ecore_Timer *timer); + EAPI void ecore_timer_freeze(Ecore_Timer *timer); + EAPI void ecore_timer_thaw(Ecore_Timer *timer); + EAPI void ecore_timer_delay(Ecore_Timer *timer, double add); + EAPI double ecore_timer_pending_get(Ecore_Timer *timer); + + EAPI double ecore_timer_precision_get(void); + EAPI void ecore_timer_precision_set(double precision); + + EAPI Ecore_Animator *ecore_animator_add(Ecore_Task_Cb func, const void *data); + EAPI void *ecore_animator_del(Ecore_Animator *animator); + EAPI void ecore_animator_freeze(Ecore_Animator *animator); + EAPI void ecore_animator_thaw(Ecore_Animator *animator); + EAPI void ecore_animator_frametime_set(double frametime); + EAPI double ecore_animator_frametime_get(void); + + EAPI void ecore_poller_poll_interval_set(Ecore_Poller_Type type, double poll_time); + EAPI double ecore_poller_poll_interval_get(Ecore_Poller_Type type); + EAPI Eina_Bool ecore_poller_poller_interval_set(Ecore_Poller *poller, int interval); + EAPI int ecore_poller_poller_interval_get(Ecore_Poller *poller); + EAPI Ecore_Poller *ecore_poller_add(Ecore_Poller_Type type, int interval, Ecore_Task_Cb func, const void *data); + EAPI void *ecore_poller_del(Ecore_Poller *poller); + + EAPI Ecore_Job *ecore_job_add(Ecore_Cb func, const void *data); + EAPI void *ecore_job_del(Ecore_Job *job); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/tests/suite/ecore/src/lib/Ecore_Getopt.h b/tests/suite/ecore/src/lib/Ecore_Getopt.h new file mode 100644 index 0000000000..18a8459bfc --- /dev/null +++ b/tests/suite/ecore/src/lib/Ecore_Getopt.h @@ -0,0 +1,403 @@ +#ifndef _ECORE_GETOPT_H +#define _ECORE_GETOPT_H + +#include <stdio.h> +#include <Eina.h> + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_ECORE_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_ECORE_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif /* ! _WIN32 */ + +/** + * @file Ecore_Getopt.h + * @brief Contains powerful getopt replacement. + * + * This replacement handles both short (-X) or long options (--ABC) + * options, with various actions supported, like storing one value and + * already converting to required type, counting number of + * occurrences, setting true or false values, show help, license, + * copyright and even support user-defined callbacks. + * + * It is provided a set of C Pre Processor macros so definition is + * straightforward. + * + * Values will be stored elsewhere indicated by an array of pointers + * to values, it is given in separate to parser description so you can + * use multiple values with the same parser. + */ + + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + ECORE_GETOPT_ACTION_STORE, + ECORE_GETOPT_ACTION_STORE_CONST, + ECORE_GETOPT_ACTION_STORE_TRUE, + ECORE_GETOPT_ACTION_STORE_FALSE, + ECORE_GETOPT_ACTION_CHOICE, + ECORE_GETOPT_ACTION_APPEND, + ECORE_GETOPT_ACTION_COUNT, + ECORE_GETOPT_ACTION_CALLBACK, + ECORE_GETOPT_ACTION_HELP, + ECORE_GETOPT_ACTION_VERSION, + ECORE_GETOPT_ACTION_COPYRIGHT, + ECORE_GETOPT_ACTION_LICENSE + } Ecore_Getopt_Action; + + typedef enum { + ECORE_GETOPT_TYPE_STR, + ECORE_GETOPT_TYPE_BOOL, + ECORE_GETOPT_TYPE_SHORT, + ECORE_GETOPT_TYPE_INT, + ECORE_GETOPT_TYPE_LONG, + ECORE_GETOPT_TYPE_USHORT, + ECORE_GETOPT_TYPE_UINT, + ECORE_GETOPT_TYPE_ULONG, + ECORE_GETOPT_TYPE_DOUBLE + } Ecore_Getopt_Type; + + typedef enum { + ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO = 0, + ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES = 1, + ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL = 3 + } Ecore_Getopt_Desc_Arg_Requirement; + + typedef union _Ecore_Getopt_Value Ecore_Getopt_Value; + + typedef struct _Ecore_Getopt_Desc_Store Ecore_Getopt_Desc_Store; + typedef struct _Ecore_Getopt_Desc_Callback Ecore_Getopt_Desc_Callback; + typedef struct _Ecore_Getopt_Desc Ecore_Getopt_Desc; + typedef struct _Ecore_Getopt Ecore_Getopt; + + union _Ecore_Getopt_Value + { + char **strp; + unsigned char *boolp; + short *shortp; + int *intp; + long *longp; + unsigned short *ushortp; + unsigned int *uintp; + unsigned long *ulongp; + double *doublep; + Eina_List **listp; + void **ptrp; + }; + + struct _Ecore_Getopt_Desc_Store + { + Ecore_Getopt_Type type; /**< type of data being handled */ + Ecore_Getopt_Desc_Arg_Requirement arg_req; + union + { + const char *strv; + unsigned char boolv; + short shortv; + int intv; + long longv; + unsigned short ushortv; + unsigned int uintv; + unsigned long ulongv; + double doublev; + } def; + }; + + struct _Ecore_Getopt_Desc_Callback + { + unsigned char (*func)(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, const char *str, void *data, Ecore_Getopt_Value *storage); + const void *data; + Ecore_Getopt_Desc_Arg_Requirement arg_req; + const char *def; + }; + + struct _Ecore_Getopt_Desc + { + char shortname; /**< used with a single dash */ + const char *longname; /**< used with double dashes */ + const char *help; /**< used by --help/ecore_getopt_help() */ + const char *metavar; /**< used by ecore_getopt_help() with nargs > 0 */ + + Ecore_Getopt_Action action; /**< define how to handle it */ + union + { + const Ecore_Getopt_Desc_Store store; + const void *store_const; + const char *const *choices; /* NULL terminated. */ + const Ecore_Getopt_Type append_type; + const Ecore_Getopt_Desc_Callback callback; + const void *dummy; + } action_param; + }; + + struct _Ecore_Getopt + { + const char *prog; /**< to be used when ecore_app_args_get() fails */ + const char *usage; /**< usage example, %prog is replaced */ + const char *version; /**< if exists, --version will work */ + const char *copyright; /**< if exists, --copyright will work */ + const char *license; /**< if exists, --license will work */ + const char *description; /**< long description, possible multiline */ + unsigned char strict : 1; /**< fail on errors */ + const Ecore_Getopt_Desc descs[]; /* NULL terminated. */ + }; + +#define ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, type, arg_requirement, default_value) \ + {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_STORE, \ + {.store = {type, arg_requirement, default_value}}} + +#define ECORE_GETOPT_STORE(shortname, longname, help, type) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, NULL, type, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, {}) + +#define ECORE_GETOPT_STORE_STR(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_STR) +#define ECORE_GETOPT_STORE_BOOL(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_BOOL) +#define ECORE_GETOPT_STORE_SHORT(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_SHORT) +#define ECORE_GETOPT_STORE_INT(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_INT) +#define ECORE_GETOPT_STORE_LONG(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_LONG) +#define ECORE_GETOPT_STORE_USHORT(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_USHORT) +#define ECORE_GETOPT_STORE_UINT(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_UINT) +#define ECORE_GETOPT_STORE_ULONG(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_ULONG) +#define ECORE_GETOPT_STORE_DOUBLE(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_DOUBLE) + + +#define ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, type) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, type, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, {}) + +#define ECORE_GETOPT_STORE_METAVAR_STR(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_STR) +#define ECORE_GETOPT_STORE_METAVAR_BOOL(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_BOOL) +#define ECORE_GETOPT_STORE_METAVAR_SHORT(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_SHORT) +#define ECORE_GETOPT_STORE_METAVAR_INT(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_INT) +#define ECORE_GETOPT_STORE_METAVAR_LONG(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_LONG) +#define ECORE_GETOPT_STORE_METAVAR_USHORT(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_USHORT) +#define ECORE_GETOPT_STORE_METAVAR_UINT(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_UINT) +#define ECORE_GETOPT_STORE_METAVAR_ULONG(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_ULONG) +#define ECORE_GETOPT_STORE_METAVAR_DOUBLE(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_DOUBLE) + + +#define ECORE_GETOPT_STORE_DEF(shortname, longname, help, type, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, NULL, type, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL, \ + default_value) + +#define ECORE_GETOPT_STORE_DEF_STR(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_STR, \ + {.strv = default_value}) +#define ECORE_GETOPT_STORE_DEF_BOOL(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_BOOL, \ + {.boolv = default_value}) +#define ECORE_GETOPT_STORE_DEF_SHORT(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_SHORT, \ + {.shortv = default_value}) +#define ECORE_GETOPT_STORE_DEF_INT(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_INT, \ + {.intv = default_value}) +#define ECORE_GETOPT_STORE_DEF_LONG(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_LONG, \ + {.longv = default_value}) +#define ECORE_GETOPT_STORE_DEF_USHORT(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_USHORT, \ + {.ushortv = default_value}) +#define ECORE_GETOPT_STORE_DEF_UINT(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_UINT, \ + {.uintv = default_value}) +#define ECORE_GETOPT_STORE_DEF_ULONG(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_ULONG, \ + {.ulongv = default_value}) +#define ECORE_GETOPT_STORE_DEF_DOUBLE(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_DOUBLE, \ + {.doublev = default_value}) + +#define ECORE_GETOPT_STORE_FULL_STR(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_STR, \ + arg_requirement, \ + {.strv = default_value}) +#define ECORE_GETOPT_STORE_FULL_BOOL(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_BOOL, \ + arg_requirement, \ + {.boolv = default_value}) +#define ECORE_GETOPT_STORE_FULL_SHORT(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_SHORT, \ + arg_requirement, \ + {.shortv = default_value}) +#define ECORE_GETOPT_STORE_FULL_INT(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_INT, \ + arg_requirement, \ + {.intv = default_value}) +#define ECORE_GETOPT_STORE_FULL_LONG(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_LONG, \ + arg_requirement, \ + {.longv = default_value}) +#define ECORE_GETOPT_STORE_FULL_USHORT(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_USHORT, \ + arg_requirement, \ + {.ushortv = default_value}) +#define ECORE_GETOPT_STORE_FULL_UINT(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_UINT, \ + arg_requirement, \ + {.uintv = default_value}) +#define ECORE_GETOPT_STORE_FULL_ULONG(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_ULONG, \ + arg_requirement, \ + {.ulongv = default_value}) +#define ECORE_GETOPT_STORE_FULL_DOUBLE(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_DOUBLE, \ + arg_requirement, \ + {.doublev = default_value}) + +#define ECORE_GETOPT_STORE_CONST(shortname, longname, help, value) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_CONST, \ + {.store_const = value}} +#define ECORE_GETOPT_STORE_TRUE(shortname, longname, help) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_TRUE, \ + {.dummy = NULL}} +#define ECORE_GETOPT_STORE_FALSE(shortname, longname, help) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_FALSE, \ + {.dummy = NULL}} + +#define ECORE_GETOPT_CHOICE(shortname, longname, help, choices_array) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_CHOICE, \ + {.choices = choices_array}} +#define ECORE_GETOPT_CHOICE_METAVAR(shortname, longname, help, metavar, choices_array) \ + {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_CHOICE, \ + {.choices = choices_array}} + + +#define ECORE_GETOPT_APPEND(shortname, longname, help, sub_type) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_APPEND, \ + {.append_type = sub_type}} +#define ECORE_GETOPT_APPEND_METAVAR(shortname, longname, help, metavar, type) \ + {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_APPEND, \ + {.append_type = type}} + +#define ECORE_GETOPT_COUNT(shortname, longname, help) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_COUNT, \ + {.dummy = NULL}} + +#define ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, metavar, callback_func, callback_data, argument_requirement, default_value) \ + {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_CALLBACK, \ + {.callback = {callback_func, callback_data, \ + argument_requirement, default_value}}} +#define ECORE_GETOPT_CALLBACK_NOARGS(shortname, longname, help, callback_func, callback_data) \ + ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, NULL, \ + callback_func, callback_data, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO, \ + NULL) +#define ECORE_GETOPT_CALLBACK_ARGS(shortname, longname, help, metavar, callback_func, callback_data) \ + ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, metavar, \ + callback_func, callback_data, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, \ + NULL) + +#define ECORE_GETOPT_HELP(shortname, longname) \ + {shortname, longname, "show this message.", NULL, \ + ECORE_GETOPT_ACTION_HELP, \ + {.dummy = NULL}} + +#define ECORE_GETOPT_VERSION(shortname, longname) \ + {shortname, longname, "show program version.", NULL, \ + ECORE_GETOPT_ACTION_VERSION, \ + {.dummy = NULL}} + +#define ECORE_GETOPT_COPYRIGHT(shortname, longname) \ + {shortname, longname, "show copyright.", NULL, \ + ECORE_GETOPT_ACTION_COPYRIGHT, \ + {.dummy = NULL}} + +#define ECORE_GETOPT_LICENSE(shortname, longname) \ + {shortname, longname, "show license.", NULL, \ + ECORE_GETOPT_ACTION_LICENSE, \ + {.dummy = NULL}} + +#define ECORE_GETOPT_SENTINEL {0, NULL, NULL, NULL, 0, {.dummy = NULL}} + +#define ECORE_GETOPT_VALUE_STR(val) {.strp = &(val)} +#define ECORE_GETOPT_VALUE_BOOL(val) {.boolp = &(val)} +#define ECORE_GETOPT_VALUE_SHORT(val) {.shortp = &(val)} +#define ECORE_GETOPT_VALUE_INT(val) {.intp = &(val)} +#define ECORE_GETOPT_VALUE_LONG(val) {.longp = &(val)} +#define ECORE_GETOPT_VALUE_USHORT(val) {.ushortp = &(val)} +#define ECORE_GETOPT_VALUE_UINT(val) {.uintp = &(val)} +#define ECORE_GETOPT_VALUE_ULONG(val) {.ulongp = &(val)} +#define ECORE_GETOPT_VALUE_DOUBLE(val) {.doublep = &(val)} +#define ECORE_GETOPT_VALUE_PTR(val) {.ptrp = &(val)} +#define ECORE_GETOPT_VALUE_PTR_CAST(val) {.ptrp = (void **)&(val)} +#define ECORE_GETOPT_VALUE_LIST(val) {.listp = &(val)} +#define ECORE_GETOPT_VALUE_NONE {.ptrp = NULL} + + EAPI void ecore_getopt_help(FILE *fp, const Ecore_Getopt *info); + + EAPI unsigned char ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser); + EAPI int ecore_getopt_parse(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv); + + EAPI Eina_List *ecore_getopt_list_free(Eina_List *list); + + /* helper functions to be used with ECORE_GETOPT_CALLBACK_*() */ + EAPI unsigned char ecore_getopt_callback_geometry_parse(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, const char *str, void *data, Ecore_Getopt_Value *storage); + EAPI unsigned char ecore_getopt_callback_size_parse(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, const char *str, void *data, Ecore_Getopt_Value *storage); + + +#ifdef __cplusplus +} +#endif +#endif /* _ECORE_GETOPT_H */ diff --git a/tests/suite/ecore/src/lib/ecore.c b/tests/suite/ecore/src/lib/ecore.c new file mode 100644 index 0000000000..5b0789c029 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore.c @@ -0,0 +1,418 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#ifndef _MSC_VER +# include <unistd.h> +#endif + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#ifdef HAVE_LANGINFO_H +# include <langinfo.h> +#endif + +#ifdef HAVE_SYS_MMAN_H +# include <sys/mman.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif +#include <Eina.h> + +#include "Ecore.h" +#include "ecore_private.h" + +#if HAVE_MALLINFO +#include <malloc.h> + +static Ecore_Version _version = { VERS_MAJ, VERS_MIN, VERS_MIC, VERS_REV }; +EAPI Ecore_Version *ecore_version = &_version; + +#define KEEP_MAX(Global, Local) \ + if (Global < (Local)) \ + Global = Local; + +static Eina_Bool _ecore_memory_statistic(void *data); +static int _ecore_memory_max_total = 0; +static int _ecore_memory_max_free = 0; +static pid_t _ecore_memory_pid = 0; +#endif + +static const char *_ecore_magic_string_get(Ecore_Magic m); +static int _ecore_init_count = 0; +int _ecore_log_dom = -1; +int _ecore_fps_debug = 0; + +/** OpenBSD does not define CODESET + * FIXME ?? + */ + +#ifndef CODESET +# define CODESET "INVALID" +#endif + +/** + * Set up connections, signal handlers, sockets etc. + * @return 1 or greater on success, 0 otherwise + * + * This function sets up all singal handlers and the basic event loop. If it + * succeeds, 1 will be returned, otherwise 0 will be returned. + * + * @code + * #include <Ecore.h> + * + * int main(int argc, char **argv) + * { + * if (!ecore_init()) + * { + * printf("ERROR: Cannot init Ecore!\n"); + * return -1; + * } + * ecore_main_loop_begin(); + * ecore_shutdown(); + * } + * @endcode + */ +EAPI int +ecore_init(void) +{ + if (++_ecore_init_count != 1) + return _ecore_init_count; + +#ifdef HAVE_LOCALE_H + setlocale(LC_CTYPE, ""); +#endif + /* + if (strcmp(nl_langinfo(CODESET), "UTF-8")) + { + WRN("Not a utf8 locale!"); + } + */ +#ifdef HAVE_EVIL + if (!evil_init()) + return --_ecore_init_count; +#endif + if (!eina_init()) + goto shutdown_evil; + _ecore_log_dom = eina_log_domain_register("Ecore",ECORE_DEFAULT_LOG_COLOR); + if (_ecore_log_dom < 0) { + EINA_LOG_ERR("Ecore was unable to create a log domain."); + goto shutdown_log_dom; + } + if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1; + if (_ecore_fps_debug) _ecore_fps_debug_init(); + _ecore_main_loop_init(); + _ecore_signal_init(); + _ecore_exe_init(); + _ecore_thread_init(); + _ecore_glib_init(); + _ecore_job_init(); + _ecore_time_init(); + +#if HAVE_MALLINFO + if (getenv("ECORE_MEM_STAT")) + { + _ecore_memory_pid = getpid(); + ecore_animator_add(_ecore_memory_statistic, NULL); + } +#endif + +#if defined(GLIB_INTEGRATION_ALWAYS) + if (_ecore_glib_always_integrate) ecore_main_loop_glib_integrate(); +#endif + + return _ecore_init_count; + + shutdown_log_dom: + eina_shutdown(); + shutdown_evil: +#ifdef HAVE_EVIL + evil_shutdown(); +#endif + return --_ecore_init_count; +} + +/** + * Shut down connections, signal handlers sockets etc. + * + * This function shuts down all things set up in ecore_init() and cleans up all + * event queues, handlers, filters, timers, idlers, idle enterers/exiters + * etc. set up after ecore_init() was called. + * + * Do not call this function from any callback that may be called from the main + * loop, as the main loop will then fall over and not function properly. + */ +EAPI int +ecore_shutdown(void) +{ + if (--_ecore_init_count != 0) + return _ecore_init_count; + + if (_ecore_fps_debug) _ecore_fps_debug_shutdown(); + _ecore_poller_shutdown(); + _ecore_animator_shutdown(); + _ecore_glib_shutdown(); + _ecore_job_shutdown(); + _ecore_thread_shutdown(); + _ecore_exe_shutdown(); + _ecore_idle_enterer_shutdown(); + _ecore_idle_exiter_shutdown(); + _ecore_idler_shutdown(); + _ecore_timer_shutdown(); + _ecore_event_shutdown(); + _ecore_main_shutdown(); + _ecore_signal_shutdown(); + _ecore_main_loop_shutdown(); + +#if HAVE_MALLINFO + if (getenv("ECORE_MEM_STAT")) + { + _ecore_memory_statistic(NULL); + + ERR("[%i] Memory MAX total: %i, free: %i", + _ecore_memory_pid, + _ecore_memory_max_total, + _ecore_memory_max_free); + } +#endif + + eina_log_domain_unregister(_ecore_log_dom); + _ecore_log_dom = -1; + eina_shutdown(); +#ifdef HAVE_EVIL + evil_shutdown(); +#endif + + return _ecore_init_count; +} + +EAPI void +ecore_print_warning(const char *function, const char *sparam) +{ + WRN("***** Developer Warning ***** :\n" + "\tThis program is calling:\n\n" + "\t%s();\n\n" + "\tWith the parameter:\n\n" + "\t%s\n\n" + "\tbeing NULL. Please fix your program.", function, sparam); + if (getenv("ECORE_ERROR_ABORT")) abort(); +} + +EAPI void +_ecore_magic_fail(const void *d, Ecore_Magic m, Ecore_Magic req_m, const char *fname) +{ + ERR("\n" + "*** ECORE ERROR: Ecore Magic Check Failed!!!\n" + "*** IN FUNCTION: %s()", fname); + if (!d) + ERR(" Input handle pointer is NULL!"); + else if (m == ECORE_MAGIC_NONE) + ERR(" Input handle has already been freed!"); + else if (m != req_m) + ERR(" Input handle is wrong type\n" + " Expected: %08x - %s\n" + " Supplied: %08x - %s", + (unsigned int)req_m, _ecore_magic_string_get(req_m), + (unsigned int)m, _ecore_magic_string_get(m)); + ERR("*** NAUGHTY PROGRAMMER!!!\n" + "*** SPANK SPANK SPANK!!!\n" + "*** Now go fix your code. Tut tut tut!"); + if (getenv("ECORE_ERROR_ABORT")) abort(); +} + +static const char * +_ecore_magic_string_get(Ecore_Magic m) +{ + switch (m) + { + case ECORE_MAGIC_NONE: + return "None (Freed Object)"; + break; + case ECORE_MAGIC_EXE: + return "Ecore_Exe (Executable)"; + break; + case ECORE_MAGIC_TIMER: + return "Ecore_Timer (Timer)"; + break; + case ECORE_MAGIC_IDLER: + return "Ecore_Idler (Idler)"; + break; + case ECORE_MAGIC_IDLE_ENTERER: + return "Ecore_Idle_Enterer (Idler Enterer)"; + break; + case ECORE_MAGIC_IDLE_EXITER: + return "Ecore_Idle_Exiter (Idler Exiter)"; + break; + case ECORE_MAGIC_FD_HANDLER: + return "Ecore_Fd_Handler (Fd Handler)"; + break; + case ECORE_MAGIC_WIN32_HANDLER: + return "Ecore_Win32_Handler (Win32 Handler)"; + break; + case ECORE_MAGIC_EVENT_HANDLER: + return "Ecore_Event_Handler (Event Handler)"; + break; + case ECORE_MAGIC_EVENT: + return "Ecore_Event (Event)"; + break; + default: + return "<UNKNOWN>"; + }; +} + +/* fps debug calls - for debugging how much time your app actually spends */ +/* "running" (and the inverse being time spent running)... this does not */ +/* account for other apps and multitasking... */ + +static int _ecore_fps_debug_init_count = 0; +static int _ecore_fps_debug_fd = -1; +unsigned int *_ecore_fps_runtime_mmap = NULL; + +void +_ecore_fps_debug_init(void) +{ + char buf[4096]; + const char *tmp; + int pid; + + _ecore_fps_debug_init_count++; + if (_ecore_fps_debug_init_count > 1) return; + +#ifndef HAVE_EVIL + tmp = "/tmp"; +#else + tmp = (char *)evil_tmpdir_get (); +#endif /* HAVE_EVIL */ + pid = (int)getpid(); + snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid); + _ecore_fps_debug_fd = open(buf, O_CREAT | O_TRUNC | O_RDWR, 0644); + if (_ecore_fps_debug_fd < 0) + { + unlink(buf); + _ecore_fps_debug_fd = open(buf, O_CREAT | O_TRUNC | O_RDWR, 0644); + } + if (_ecore_fps_debug_fd >= 0) + { + unsigned int zero = 0; + char *buf = (char *)&zero; + ssize_t todo = sizeof(unsigned int); + + while (todo > 0) + { + ssize_t r = write(_ecore_fps_debug_fd, buf, todo); + if (r > 0) + { + todo -= r; + buf += r; + } + else if ((r < 0) && (errno == EINTR)) + continue; + else + { + ERR("could not write to file '%s' fd %d: %s", + tmp, _ecore_fps_debug_fd, strerror(errno)); + close(_ecore_fps_debug_fd); + _ecore_fps_debug_fd = -1; + return; + } + } + _ecore_fps_runtime_mmap = mmap(NULL, sizeof(unsigned int), + PROT_READ | PROT_WRITE, + MAP_SHARED, + _ecore_fps_debug_fd, 0); + if (_ecore_fps_runtime_mmap == MAP_FAILED) + _ecore_fps_runtime_mmap = NULL; + } +} + +void +_ecore_fps_debug_shutdown(void) +{ + _ecore_fps_debug_init_count--; + if (_ecore_fps_debug_init_count > 0) return; + if (_ecore_fps_debug_fd >= 0) + { + char buf[4096]; + const char *tmp; + int pid; + +#ifndef HAVE_EVIL + tmp = "/tmp"; +#else + tmp = (char *)evil_tmpdir_get (); +#endif /* HAVE_EVIL */ + pid = (int)getpid(); + snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid); + unlink(buf); + if (_ecore_fps_runtime_mmap) + { + munmap(_ecore_fps_runtime_mmap, sizeof(int)); + _ecore_fps_runtime_mmap = NULL; + } + close(_ecore_fps_debug_fd); + _ecore_fps_debug_fd = -1; + } +} + +void +_ecore_fps_debug_runtime_add(double t) +{ + if ((_ecore_fps_debug_fd >= 0) && + (_ecore_fps_runtime_mmap)) + { + unsigned int tm; + + tm = (unsigned int)(t * 1000000.0); + /* i know its not 100% theoretically guaranteed, but i'd say a write */ + /* of an int could be considered atomic for all practical purposes */ + /* oh and since this is cumulative, 1 second = 1,000,000 ticks, so */ + /* this can run for about 4294 seconds becore looping. if you are */ + /* doing performance testing in one run for over an hour... well */ + /* time to restart or handle a loop condition :) */ + *(_ecore_fps_runtime_mmap) += tm; + } +} + +#if HAVE_MALLINFO +static Eina_Bool +_ecore_memory_statistic(__UNUSED__ void *data) +{ + struct mallinfo mi; + static int uordblks = 0; + static int fordblks = 0; + Eina_Bool changed = EINA_FALSE; + + mi = mallinfo(); + +#define HAS_CHANGED(Global, Local) \ + if (Global != Local) \ + { \ + Global = Local; \ + changed = EINA_TRUE; \ + } + + HAS_CHANGED(uordblks, mi.uordblks); + HAS_CHANGED(fordblks, mi.fordblks); + + if (changed) + ERR("[%i] Memory total: %i, free: %i", + _ecore_memory_pid, + mi.uordblks, + mi.fordblks); + + KEEP_MAX(_ecore_memory_max_total, mi.uordblks); + KEEP_MAX(_ecore_memory_max_free, mi.fordblks); + + return ECORE_CALLBACK_RENEW; +} +#endif diff --git a/tests/suite/ecore/src/lib/ecore_anim.c b/tests/suite/ecore/src/lib/ecore_anim.c new file mode 100644 index 0000000000..921071e347 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_anim.c @@ -0,0 +1,233 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <math.h> + +#include "Ecore.h" +#include "ecore_private.h" + + +struct _Ecore_Animator +{ + EINA_INLIST; + ECORE_MAGIC; + + Ecore_Task_Cb func; + void *data; + + Eina_Bool delete_me : 1; + Eina_Bool suspended : 1; +}; + + +static Eina_Bool _ecore_animator(void *data); + +static Ecore_Timer *timer = NULL; +static int animators_delete_me = 0; +static Ecore_Animator *animators = NULL; +static double animators_frametime = 1.0 / 30.0; + +/** + * Add a animator to tick off at every animaton tick during main loop execution. + * @param func The function to call when it ticks off + * @param data The data to pass to the function + * @return A handle to the new animator + * @ingroup Ecore_Animator_Group + * + * This function adds a animator and returns its handle on success and NULL on + * failure. The function @p func will be called every N seconds where N is the + * frametime interval set by ecore_animator_frametime_set(). The function will + * be passed the @p data pointer as its parameter. + * + * When the animator @p func is called, it must return a value of either 1 or 0. + * If it returns 1 (or ECORE_CALLBACK_RENEW), it will be called again at the + * next tick, or if it returns 0 (or ECORE_CALLBACK_CANCEL) it will be deleted + * automatically making any references/handles for it invalid. + */ +EAPI Ecore_Animator * +ecore_animator_add(Ecore_Task_Cb func, const void *data) +{ + Ecore_Animator *animator; + + if (!func) return NULL; + animator = calloc(1, sizeof(Ecore_Animator)); + if (!animator) return NULL; + ECORE_MAGIC_SET(animator, ECORE_MAGIC_ANIMATOR); + animator->func = func; + animator->data = (void *)data; + animators = (Ecore_Animator *)eina_inlist_append(EINA_INLIST_GET(animators), EINA_INLIST_GET(animator)); + if (!timer) + { + double t_loop = ecore_loop_time_get(); + double sync_0 = 0.0; + double d = -fmod(t_loop - sync_0, animators_frametime); + + timer = ecore_timer_loop_add(animators_frametime, _ecore_animator, NULL); + ecore_timer_delay(timer, d); + } + return animator; +} + +/** + * Delete the specified animator from the animator list. + * @param animator The animator to delete + * @return The data pointer set for the animator + * @ingroup Ecore_Animator_Group + * + * Delete the specified @p aqnimator from the set of animators that are executed + * during main loop execution. This function returns the data parameter that + * was being passed to the callback on success, or NULL on failure. After this + * call returns the specified animator object @p animator is invalid and should not + * be used again. It will not get called again after deletion. + */ +EAPI void * +ecore_animator_del(Ecore_Animator *animator) +{ + if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR)) + { + ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR, + "ecore_animator_del"); + return NULL; + } + if (animator->delete_me) return animator->data; + animator->delete_me = EINA_TRUE; + animators_delete_me++; + return animator->data; +} + +/** + * Set the animator call interval in seconds. + * @param frametime The time in seconds in between animator ticks. + * + * This function sets the time interval (in seconds) between animator ticks. + */ +EAPI void +ecore_animator_frametime_set(double frametime) +{ + if (frametime < 0.0) frametime = 0.0; + if (animators_frametime == frametime) return; + animators_frametime = frametime; + if (timer) + { + ecore_timer_del(timer); + timer = NULL; + } + if (animators) + timer = ecore_timer_add(animators_frametime, _ecore_animator, NULL); +} + +/** + * Get the animator call interval in seconds. + * @return The time in second in between animator ticks. + * + * this function retrieves the time between animator ticks, in seconds. + */ +EAPI double +ecore_animator_frametime_get(void) +{ + return animators_frametime; +} + +/** + * Suspend the specified animator. + * @param animator The animator to delete + * @ingroup Ecore_Animator_Group + * + * The specified @p animator will be temporarly removed from the set of animators + * that are executed during main loop execution. + */ +EAPI void +ecore_animator_freeze(Ecore_Animator *animator) +{ + if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR)) + { + ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR, + "ecore_animator_del"); + return; + } + if (animator->delete_me) return; + animator->suspended = EINA_TRUE; +} + +/** + * Restore execution of the specified animator. + * @param animator The animator to delete + * @ingroup Ecore_Animator_Group + * + * The specified @p animator will be put back in the set of animators + * that are executed during main loop execution. + */ +EAPI void +ecore_animator_thaw(Ecore_Animator *animator) +{ + if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR)) + { + ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR, + "ecore_animator_del"); + return; + } + if (animator->delete_me) return; + animator->suspended = EINA_FALSE; +} + +void +_ecore_animator_shutdown(void) +{ + if (timer) + { + ecore_timer_del(timer); + timer = NULL; + } + while (animators) + { + Ecore_Animator *animator; + + animator = animators; + animators = (Ecore_Animator *) eina_inlist_remove(EINA_INLIST_GET(animators), EINA_INLIST_GET(animators)); + ECORE_MAGIC_SET(animator, ECORE_MAGIC_NONE); + free(animator); + } +} + +static Eina_Bool +_ecore_animator(void *data __UNUSED__) +{ + Ecore_Animator *animator; + + EINA_INLIST_FOREACH(animators, animator) + { + if (!animator->delete_me && !animator->suspended) + { + if (!animator->func(animator->data)) + { + animator->delete_me = EINA_TRUE; + animators_delete_me++; + } + } + } + if (animators_delete_me) + { + Ecore_Animator *l; + for(l = animators; l;) + { + animator = l; + l = (Ecore_Animator *) EINA_INLIST_GET(l)->next; + if (animator->delete_me) + { + animators = (Ecore_Animator *) eina_inlist_remove(EINA_INLIST_GET(animators), EINA_INLIST_GET(animator)); + ECORE_MAGIC_SET(animator, ECORE_MAGIC_NONE); + free(animator); + animators_delete_me--; + if (animators_delete_me == 0) break; + } + } + } + if (!animators) + { + timer = NULL; + return ECORE_CALLBACK_CANCEL; + } + return ECORE_CALLBACK_RENEW; +} diff --git a/tests/suite/ecore/src/lib/ecore_app.c b/tests/suite/ecore/src/lib/ecore_app.c new file mode 100644 index 0000000000..f9663a50bb --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_app.c @@ -0,0 +1,77 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +#ifndef _MSC_VER +# include <unistd.h> +#else +# include <process.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "Ecore.h" +#include "ecore_private.h" + +static int app_argc = 0; +static char **app_argv = NULL; + +/** + * Set up the programs command-line arguments. + * @param argc The same as passed as argc to the programs main() function + * @param argv The same as passed as argv to the programs main() function + * + * A call to this function will store the programs command-line arguments + * for later use by ecore_app_restart() or ecore_app_args_get(). + */ +EAPI void +ecore_app_args_set(int argc, const char **argv) +{ + if ((argc < 1) || + (!argv)) return; + app_argc = argc; + app_argv = (char **)argv; +} + +/** + * Return the programs stored command-line arguments. + * @param argc A pointer to the return value to hold argc + * @param argv A pointer to the return value to hold argv + * + * When called, this funciton returns the arguments for the program stored by + * ecore_app_args_set(). The integer pointed to by @p argc will be filled, if + * the pointer is not NULL, and the string array pointer @p argv will be filled + * also if the pointer is not NULL. The values they are filled with will be the + * same set by ecore_app_args_set(). + */ +EAPI void +ecore_app_args_get(int *argc, char ***argv) +{ + if (argc) *argc = app_argc; + if (argv) *argv = app_argv; +} + +/** + * Restart the program executable with the command-line arguments stored. + * + * This function will restart & re-execute this program in place of itself + * using the command-line arguments stored by ecore_app_args_set(). This is + * an easy way for a program to restart itself for cleanup purposes, + * configuration reasons or in the event of a crash. + */ +EAPI void +ecore_app_restart(void) +{ + char *args[4096]; + int i; + + if ((app_argc < 1) || (!app_argv)) return; + if (app_argc >= 4096) return; + for (i = 0; i < app_argc; i++) args[i] = app_argv[i]; + args[i] = NULL; + execvp(app_argv[0], args); +} diff --git a/tests/suite/ecore/src/lib/ecore_events.c b/tests/suite/ecore/src/lib/ecore_events.c new file mode 100644 index 0000000000..470838cb9a --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_events.c @@ -0,0 +1,659 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +#include "Ecore.h" +#include "ecore_private.h" + +static int inpurge = 0; + +struct _Ecore_Event_Handler +{ + EINA_INLIST; + ECORE_MAGIC; + int type; + Ecore_Event_Handler_Cb func; + void *data; + int references; + Eina_Bool delete_me : 1; +}; + +struct _Ecore_Event_Filter +{ + EINA_INLIST; + ECORE_MAGIC; + Ecore_Data_Cb func_start; + Ecore_Filter_Cb func_filter; + Ecore_End_Cb func_end; + void *loop_data; + void *data; + int references; + Eina_Bool delete_me : 1; +}; + +struct _Ecore_Event +{ + EINA_INLIST; + ECORE_MAGIC; + int type; + void *event; + Ecore_End_Cb func_free; + void *data; + int references; + Eina_Bool delete_me : 1; +}; + + +static int events_num = 0; +static Ecore_Event *events = NULL; +static Ecore_Event *event_current = NULL; +static Ecore_Event *purge_events = NULL; + +static Ecore_Event_Handler **event_handlers = NULL; +static Ecore_Event_Handler *event_handler_current = NULL; +static int event_handlers_num = 0; +static int event_handlers_alloc_num = 0; +static Eina_List *event_handlers_delete_list = NULL; + +static Ecore_Event_Filter *event_filters = NULL; +static Ecore_Event_Filter *event_filter_current = NULL; +static Ecore_Event *event_filter_event_current = NULL; +static int event_filters_delete_me = 0; +static int event_id_max = ECORE_EVENT_COUNT; +static int ecore_raw_event_type = ECORE_EVENT_NONE; +static void *ecore_raw_event_event = NULL; + + +static void _ecore_event_purge_deleted(void); +static void *_ecore_event_del(Ecore_Event *event); + + +/** + * Add an event handler. + * @param type The type of the event this handler will get called for + * @param func The function to call when the event is found in the queue + * @param data A data pointer to pass to the called function @p func + * @return A new Event handler, or NULL on failure + * + * Add an event handler to the list of handlers. This will, on success, return + * a handle to the event handler object that was created, that can be used + * later to remove the handler using ecore_event_handler_del(). The @p type + * parameter is the integer of the event type that will trigger this callback + * to be called. The callback @p func is called when this event is processed + * and will be passed the event type, a pointer to the private event + * structure that is specific to that event type, and a data pointer that is + * provided in this call as the @p data parameter. + * + * When the callback @p func is called, it must return 1 or 0. If it returns + * 1 (or ECORE_CALLBACK_RENEW), It will keep being called as per normal, for + * each handler set up for that event type. If it returns 0 (or + * ECORE_CALLBACK_CANCEL), it will cease processing handlers for that particular + * event, so all handler set to handle that event type that have not already + * been called, will not be. + */ +EAPI Ecore_Event_Handler * +ecore_event_handler_add(int type, Ecore_Event_Handler_Cb func, const void *data) +{ + Ecore_Event_Handler *eh; + + if (!func) return NULL; + if ((type <= ECORE_EVENT_NONE) || (type >= event_id_max)) return NULL; + eh = calloc(1, sizeof(Ecore_Event_Handler)); + if (!eh) return NULL; + ECORE_MAGIC_SET(eh, ECORE_MAGIC_EVENT_HANDLER); + eh->type = type; + eh->func = func; + eh->data = (void *)data; + if (type >= (event_handlers_num - 1)) + { + int p_alloc_num; + + p_alloc_num = event_handlers_alloc_num; + event_handlers_num = type + 1; + if (event_handlers_num > event_handlers_alloc_num) + { + Ecore_Event_Handler **new_handlers; + int i; + + event_handlers_alloc_num = ((event_handlers_num + 16) / 16) * 16; + new_handlers = realloc(event_handlers, event_handlers_alloc_num * sizeof(Ecore_Event_Handler *)); + if (!new_handlers) + { + free(eh); + return NULL; + } + event_handlers = new_handlers; + for (i = p_alloc_num; i < event_handlers_alloc_num; i++) + event_handlers[i] = NULL; + } + } + event_handlers[type] = (Ecore_Event_Handler *) eina_inlist_append(EINA_INLIST_GET(event_handlers[type]), EINA_INLIST_GET(eh)); + return eh; +} + +/** + * Delete an event handler. + * @param event_handler Event handler handle to delete + * @return Data passed to handler + * + * Delete a specified event handler from the handler list. On success this will + * delete the event handler and return the pointer passed as @p data when the + * handler was added by ecore_event_handler_add(). On failure NULL will be + * returned. Once a handler is deleted it will no longer be called. + */ +EAPI void * +ecore_event_handler_del(Ecore_Event_Handler *event_handler) +{ + if (!ECORE_MAGIC_CHECK(event_handler, ECORE_MAGIC_EVENT_HANDLER)) + { + ECORE_MAGIC_FAIL(event_handler, ECORE_MAGIC_EVENT_HANDLER, + "ecore_event_handler_del"); + return NULL; + } + EINA_SAFETY_ON_TRUE_RETURN_VAL(event_handler->delete_me, NULL); + event_handler->delete_me = 1; + event_handlers_delete_list = eina_list_append(event_handlers_delete_list, event_handler); + return event_handler->data; +} + +static void +_ecore_event_generic_free (void *data __UNUSED__, void *event) +{ + free (event); +} + +/** + * Add an event to the event queue. + * @param type The event type to add to the end of the event queue + * @param ev The private data structure for this event type + * @param func_free The function to be called to free this private structure + * @param data The data pointer to be passed to the free function + * @return A Handle for that event + * + * On success this function returns a handle to an event on the event queue, or + * NULL if it fails. If it succeeds, an event of type @p type will be added + * to the queue for processing by event handlers added by + * ecore_event_handler_add(). The @p ev parameter will be a pointer to the event + * private data that is specific to that event type. When the event is no + * longer needed, @p func_free will be called and passed the private structure + * pointer for cleaning up. If @p func_free is NULL, free() will be called + * with the private structure pointer. + * func_free is passed @p data as its data parameter. + */ +EAPI Ecore_Event * +ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data) +{ +/* if (!ev) return NULL;*/ + if (type <= ECORE_EVENT_NONE) return NULL; + if (type >= event_id_max) return NULL; + if ((ev) && (!func_free)) func_free = _ecore_event_generic_free; + return _ecore_event_add(type, ev, func_free, data); +} + +/** + * Delete an event from the queue. + * @param event The event handle to delete + * @return The data pointer originally set for the event free function + * + * This deletes the event @p event from the event queue, and returns the + * @p data parameer originally set when adding it with ecore_event_add(). This + * does not immediately call the free function, and it may be called later on + * cleanup, and so if the free function depends on the data pointer to work, + * you should defer cleaning of this till the free function is called later. + */ +EAPI void * +ecore_event_del(Ecore_Event *event) +{ + if (!ECORE_MAGIC_CHECK(event, ECORE_MAGIC_EVENT)) + { + ECORE_MAGIC_FAIL(event, ECORE_MAGIC_EVENT, "ecore_event_del"); + return NULL; + } + EINA_SAFETY_ON_TRUE_RETURN_VAL(event->delete_me, NULL); + event->delete_me = 1; + return event->data; +} + +/** + * Allocate a new event type id sensibly and return the new id. + * @return A new event type id. + * + * This function allocates a new event type id and returns it. Once an event + * type has been allocated it can never be de-allocated during the life of + * the program. There is no guarantee of the contents of this event ID, or how + * it is calculated, except that the ID will be unique to the current instance + * of the process. + */ +EAPI int +ecore_event_type_new(void) +{ + event_id_max++; + return event_id_max - 1; +} + +/** + * Add a filter the current event queue. + * @param func_start Function to call just before filtering and return data + * @param func_filter Function to call on each event + * @param func_end Function to call after the queu has been filtered + * @param data Data to pass to the filter functions + * @return A filter handle + * + * This adds a filter to call callbacks to loop through the event queue and + * filter events out of the queue. On failure NULL is returned. On success a + * Filter handle is returned. Filters are called on the queue just before + * Event handler processing to try and remove redundant events. Just as + * processing starts @p func_start is called and passed the @p data pointer. + * This function returns a pointer that is used as loop_data that is now passed to + * @p func_filter as loop_data. @p func_filter is also passed @p data and the + * event type and private event structure. If this callback returns 0, the + * event is removed from the queue. If it returns 1, the event is kept. When + * processing is finished @p func_end is called and is passed the loop_data + * and @p data pointer to clean up. + */ +EAPI Ecore_Event_Filter * +ecore_event_filter_add(Ecore_Data_Cb func_start, Ecore_Filter_Cb func_filter, Ecore_End_Cb func_end, const void *data) +{ + Ecore_Event_Filter *ef; + + if (!func_filter) return NULL; + ef = calloc(1, sizeof(Ecore_Event_Filter)); + if (!ef) return NULL; + ECORE_MAGIC_SET(ef, ECORE_MAGIC_EVENT_FILTER); + ef->func_start = func_start; + ef->func_filter = func_filter; + ef->func_end = func_end; + ef->data = (void *)data; + event_filters = (Ecore_Event_Filter *) eina_inlist_append(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(ef)); + return ef; +} + +/** + * Delete an event filter. + * @param ef The event filter handle + * @return The data set for the filter + * + * Delete a filter that has been added by its @p ef handle. On success this + * will return the data pointer set when this filter was added. On failure + * NULL is returned. + */ +EAPI void * +ecore_event_filter_del(Ecore_Event_Filter *ef) +{ + if (!ECORE_MAGIC_CHECK(ef, ECORE_MAGIC_EVENT_FILTER)) + { + ECORE_MAGIC_FAIL(ef, ECORE_MAGIC_EVENT_FILTER, "ecore_event_filter_del"); + return NULL; + } + EINA_SAFETY_ON_TRUE_RETURN_VAL(ef->delete_me, NULL); + ef->delete_me = 1; + event_filters_delete_me = 1; + return ef->data; +} + +/** + * Return the current event type being handled. + * @return The current event type being handled if inside a handler callback + * + * If the program is currently inside an Ecore event handler callback this + * will return the type of the current event being processed. If Ecore is + * not inside an event handler, ECORE_EVENT_NONE is returned. + * + * This is useful when certain Ecore modules such as Ecore_Evas "swallow" + * events and not all the original information is passed on. In special cases + * this extra information may be useful or needed and using this call can let + * the program know if the event type being handled is one it wants to get more + * information about. + */ +EAPI int +ecore_event_current_type_get(void) +{ + return ecore_raw_event_type; +} + +/** + * Return the current event type pointer handled. + * @return The current event pointer being handled if inside a handler callback + * + * If the program is currently inside an Ecore event handler callback this + * will return the pointer of the current event being processed. If Ecore is + * not inside an event handler, NULL will be returned. + * + * This is useful when certain Ecore modules such as Ecore_Evas "swallow" + * events and not all the original information is passed on. In special cases + * this extra information may be useful or needed and using this call can let + * the program access the event data if the type of the event is handled by + * the program. + */ +EAPI void * +ecore_event_current_event_get(void) +{ + return ecore_raw_event_event; +} + +void +_ecore_event_shutdown(void) +{ + int i; + Ecore_Event_Handler *eh; + Ecore_Event_Filter *ef; + + while (events) _ecore_event_del(events); + event_current = NULL; + for (i = 0; i < event_handlers_num; i++) + { + while ((eh = event_handlers[i])) + { + event_handlers[i] = (Ecore_Event_Handler *) eina_inlist_remove(EINA_INLIST_GET(event_handlers[i]), EINA_INLIST_GET(event_handlers[i])); + ECORE_MAGIC_SET(eh, ECORE_MAGIC_NONE); + if (!eh->delete_me) free(eh); + } + } + EINA_LIST_FREE(event_handlers_delete_list, eh) + free(eh); + if (event_handlers) free(event_handlers); + event_handlers = NULL; + event_handlers_num = 0; + event_handlers_alloc_num = 0; + while ((ef = event_filters)) + { + event_filters = (Ecore_Event_Filter *) eina_inlist_remove(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(event_filters)); + ECORE_MAGIC_SET(ef, ECORE_MAGIC_NONE); + free(ef); + } + event_filters_delete_me = 0; + event_filter_current = NULL; + event_filter_event_current = NULL; +} + +int +_ecore_event_exist(void) +{ + Ecore_Event *e; + EINA_INLIST_FOREACH(events, e) + if (!e->delete_me) return 1; + return 0; +} + +Ecore_Event * +_ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data) +{ + Ecore_Event *e; + + e = calloc(1, sizeof(Ecore_Event)); + if (!e) return NULL; + ECORE_MAGIC_SET(e, ECORE_MAGIC_EVENT); + e->type = type; + e->event = ev; + e->func_free = func_free; + e->data = data; + if (inpurge > 0) + { + purge_events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(purge_events), EINA_INLIST_GET(e)); + events_num++; + } + else + { + events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(events), EINA_INLIST_GET(e)); + events_num++; + } + return e; +} + +void * +_ecore_event_del(Ecore_Event *event) +{ + void *data; + + data = event->data; + if (event->func_free) event->func_free(event->data, event->event); + events = (Ecore_Event *) eina_inlist_remove(EINA_INLIST_GET(events), EINA_INLIST_GET(event)); + ECORE_MAGIC_SET(event, ECORE_MAGIC_NONE); + free(event); + events_num--; + return data; +} + +static void +_ecore_event_purge_deleted(void) +{ + Ecore_Event *itr = events; + + inpurge++; + while (itr) + { + Ecore_Event *next = (Ecore_Event *)EINA_INLIST_GET(itr)->next; + if ((!itr->references) && (itr->delete_me)) + _ecore_event_del(itr); + itr = next; + } + inpurge--; + while (purge_events) + { + Ecore_Event *e = purge_events; + purge_events = (Ecore_Event *)eina_inlist_remove(EINA_INLIST_GET(purge_events), EINA_INLIST_GET(purge_events)); + events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(events), EINA_INLIST_GET(e)); + } +} + +static inline void +_ecore_event_filters_apply() +{ + + if (!event_filter_current) + { + /* regular main loop, start from head */ + event_filter_current = event_filters; + } + else + { + /* recursive main loop, continue from where we were */ + event_filter_current = (Ecore_Event_Filter *)EINA_INLIST_GET(event_filter_current)->next; + } + + while (event_filter_current) + { + Ecore_Event_Filter *ef = event_filter_current; + + if (!ef->delete_me) + { + ef->references++; + + if (ef->func_start) + ef->loop_data = ef->func_start(ef->data); + + if (!event_filter_event_current) + { + /* regular main loop, start from head */ + event_filter_event_current = events; + } + else + { + /* recursive main loop, continue from where we were */ + event_filter_event_current = (Ecore_Event *)EINA_INLIST_GET(event_filter_event_current)->next; + } + + while (event_filter_event_current) + { + Ecore_Event *e = event_filter_event_current; + + if (!ef->func_filter(ef->data, ef->loop_data, + e->type, e->event)) + { + ecore_event_del(e); + } + + if (event_filter_event_current) /* may have changed in recursive main loops */ + event_filter_event_current = (Ecore_Event *)EINA_INLIST_GET(event_filter_event_current)->next; + } + if (ef->func_end) + ef->func_end(ef->data, ef->loop_data); + + ef->references--; + } + + if (event_filter_current) /* may have changed in recursive main loops */ + event_filter_current = (Ecore_Event_Filter *)EINA_INLIST_GET(event_filter_current)->next; + } + if (event_filters_delete_me) + { + int deleted_in_use = 0; + Ecore_Event_Filter *l; + for (l = event_filters; l;) + { + Ecore_Event_Filter *ef = l; + l = (Ecore_Event_Filter *) EINA_INLIST_GET(l)->next; + if (ef->delete_me) + { + if (ef->references) + { + deleted_in_use++; + continue; + } + + event_filters = (Ecore_Event_Filter *) eina_inlist_remove(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(ef)); + ECORE_MAGIC_SET(ef, ECORE_MAGIC_NONE); + free(ef); + } + } + if (!deleted_in_use) + event_filters_delete_me = 0; + } +} +void +_ecore_event_call(void) +{ + Eina_List *l, *l_next; + Ecore_Event_Handler *eh; + + _ecore_event_filters_apply(); + + if (!event_current) + { + /* regular main loop, start from head */ + event_current = events; + event_handler_current = NULL; + } + + while (event_current) + { + Ecore_Event *e = event_current; + int handle_count = 0; + + if (e->delete_me) + { + event_current = (Ecore_Event *)EINA_INLIST_GET(event_current)->next; + continue; + } + + ecore_raw_event_type = e->type; + ecore_raw_event_event = e->event; + e->references++; + if ((e->type >= 0) && (e->type < event_handlers_num)) + { + if (!event_handler_current) + { + /* regular main loop, start from head */ + event_handler_current = event_handlers[e->type]; + } + else + { + /* recursive main loop, continue from where we were */ + event_handler_current = (Ecore_Event_Handler *)EINA_INLIST_GET(event_handler_current)->next; + } + + while ((event_handler_current) && (!e->delete_me)) + { + Ecore_Event_Handler *eh = event_handler_current; + if (!eh->delete_me) + { + Eina_Bool ret; + + handle_count++; + + eh->references++; + ret = eh->func(eh->data, e->type, e->event); + eh->references--; + + if (!ret) + { + event_handler_current = NULL; + break; /* 0 == "call no further handlers" */ + } + } + + if (event_handler_current) /* may have changed in recursive main loops */ + event_handler_current = (Ecore_Event_Handler *)EINA_INLIST_GET(event_handler_current)->next; + } + } + /* if no handlers were set for EXIT signal - then default is */ + /* to quit the main loop */ + if ((e->type == ECORE_EVENT_SIGNAL_EXIT) && (handle_count == 0)) + ecore_main_loop_quit(); + e->references--; + e->delete_me = 1; + + if (event_current) /* may have changed in recursive main loops */ + event_current = (Ecore_Event *)EINA_INLIST_GET(event_current)->next; + } + + ecore_raw_event_type = ECORE_EVENT_NONE; + ecore_raw_event_event = NULL; + + _ecore_event_purge_deleted(); + + EINA_LIST_FOREACH_SAFE(event_handlers_delete_list, l, l_next, eh) + { + if (eh->references) continue; + + event_handlers_delete_list = eina_list_remove_list(event_handlers_delete_list, l); + + event_handlers[eh->type] = (Ecore_Event_Handler *) eina_inlist_remove(EINA_INLIST_GET(event_handlers[eh->type]), EINA_INLIST_GET(eh)); + ECORE_MAGIC_SET(eh, ECORE_MAGIC_NONE); + free(eh); + } +} + +EAPI void * +_ecore_event_signal_user_new(void) +{ + Ecore_Event_Signal_User *e; + + e = calloc(1, sizeof(Ecore_Event_Signal_User)); + return e; +} + +void * +_ecore_event_signal_hup_new(void) +{ + Ecore_Event_Signal_Hup *e; + + e = calloc(1, sizeof(Ecore_Event_Signal_Hup)); + return e; +} + +void * +_ecore_event_signal_exit_new(void) +{ + Ecore_Event_Signal_Exit *e; + + e = calloc(1, sizeof(Ecore_Event_Signal_Exit)); + return e; +} + +void * +_ecore_event_signal_power_new(void) +{ + Ecore_Event_Signal_Power *e; + + e = calloc(1, sizeof(Ecore_Event_Signal_Power)); + return e; +} + +void * +_ecore_event_signal_realtime_new(void) +{ + return calloc(1, sizeof(Ecore_Event_Signal_Realtime)); +} diff --git a/tests/suite/ecore/src/lib/ecore_exe.c b/tests/suite/ecore/src/lib/ecore_exe.c new file mode 100644 index 0000000000..251157c621 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_exe.c @@ -0,0 +1,1846 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__NetBSD__) +# include <sys/time.h> +# include <sys/resource.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> + +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif + +#include "Ecore.h" +#include "ecore_private.h" + + + /* FIXME: Getting respawn to work + * + * There is no way that we can do anything about the internal state info of + * an external exe. The same can be said about the state of user code. User + * code in this context means the code that is using ecore_exe to manage exe's + * for it. + * + * Document that the exe must be respawnable, in other words, there is no + * state that it cannot regenerate by just killing it and starting it again. + * This includes state that the user code knows about, as the respawn is + * transparent to that code. On the other hand, maybe a respawn event might + * be useful, or maybe resend the currently non existent add event. For + * consistancy with ecore_con, an add event is good anyway. + * + * The Ecore_exe structure is reused for respawning, so that the (opaque) + * pointer held by the user remains valid. This means that the Ecore_Exe + * init and del functions may need to be split into two parts each to avoid + * duplicating code - common code part, and the rest. This implies that + * the unchanging members mentioned next should NEVER change. + * + * These structure members don't need to change - + * __list_data - we stay on the list + * ECORE_MAGIC - this is a constant + * data - passed in originally + * cmd - passed in originally + * flags - passed in originally + * + * These structure members need to change - + * tag - state that must be regenerated, zap it + * pid - it will be different + * child_fd_write - it will be different + * child_fd_read - it will be different + * child_fd_error - it will be different + * write_fd_handler - we cannot change the fd used by a handler, this changes coz the fd changes. + * read_fd_handler - we cannot change the fd used by a handler, this changes coz the fd changes. + * error_fd_handler - we cannot change the fd used by a handler, this changes coz the fd changes. + * + * Hmm, the read, write, and error buffers could be tricky. + * They are not atomic, and could be in a semi complete state. + * They fall into the "state must be regenerated" mentioned above. + * A respawn/add event should take care of it. + * + * These structure members need to change - + * write_data_buf - state that must be regenerated, zap it + * write_data_size - state that must be regenerated, zap it + * write_data_offset - state that must be regenerated, zap it + * read_data_buf - state that must be regenerated, zap it + * read_data_size - state that must be regenerated, zap it + * error_data_buf - state that must be regenerated, zap it + * error_data_size - state that must be regenerated, zap it + * close_write - state that must be regenerated, zap it + * + * There is the problem that an exe that fell over and needs respawning + * might keep falling over, keep needing to be respawned, and tie up system + * resources with the constant respawning. An exponentially increasing + * timeout (with maximum timeout) between respawns should take care of that. + * Although this is not a "contention for a resource" problem, the exe falling + * over may be, so a random element added to the timeout may help, and won't + * hurt. The user code may need to be informed that a timeout is in progress. + */ + +struct _Ecore_Exe +{ + EINA_INLIST; + ECORE_MAGIC; + pid_t pid; + void *data; + char *tag, *cmd; + Ecore_Exe_Flags flags; + Ecore_Fd_Handler *write_fd_handler; /* the fd_handler to handle write to child - if this was used, or NULL if not */ + Ecore_Fd_Handler *read_fd_handler; /* the fd_handler to handle read from child - if this was used, or NULL if not */ + Ecore_Fd_Handler *error_fd_handler; /* the fd_handler to handle errors from child - if this was used, or NULL if not */ + void *write_data_buf; /* a data buffer for data to write to the child - + * realloced as needed for more data and flushed when the fd handler says writes are possible + */ + int write_data_size; /* the size in bytes of the data buffer */ + int write_data_offset; /* the offset in bytes in the data buffer */ + void *read_data_buf; /* data read from the child awating delivery to an event */ + int read_data_size; /* data read from child in bytes */ + void *error_data_buf; /* errors read from the child awating delivery to an event */ + int error_data_size; /* errors read from child in bytes */ + int child_fd_write; /* fd to write TO to send data to the child */ + int child_fd_read; /* fd to read FROM when child has sent us (the parent) data */ + int child_fd_error; /* fd to read FROM when child has sent us (the parent) errors */ + int child_fd_write_x; /* fd to write TO to send data to the child */ + int child_fd_read_x; /* fd to read FROM when child has sent us (the parent) data */ + int child_fd_error_x; /* fd to read FROM when child has sent us (the parent) errors */ + Eina_Bool close_stdin : 1; + + int start_bytes, end_bytes, start_lines, end_lines; /* Number of bytes/lines to auto pipe at start/end of stdout/stderr. */ + + Ecore_Timer *doomsday_clock; /* The Timer of Death. Muahahahaha. */ + void *doomsday_clock_dead; /* data for the doomsday clock */ + + Ecore_Exe_Cb pre_free_cb; +}; + + +/* TODO: Something to let people build a command line and does auto escaping - + * + * ecore_exe_snprintf() + * + * OR + * + * cmd = ecore_exe_comand_parameter_append(cmd, "firefox"); + * cmd = ecore_exe_comand_parameter_append(cmd, "http://www.foo.com/bar.html?baz=yes"); + * each parameter appended is one argument, and it gets escaped, quoted, and + * appended with a preceding space. The first is the command off course. + */ + +struct _ecore_exe_dead_exe +{ + pid_t pid; + char *cmd; +}; + +static inline void _ecore_exe_exec_it(const char *exe_cmd, Ecore_Exe_Flags flags); +static Eina_Bool _ecore_exe_data_generic_handler(void *data, Ecore_Fd_Handler *fd_handler, Ecore_Exe_Flags flags); +static Eina_Bool _ecore_exe_data_error_handler(void *data, Ecore_Fd_Handler *fd_handler); +static Eina_Bool _ecore_exe_data_read_handler(void *data, Ecore_Fd_Handler *fd_handler); +static Eina_Bool _ecore_exe_data_write_handler(void *data, Ecore_Fd_Handler *fd_handler); +static void _ecore_exe_flush(Ecore_Exe * exe); +static void _ecore_exe_event_exe_data_free(void *data __UNUSED__, void *ev); +static Ecore_Exe *_ecore_exe_is_it_alive(pid_t pid); +static Eina_Bool _ecore_exe_make_sure_its_dead(void *data); +static Eina_Bool _ecore_exe_make_sure_its_really_dead(void *data); +static Ecore_Exe_Event_Add *_ecore_exe_event_add_new(void); +static void _ecore_exe_event_add_free(void *data, void *ev); +static void _ecore_exe_dead_attach(Ecore_Exe *exe); + +EAPI int ECORE_EXE_EVENT_ADD = 0; +EAPI int ECORE_EXE_EVENT_DEL = 0; +EAPI int ECORE_EXE_EVENT_DATA = 0; +EAPI int ECORE_EXE_EVENT_ERROR = 0; + +static Ecore_Exe *exes = NULL; +static const char *shell = NULL; + +/* FIXME: This errno checking stuff should be put elsewhere for everybody to use. + * For now it lives here though, just to make testing easier. + */ +static int _ecore_exe_check_errno(int result, const char *file, int line); + +#define E_IF_NO_ERRNO(result, foo, ok) \ + while (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__)) == -1) sleep(1); \ + if (ok) + +#define E_NO_ERRNO(result, foo, ok) \ + while (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__)) == -1) sleep(1) + +#define E_IF_NO_ERRNO_NOLOOP(result, foo, ok) \ + if (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__))) + +static int +_ecore_exe_check_errno(int result, const char *file, int line) +{ + int saved_errno = errno; + + if (result == -1) + { + perror("*** errno reports "); +/* What is currently supported - + * + * pipe + * EFAULT Argument is not valid. + * EMFILE Too many file descriptors used by process. + * ENFILE Too many open files by system. + * read + * EAGAIN No data now, try again. + * EBADF This is not an fd that can be read. + * EFAULT This is not a valid buffer. + * EINTR Interupted by signal, try again. + * EINVAL This is not an fd that can be read. + * EIO I/O error. + * EISDIR This is a directory, and cannot be read. + * others Depending on what sort of thing we are reading from. + * close + * EBADF This is not an fd that can be closed. + * EINTR Interupted by signal, try again. + * EIO I/O error. + * dup2 + * EBADF This is not an fd that can be dup2'ed. + * EBUSY Race condition between open() and dup() + * EINTR Interupted by signal, try again. + * EMFILE Too many file descriptors used by process. + * fcntl + * EACCES, EAGAIN Locked or mapped by something else, try again later. + * EBADF This is not an fd that can be fcntl'ed. + * EDEADLK This will cause a deadlock. + * EFAULT This is not a valid lock. + * EINTR Interupted by signal, try again. + * EINVAL This is not a valid arg. + * EMFILE Too many file descriptors used by process. + * ENOLCK Problem getting a lock. + * EPERM Not allowed to do that. + * fsync + * EBADF This is not an fd that is open for writing. + * EINVAL, EROFS This is not an fd that can be fsynced. + * EIO I/O error. + * + * How to use it - + * int ok = 0; + * int result; + * + * E_IF_NO_ERRNO(result, foo(bar), ok) + * { + * E_IF_NO_ERRNO_NOLOOP(result, foo(bar), ok) + * { + * } + * } + * + * if (!ok) + * { + * // Something failed, cleanup. + * } + */ + switch (saved_errno) + { + case EACCES: + case EAGAIN: + case EINTR: + { /* Not now, try later. */ + ERR("*** Must try again in %s @%u.", file, line); + result = -1; + break; + } + case EMFILE: + case ENFILE: + case ENOLCK: + { /* Low on resources. */ + ERR("*** Low on resources in %s @%u.", file, + line); + result = 0; + break; + } + case EIO: + { /* I/O error. */ + ERR("*** I/O error in %s @%u.", file, line); + result = 0; + break; + } + case EFAULT: + case EBADF: + case EINVAL: + case EROFS: + case EISDIR: + case EDEADLK: + case EPERM: + case EBUSY: + { /* Programmer fucked up. */ + ERR("*** NAUGHTY PROGRAMMER!!!\n" + "*** SPANK SPANK SPANK!!!\n" + "*** Now go fix your code in %s @%u. Tut tut tut!", + file, line); + result = 0; + break; + } + default: + { /* Unsupported errno code, please add this one. */ + ERR("*** NAUGHTY PROGRAMMER!!!\n" + "*** SPANK SPANK SPANK!!!\n" + "*** Unsupported errno code %d, please add this one.\n" + "*** Now go fix your code in %s @%u, from %s @%u. Tut tut tut!", + saved_errno, __FILE__, __LINE__, file, line); + result = 0; + break; + } + } + } + else /* Everything is fine. */ + result = 1; + + errno = saved_errno; + return result; +} + +/** + * @defgroup Ecore_Exe_Basic_Group Process Spawning Functions + * + * Functions that deal with spawned processes. + */ + +static int run_pri = ECORE_EXE_PRIORITY_INHERIT; + +/** + * Sets the priority at which to launch processes + * + * This sets the priority of processes run by ecore_exe_run() and + * ecore_exe_pipe_run(). + * @li On Windows, the child process is created by default with the + * #ECORE_EXE_WIN32_PRIORITY_NORMAL priority, unless the calling + * process is in #ECORE_EXE_WIN32_PRIORITY_IDLE or + * #ECORE_EXE_WIN32_PRIORITY_BELOW_NORMAL priority. In that case, the + * child process inherits this priority. + * @li On other platforms, if set to #ECORE_EXE_PRIORITY_INHERIT child + * processes inherits the priority of their parent. This is the default. + * + * @param pri value a Ecore_Exe_Win32_Priority value on Windows, -20 + * to 19 or ECORE_EXE_PRIORITY_INHERIT on other OS. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI void +ecore_exe_run_priority_set(int pri) +{ + run_pri = pri; +} + +/** + * Gets the priority at which to launch processes + * + * This gets ths priority of launched processes. See + * ecore_exe_run_priority_set() for details. This just returns the value set + * by this call. + * + * @return the value set by ecore_exe_run_priority_set() + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI int +ecore_exe_run_priority_get(void) +{ + return run_pri; +} + +/** + * Spawns a child process. + * + * This is now just a thin wrapper around ecore_exe_pipe_run() + * + * @param exe_cmd The command to run with @c /bin/sh. + * @param data Data to attach to the returned process handle. + * @return A process handle to the spawned process. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI Ecore_Exe * +ecore_exe_run(const char *exe_cmd, const void *data) +{ +/* I'm just being paranoid again, leaving in the original code in case there is a problem. */ +#if 0 + Ecore_Exe *exe; + pid_t pid; + + if (!exe_cmd) + return NULL; + pid = fork(); + if (pid) + { + exe = calloc(1, sizeof(Ecore_Exe)); + if (!exe) + { + kill(pid, SIGKILL); + return NULL; + } + ECORE_MAGIC_SET(exe, ECORE_MAGIC_EXE); + exe->pid = pid; + exe->data = (void *)data; + exe->cmd = strdup(exe_cmd); + exes = _ecore_list2_append(exes, exe); + return exe; + } + _ecore_exe_exec_it(exe_cmd, 0); + exit(127); + return NULL; +#else + return ecore_exe_pipe_run(exe_cmd, 0, data); +#endif +} + +/** + * Spawns a child process with its stdin/out available for communication. + * + * This function forks and runs the given command using @c /bin/sh. + * + * Note that the process handle is only valid until a child process + * terminated event is received. After all handlers for the child process + * terminated event have been called, the handle will be freed by Ecore. + * + * This function does the same thing as ecore_exe_run(), but also makes the + * standard in and/or out as well as stderr from the child process available + * for reading or writing. To write use ecore_exe_send(). To read listen to + * ECORE_EXE_EVENT_DATA or ECORE_EXE_EVENT_ERROR events (set up handlers). + * Ecore may buffer read and error data until a newline character if asked + * for with the @p flags. All data will be included in the events (newlines + * will be replaced with NULLS if line buffered). ECORE_EXE_EVENT_DATA events + * will only happen if the process is run with ECORE_EXE_PIPE_READ enabled + * in the flags. The same with the error version. Writing will only be + * allowed with ECORE_EXE_PIPE_WRITE enabled in the flags. + * + * @param exe_cmd The command to run with @c /bin/sh. + * @param flags The flag parameters for how to deal with inter-process I/O + * @param data Data to attach to the returned process handle. + * @return A process handle to the spawned process. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI Ecore_Exe * +ecore_exe_pipe_run(const char *exe_cmd, Ecore_Exe_Flags flags, const void *data) +{ + Ecore_Exe *exe = NULL; + int statusPipe[2] = { -1, -1 }; + int errorPipe[2] = { -1, -1 }; + int readPipe[2] = { -1, -1 }; + int writePipe[2] = { -1, -1 }; + int n = 0; + int ok = 1; + int result; + + if (!exe_cmd) return NULL; + exe = calloc(1, sizeof(Ecore_Exe)); + if (!exe) return NULL; + + if ((flags & ECORE_EXE_PIPE_AUTO) && (!(flags & ECORE_EXE_PIPE_ERROR)) + && (!(flags & ECORE_EXE_PIPE_READ))) + /* We need something to auto pipe. */ + flags |= ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR; + + exe->child_fd_error = -1; + exe->child_fd_read = -1; + exe->child_fd_write = -1; + exe->child_fd_error_x = -1; + exe->child_fd_read_x = -1; + exe->child_fd_write_x = -1; + + /* Create some pipes. */ + if (ok) + { + E_IF_NO_ERRNO_NOLOOP(result, pipe(statusPipe), ok) + { + } + } + if (ok && (flags & ECORE_EXE_PIPE_ERROR)) + { + E_IF_NO_ERRNO_NOLOOP(result, pipe(errorPipe), ok) + { + exe->child_fd_error = errorPipe[0]; + exe->child_fd_error_x = errorPipe[1]; + } + } + if (ok && (flags & ECORE_EXE_PIPE_READ)) + { + E_IF_NO_ERRNO_NOLOOP(result, pipe(readPipe), ok) + { + exe->child_fd_read = readPipe[0]; + exe->child_fd_read_x = readPipe[1]; + } + } + if (ok && (flags & ECORE_EXE_PIPE_WRITE)) + { + E_IF_NO_ERRNO_NOLOOP(result, pipe(writePipe), ok) + { + exe->child_fd_write = writePipe[1]; + exe->child_fd_write_x = writePipe[0]; + } + } + if (ok) + { + pid_t pid = 0; + volatile int vfork_exec_errno = 0; + + /* FIXME: I should double check this. After a quick look around, this is already done, but via a more modern method. */ + /* signal(SIGPIPE, SIG_IGN); We only want EPIPE on errors */ + pid = fork(); + + if (pid == -1) + { + ERR("Failed to fork process"); + pid = 0; + } + else if (pid == 0) /* child */ + { + if (run_pri != ECORE_EXE_PRIORITY_INHERIT) + { + if ((run_pri >= -20) && (run_pri <= 19)) + setpriority(PRIO_PROCESS, 0, run_pri); + } + /* dup2 STDERR, STDIN, and STDOUT. dup2() allegedly closes the + * second pipe if it's open. On the other hand, there was the + * Great FD Leak Scare of '06, so let's be paranoid. */ + if (ok && (flags & ECORE_EXE_PIPE_ERROR)) + { + E_NO_ERRNO(result, close(STDERR_FILENO), ok); + E_NO_ERRNO(result, dup2(errorPipe[1], STDERR_FILENO), ok); + } + if (ok && (flags & ECORE_EXE_PIPE_READ)) + { + E_NO_ERRNO(result, close(STDOUT_FILENO), ok); + E_NO_ERRNO(result, dup2(readPipe[1], STDOUT_FILENO), ok); + } + if (ok && (flags & ECORE_EXE_PIPE_WRITE)) + { + E_NO_ERRNO(result, close(STDIN_FILENO), ok); + E_NO_ERRNO(result, dup2(writePipe[0], STDIN_FILENO), ok); + } + + if (ok) + { + /* Setup the status pipe. */ + E_NO_ERRNO(result, close(statusPipe[0]), ok); + E_IF_NO_ERRNO(result, fcntl(statusPipe[1], F_SETFD, FD_CLOEXEC), ok) /* close on exec shows success */ + { + /* Run the actual command. */ + _ecore_exe_exec_it(exe_cmd, flags); /* no return */ + } + } + + /* Something went 'orribly wrong. */ + vfork_exec_errno = errno; + + /* Close the pipes. */ + if (flags & ECORE_EXE_PIPE_ERROR) + E_NO_ERRNO(result, close(errorPipe[1]), ok); + if (flags & ECORE_EXE_PIPE_READ) + E_NO_ERRNO(result, close(readPipe[1]), ok); + if (flags & ECORE_EXE_PIPE_WRITE) + E_NO_ERRNO(result, close(writePipe[0]), ok); + E_NO_ERRNO(result, close(statusPipe[1]), ok); + + _exit(-1); + } + else /* parent */ + { + /* Close the unused pipes. */ + E_NO_ERRNO(result, close(statusPipe[1]), ok); + + /* FIXME: after having a good look at the current e fd + * handling, investigate fcntl(dataPipe[x], F_SETSIG, ...) */ + /* FIXME: above F_SETSIG etc. - this is async SIGIO based IO + * which is also linux specific so we probably don't want to + * do this as long as select() is working fine. the only time + * we really want to think of SIGIO async IO is when it all + * actually works basically everywhere and we can turn all + * IO into DMA async activities (i.e. you do a read() then + * the read is complete not on return but when you get a + * SIGIO - the read() just starts the transfer and it is + * completed in the background by DMA (or whatever mechanism + * the kernel choses)) */ + + /* Wait for it to start executing. */ + /* FIXME: this doesn't seem very nice - we sit and block + * waiting on a child process... even though it's just + * the segment between the fork() and the exec) it just feels + * wrong */ + for (;;) + { + char buf; + + E_NO_ERRNO(result, read(statusPipe[0], &buf, 1), ok); + if (result == 0) + { + if (vfork_exec_errno != 0) + { + n = vfork_exec_errno; + ERR("Could not start \"%s\"", exe_cmd); + pid = 0; + } + break; + } + } + + /* Close the status pipe. */ + E_NO_ERRNO(result, close(statusPipe[0]), ok); + } + + if (pid) + { + /* Setup the exe structure. */ + ECORE_MAGIC_SET(exe, ECORE_MAGIC_EXE); + exe->start_bytes = -1; + exe->end_bytes = -1; + exe->start_lines = -1; + exe->end_lines = -1; + exe->pid = pid; + exe->flags = flags; + exe->data = (void *)data; + if ((exe->cmd = strdup(exe_cmd))) + { + if (flags & ECORE_EXE_PIPE_ERROR) + { /* Setup the error stuff. */ + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_error, F_SETFL, + O_NONBLOCK), ok) {} + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_error, F_SETFD, + FD_CLOEXEC), ok) {} + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_error_x, F_SETFD, + FD_CLOEXEC), ok) {} + { + exe->error_fd_handler = + ecore_main_fd_handler_add(exe->child_fd_error, + ECORE_FD_READ, + _ecore_exe_data_error_handler, + exe, NULL, NULL); + if (!exe->error_fd_handler) + ok = 0; + } + } + if (ok && (flags & ECORE_EXE_PIPE_READ)) + { /* Setup the read stuff. */ + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_read, F_SETFL, + O_NONBLOCK), ok) {} + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_read, F_SETFD, + FD_CLOEXEC), ok) {} + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_read_x, F_SETFD, + FD_CLOEXEC), ok) {} + { + exe->read_fd_handler = + ecore_main_fd_handler_add(exe->child_fd_read, + ECORE_FD_READ, + _ecore_exe_data_read_handler, + exe, NULL, NULL); + if (!exe->read_fd_handler) + ok = 0; + } + } + if (ok && (flags & ECORE_EXE_PIPE_WRITE)) + { /* Setup the write stuff. */ + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_write, F_SETFL, + O_NONBLOCK), ok) {} + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_write, F_SETFD, + FD_CLOEXEC), ok) {} + E_IF_NO_ERRNO(result, + fcntl(exe->child_fd_write_x, F_SETFD, + FD_CLOEXEC), ok) {} + { + exe->write_fd_handler = + ecore_main_fd_handler_add(exe->child_fd_write, + ECORE_FD_WRITE, + _ecore_exe_data_write_handler, + exe, NULL, NULL); + if (exe->write_fd_handler) + ecore_main_fd_handler_active_set(exe->write_fd_handler, 0); /* Nothing to write to start with. */ + else + ok = 0; + } + } + + exes = (Ecore_Exe *) eina_inlist_append(EINA_INLIST_GET(exes), EINA_INLIST_GET(exe)); + n = 0; + } + else + ok = 0; + } + else + ok = 0; + } + + if (!ok) + { /* Something went wrong, so pull down everything. */ + if (exe->pid) ecore_exe_terminate(exe); + IF_FN_DEL(ecore_exe_free, exe); + } + else + { + Ecore_Exe_Event_Add *e; + + e = _ecore_exe_event_add_new(); + e->exe = exe; + if (e) /* Send the event. */ + ecore_event_add(ECORE_EXE_EVENT_ADD, e, + _ecore_exe_event_add_free, NULL); + /* INF("Running as %d for %s.\n", exe->pid, exe->cmd); */ + } + + errno = n; + return exe; +} + +/** + * Defines a function to be called before really freeing the handle data. + * + * This might be useful for language bindings such as Python and Perl + * that need to deallocate wrappers associated with this handle. + * + * This handle should never be modified by this call. It should be + * considered informative only. All getters are valid when the given + * function is called back. + * + * @param exe The child process to attach the pre_free function. + * @param func The function to call before @a exe is freed. + */ +EAPI void +ecore_exe_callback_pre_free_set(Ecore_Exe *exe, Ecore_Exe_Cb func) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, + "ecore_exe_callback_pre_free_set"); + return; + } + exe->pre_free_cb = func; +} + +/** + * Sends data to the given child process which it receives on stdin. + * + * This function writes to a child processes standard in, with unlimited + * buffering. This call will never block. It may fail if the system runs out + * of memory. + * + * @param exe The child process to send to + * @param data The data to send + * @param size The size of the data to send, in bytes + * @return EINA_TRUE if successful, EINA_FALSE on failure. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI Eina_Bool +ecore_exe_send(Ecore_Exe * exe, const void *data, int size) +{ + void *buf; + + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_send"); + return EINA_FALSE; + } + + if (exe->close_stdin) + { + ERR("Ecore_Exe %p stdin is closed! Cannot send %d bytes from %p", + exe, size, data); + return EINA_FALSE; + } + + if (exe->child_fd_write == -1) + { + ERR("Ecore_Exe %p created without ECORE_EXE_PIPE_WRITE! " + "Cannot send %d bytes from %p", exe, size, data); + return EINA_FALSE; + } + + buf = realloc(exe->write_data_buf, exe->write_data_size + size); + if (!buf) return EINA_FALSE; + + exe->write_data_buf = buf; + memcpy((char *)exe->write_data_buf + exe->write_data_size, data, size); + exe->write_data_size += size; + + if (exe->write_fd_handler) + ecore_main_fd_handler_active_set(exe->write_fd_handler, ECORE_FD_WRITE); + + return EINA_TRUE; +} + +/** + * The stdin of the given child process will close when the write buffer is empty. + * + * @param exe The child process + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI void +ecore_exe_close_stdin(Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_close_stdin"); + return; + } + exe->close_stdin = 1; +} + +/** + * Sets the auto pipe limits for the given process handle. On Windows + * this function does nothing. + * + * @param exe The given process handle. + * @param start_bytes limit of bytes at start of output to buffer. + * @param end_bytes limit of bytes at end of output to buffer. + * @param start_lines limit of lines at start of output to buffer. + * @param end_lines limit of lines at end of output to buffer. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI void +ecore_exe_auto_limits_set(Ecore_Exe *exe, int start_bytes, int end_bytes, int start_lines, int end_lines) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_auto_limits_set"); + return; + } + /* FIXME: sanitize the input. */ + exe->start_bytes = start_bytes; + exe->end_bytes = end_bytes; + exe->start_lines = start_lines; + exe->end_lines = end_lines; + + /* FIXME: get this can of worms working. + * + * capture stderr & stdout internally + * + * raster and onefang keep moving the goal posts on this one. It started out as + * "show users the error output if an exe fails" and is rapidly approaching + * "alternative method of getting the data, poll vs event driven". Some serious + * thinking needs to be applied to this. Do we really want to go that far? If + * so, we should change the names. The basic design will probably remain the + * same which ever way we go. The constant goal post moving is probably due to + * generic design methods leading to feature creep as we inspired each other to + * more generic designs. It does seem like the closer we get to poll driven, + * the more issues and corner cases there are. + * + * Instead of doing the usual register an event handler thing, we are ecore_exe, + * we can take some short cuts. Don't send the events, just leave the exe buffers + * as is until the user asks for them, then return the event. + * + * start = 0, end = 0; clogged arteries get flushed, everything is ignored. + * start = -1, end = -1; clogged arteries get transferred to internal buffers. Actually, either == -1 means buffer everything. + * start = X, end = 0; buffer first X out of clogged arteries, flush and ignore rest. + * start = 0, end = X; circular buffer X + * start = X, end = Y; buffer first X out of clogged arteries, circular buffer Y from beginning. + * + * bytes vs lines, which ever one reaches the limit first. + * Before we go beyond the start+end limit, leave the end buffer empty, and store both in the start buffer, coz they overlap. + * After we pass the the start+end limit, insert "\n...\n" at the end of the start buffer, copy the rest to the end buffer, then store in the end buffer. + * + * Other issues - + * Spank programmer for polling data if polling is not turned on. + * Spank programmer for setting up event callbacks if polling is turned on. + * Spank programmer for freeing the event data if it came from the event system, as that autofrees. + * Spank the programmer if they try to set the limits bigger than what has been gathered & ignored already, coz they just lost data. + * Spank onefang and raster for opening this can of worms. + * Should we have separate out/err limits? + * Should we remove from the internal buffer the data that was delivered already? + * If so, what to do about limits, start, and end? They could loose their meaning. + */ +} + +/** + * Gets the auto pipe data for the given process handle + * + * @param exe The given process handle. + * @param flags Is this a ECORE_EXE_PIPE_READ or ECORE_EXE_PIPE_ERROR? + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI Ecore_Exe_Event_Data * +ecore_exe_event_data_get(Ecore_Exe *exe, Ecore_Exe_Flags flags) +{ + Ecore_Exe_Event_Data *e = NULL; + int is_buffered = 0; + unsigned char *inbuf; + int inbuf_num; + + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_event_data_get"); + return NULL; + } + + /* Sort out what sort of event we are. */ + if (flags & ECORE_EXE_PIPE_READ) + { + flags = ECORE_EXE_PIPE_READ; + if (exe->flags & ECORE_EXE_PIPE_READ_LINE_BUFFERED) + is_buffered = 1; + } + else + { + flags = ECORE_EXE_PIPE_ERROR; + if (exe->flags & ECORE_EXE_PIPE_ERROR_LINE_BUFFERED) + is_buffered = 1; + } + + /* Get the data. */ + if (flags & ECORE_EXE_PIPE_READ) + { + inbuf = exe->read_data_buf; + inbuf_num = exe->read_data_size; + exe->read_data_buf = NULL; + exe->read_data_size = 0; + } + else + { + inbuf = exe->error_data_buf; + inbuf_num = exe->error_data_size; + exe->error_data_buf = NULL; + exe->error_data_size = 0; + } + + e = calloc(1, sizeof(Ecore_Exe_Event_Data)); + if (e) + { + e->exe = exe; + e->data = inbuf; + e->size = inbuf_num; + + if (is_buffered) + { /* Deal with line buffering. */ + int max = 0; + int count = 0; + int i; + int last = 0; + char *c; + + c = (char *)inbuf; + for (i = 0; i < inbuf_num; i++) /* Find the lines. */ + { + if (inbuf[i] == '\n') + { + if (count >= max) + { + /* In testing, the lines seem to arrive in batches of 500 to 1000 lines at most, roughly speaking. */ + max += 10; /* FIXME: Maybe keep track of the largest number of lines ever sent, and add half that many instead of 10. */ + e->lines = realloc(e->lines, sizeof(Ecore_Exe_Event_Data_Line) * (max + 1)); /* Allow room for the NULL termination. */ + } + /* raster said to leave the line endings as line endings, however - + * This is line buffered mode, we are not dealing with binary here, but lines. + * If we are not dealing with binary, we must be dealing with ASCII, unicode, or some other text format. + * Thus the user is most likely gonna deal with this text as strings. + * Thus the user is most likely gonna pass this data to str functions. + * rasters way - the endings are always gonna be '\n'; onefangs way - they will always be '\0' + * We are handing them the string length as a convenience. + * Thus if they really want it in raw format, they can e->lines[i].line[e->lines[i].size - 1] = '\n'; easily enough. + * In the default case, we can do this conversion quicker than the user can, as we already have the index and pointer. + * Let's make it easy on them to use these as standard C strings. + * + * onefang is proud to announce that he has just set a new personal record for the + * most over documentation of a simple assignment statement. B-) + */ + inbuf[i] = '\0'; + e->lines[count].line = c; + e->lines[count].size = i - last; + last = i + 1; + c = (char *)&inbuf[last]; + count++; + } + } + if (count == 0) /* No lines to send, cancel the event. */ + { + _ecore_exe_event_exe_data_free(NULL, e); + e = NULL; + } + else /* NULL terminate the array, so that people know where the end is. */ + { + e->lines[count].line = NULL; + e->lines[count].size = 0; + } + if (i > last) /* Partial line left over, save it for next time. */ + { + if (e) e->size = last; + if (flags & ECORE_EXE_PIPE_READ) + { + exe->read_data_size = i - last; + exe->read_data_buf = malloc(exe->read_data_size); + memcpy(exe->read_data_buf, c, exe->read_data_size); + } + else + { + exe->error_data_size = i - last; + exe->error_data_buf = malloc(exe->error_data_size); + memcpy(exe->error_data_buf, c, exe->error_data_size); + } + } + } + } + + return e; +} + +/** + * Sets the string tag for the given process handle + * + * @param exe The given process handle. + * @param tag The string tag to set on the process handle. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI void +ecore_exe_tag_set(Ecore_Exe *exe, const char *tag) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_tag_set"); + return; + } + IF_FREE(exe->tag); + if (tag) + exe->tag = strdup(tag); + else + exe->tag = NULL; +} + +/** + * Retrieves the tag attached to the given process handle. There is no need to + * free it as it just returns the internal pointer value. This value is only + * valid as long as the @p exe is valid or until the tag is set to something + * else on this @p exe. + * + * @param exe The given process handle. + * @return The string attached to @p exe. It is a handle to existing + * internal string and should not be modified, use + * ecore_exe_tag_set() to change it. It might be @c NULL. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI const char * +ecore_exe_tag_get(const Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_tag_get"); + return NULL; + } + return exe->tag; +} + +/** + * Frees the given process handle. + * + * Note that the process that the handle represents is unaffected by this + * function. + * + * @param exe The given process handle. + * @return The data attached to the handle when @ref ecore_exe_run was + * called. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI void * +ecore_exe_free(Ecore_Exe *exe) +{ + void *data; + int ok = 0; + int result; + + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_free"); + return NULL; + } + + data = exe->data; + + if (exe->pre_free_cb) + exe->pre_free_cb(data, exe); + + if (exe->doomsday_clock) + { + struct _ecore_exe_dead_exe *dead; + + ecore_timer_del(exe->doomsday_clock); + exe->doomsday_clock = NULL; + dead = exe->doomsday_clock_dead; + if (dead) + { + IF_FREE(dead->cmd); + free(dead); + exe->doomsday_clock_dead = NULL; + } + } + IF_FN_DEL(ecore_main_fd_handler_del, exe->write_fd_handler); + IF_FN_DEL(ecore_main_fd_handler_del, exe->read_fd_handler); + IF_FN_DEL(ecore_main_fd_handler_del, exe->error_fd_handler); + if (exe->child_fd_write_x != -1) + E_NO_ERRNO(result, close(exe->child_fd_write_x), ok); + if (exe->child_fd_read_x != -1) + E_NO_ERRNO(result, close(exe->child_fd_read_x), ok); + if (exe->child_fd_error_x != -1) + E_NO_ERRNO(result, close(exe->child_fd_error_x), ok); + if (exe->child_fd_write != -1) + E_NO_ERRNO(result, close(exe->child_fd_write), ok); + if (exe->child_fd_read != -1) + E_NO_ERRNO(result, close(exe->child_fd_read), ok); + if (exe->child_fd_error != -1) + E_NO_ERRNO(result, close(exe->child_fd_error), ok); + IF_FREE(exe->write_data_buf); + IF_FREE(exe->read_data_buf); + IF_FREE(exe->error_data_buf); + IF_FREE(exe->cmd); + + exes = (Ecore_Exe *) eina_inlist_remove(EINA_INLIST_GET(exes), EINA_INLIST_GET(exe)); + ECORE_MAGIC_SET(exe, ECORE_MAGIC_NONE); + IF_FREE(exe->tag); + free(exe); + return data; +} + +/** + * Frees the given event data. + * + * @param e The given event data. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI void +ecore_exe_event_data_free(Ecore_Exe_Event_Data *e) +{ + if (!e) return; + IF_FREE(e->lines); + IF_FREE(e->data); + free(e); +} + +/** + * Retrieves the process ID of the given spawned process. + * @param exe Handle to the given spawned process. + * @return The process ID on success. @c -1 otherwise. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI pid_t +ecore_exe_pid_get(const Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_pid_get"); + return -1; + } + return exe->pid; +} + +/** + * Retrieves the command of the given spawned process. + * @param exe Handle to the given spawned process. + * @return The command on success. NULL otherwise. This string is the + * pointer to the internal value and must not be modified in + * any way. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI const char * +ecore_exe_cmd_get(const Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_cmd_get"); + return NULL; + } + return exe->cmd; +} + +/** + * Retrieves the data attached to the given process handle. + * @param exe The given process handle. + * @return The data pointer attached to @p exe Given to + * ecore_exe_run() or ecore_exe_pipe_run() + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI void * +ecore_exe_data_get(const Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_data_get"); + return NULL; + } + return exe->data; +} + +/** + * Retrieves the flags attached to the given process handle. + * @param exe The given process handle. + * @return The flags attached to @p exe. + * @ingroup Ecore_Exe_Basic_Group + */ +EAPI Ecore_Exe_Flags +ecore_exe_flags_get(const Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_data_get"); + return 0; + } + return exe->flags; +} + +/** + * @defgroup Ecore_Exe_Signal_Group Spawned Process Signal Functions + * + * Functions that send signals to spawned processes. + */ + +/** + * Pauses the given process by sending it a @c SIGSTOP signal. + * @param exe Process handle to the given process. + * @ingroup Ecore_Exe_Signal_Group + */ +EAPI void +ecore_exe_pause(Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_pause"); + return; + } + kill(exe->pid, SIGSTOP); +} + +/** + * Continues the given paused process by sending it a @c SIGCONT signal. + * @param exe Process handle to the given process. + * @ingroup Ecore_Exe_Signal_Group + */ +EAPI void +ecore_exe_continue(Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_continue"); + return; + } + kill(exe->pid, SIGCONT); +} + +/** + * Sends the given spawned process a interrupt (@c SIGINT) signal. + * @param exe Process handle to the given process. + * @ingroup Ecore_Exe_Signal_Group + */ +EAPI void +ecore_exe_interrupt(Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_interrupt"); + return; + } + _ecore_exe_dead_attach(exe); + kill(exe->pid, SIGINT); +} + +/** + * Sends the given spawned process a quit (@c SIGQUIT) signal. + * @param exe Process handle to the given process. + * @ingroup Ecore_Exe_Signal_Group + */ +EAPI void +ecore_exe_quit(Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_quit"); + return; + } + _ecore_exe_dead_attach(exe); + kill(exe->pid, SIGQUIT); +} + +/** + * Sends the given spawned process a terminate (@c SIGTERM) signal. + * @param exe Process handle to the given process. + * @ingroup Ecore_Exe_Signal_Group + */ +EAPI void +ecore_exe_terminate(Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_terminate"); + return; + } + _ecore_exe_dead_attach(exe); + INF("Sending TERM signal to %s (%d).", exe->cmd, exe->pid); + kill(exe->pid, SIGTERM); +} + +/** + * Kills the given spawned process by sending it a @c SIGKILL signal. + * @param exe Process handle to the given process. + * @ingroup Ecore_Exe_Signal_Group + */ +EAPI void +ecore_exe_kill(Ecore_Exe *exe) +{ + struct _ecore_exe_dead_exe *dead; + + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_kill"); + return; + } + + dead = calloc(1, sizeof(struct _ecore_exe_dead_exe)); + if (dead) + { + dead->pid = exe->pid; + dead->cmd = strdup(exe->cmd); + IF_FN_DEL(ecore_timer_del, exe->doomsday_clock); + exe->doomsday_clock = + ecore_timer_add(10.0, _ecore_exe_make_sure_its_really_dead, dead); + } + + INF("Sending KILL signal to %s (%d).", exe->cmd, exe->pid); + kill(exe->pid, SIGKILL); +} + +/** + * Sends a @c SIGUSR signal to the given spawned process. + * @param exe Process handle to the given process. + * @param num The number user signal to send. Must be either 1 or 2, or + * the signal will be ignored. + * @ingroup Ecore_Exe_Signal_Group + */ +EAPI void +ecore_exe_signal(Ecore_Exe *exe, int num) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_signal"); + return; + } + if (num == 1) + kill(exe->pid, SIGUSR1); + else if (num == 2) + kill(exe->pid, SIGUSR2); +} + +/** + * Sends a @c SIGHUP signal to the given spawned process. + * @param exe Process handle to the given process. + * @ingroup Ecore_Exe_Signal_Group + */ +EAPI void +ecore_exe_hup(Ecore_Exe *exe) +{ + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + { + ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_hup"); + return; + } + kill(exe->pid, SIGHUP); +} + +static Ecore_Exe * +_ecore_exe_is_it_alive(pid_t pid) +{ + Ecore_Exe *exe = NULL; + + /* FIXME: There is no nice, safe, OS independent way to tell if a + * particular PID is still alive. I have written code to do so + * for my urunlevel busybox applet (http://urunlevel.sourceforge.net/), + * but it's for linux only, and still not guaranteed. + * + * So for now, we just check that a valid Ecore_Exe structure + * exists for it. Even that is not a guarantee, as the structure + * can be freed without killing the process. + * + * I think we can safely put exe's into two categories, those users + * that care about the life of the exe, and the run and forget type. + * The run and forget type starts up the exe, then free's the + * Ecore_Exe structure straight away. They can never call any of + * the functions that can call this, so we don't worry about them. + * + * Those user's that care about the life of exe's will keep the + * Ecore_Exe structure around, terminate them eventually, or + * register for exit events. For these ones the assumption + * that valid Ecore_Exe struct == live exe is almost valid. + * + * I will probably copy my urunlevel code into here someday. + */ + exe = _ecore_exe_find(pid); + if (exe) + { + if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE)) + exe = NULL; + } + + return exe; +} + +static Eina_Bool +_ecore_exe_make_sure_its_dead(void *data) +{ + struct _ecore_exe_dead_exe *dead; + + dead = data; + if (dead) + { + Ecore_Exe *exe = NULL; + + if ((exe = _ecore_exe_is_it_alive(dead->pid))) + { + if (dead->cmd) + INF("Sending KILL signal to allegedly dead %s (%d).", + dead->cmd, dead->pid); + else + INF("Sending KILL signal to allegedly dead PID %d.", + dead->pid); + exe->doomsday_clock = + ecore_timer_add(10.0, _ecore_exe_make_sure_its_really_dead, + dead); + kill(dead->pid, SIGKILL); + } + else + { + IF_FREE(dead->cmd); + free(dead); + } + } + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +_ecore_exe_make_sure_its_really_dead(void *data) +{ + struct _ecore_exe_dead_exe *dead; + + dead = data; + if (dead) + { + Ecore_Exe *exe = NULL; + + if ((exe = _ecore_exe_is_it_alive(dead->pid))) + { + ERR("RUN! The zombie wants to eat your brains! And your CPU!"); + if (dead->cmd) + INF("%s (%d) is not really dead.", dead->cmd, dead->pid); + else + INF("PID %d is not really dead.", dead->pid); + exe->doomsday_clock = NULL; + } + IF_FREE(dead->cmd); + free(dead); + } + return ECORE_CALLBACK_CANCEL; +} + +void +_ecore_exe_init(void) +{ + ECORE_EXE_EVENT_ADD = ecore_event_type_new(); + ECORE_EXE_EVENT_DEL = ecore_event_type_new(); + ECORE_EXE_EVENT_DATA = ecore_event_type_new(); + ECORE_EXE_EVENT_ERROR = ecore_event_type_new(); +} + +void +_ecore_exe_shutdown(void) +{ + while (exes) + ecore_exe_free(exes); +} + +Ecore_Exe * +_ecore_exe_find(pid_t pid) +{ + Ecore_Exe *exe; + + EINA_INLIST_FOREACH(exes, exe) + { + if (exe->pid == pid) + return exe; + } + return NULL; +} + +Ecore_Timer * +_ecore_exe_doomsday_clock_get(Ecore_Exe *exe) +{ + return exe->doomsday_clock; +} + +void +_ecore_exe_doomsday_clock_set(Ecore_Exe *exe, Ecore_Timer *dc) +{ + exe->doomsday_clock = dc; +} + +static inline void +_ecore_exe_exec_it(const char *exe_cmd, Ecore_Exe_Flags flags) +{ + char use_sh = 1; + char *buf = NULL; + char **args = NULL; + int save_errno = 0; + + /* So what is this doing? + * + * We are trying to avoid wrapping the exe call with /bin/sh -c. + * We conservatively search for certain shell meta characters, + * If we don't find them, we can call the exe directly. + */ + if (!strpbrk(exe_cmd, "|&;<>()$`\\\"'*?#")) + { + char *token; + char pre_command = 1; + int num_tokens = 0; + + if (!(buf = strdup(exe_cmd))) + return; + + token = strtok(buf, " \t\n\v"); + while (token) + { + if (token[0] == '~') + break; + if (pre_command) + { + if (token[0] == '[') + break; + if (strchr(token, '=')) + break; + else + pre_command = 0; + } + num_tokens++; + token = strtok(NULL, " \t\n\v"); + } + IF_FREE(buf); + if ((!token) && (num_tokens)) + { + int i = 0; + + if (!(buf = strdup(exe_cmd))) + return; + + token = strtok(buf, " \t\n\v"); + use_sh = 0; + if (!(args = (char **)calloc(num_tokens + 1, sizeof(char *)))) + { + IF_FREE(buf); + return; + } + for (i = 0; i < num_tokens; i++) + { + if (token) + args[i] = token; + token = strtok(NULL, " \t\n\v"); + } + args[num_tokens] = NULL; + } + } + + if (!(flags & ECORE_EXE_NOT_LEADER)) setsid(); + if ((flags & ECORE_EXE_USE_SH)) + { + errno = 0; + execl("/bin/sh", "/bin/sh", "-c", exe_cmd, (char *)NULL); + } + else if (use_sh) + { /* We have to use a shell to run this. */ + if (!shell) + { /* Find users preferred shell. */ + shell = getenv("SHELL"); + if (!shell) + shell = "/bin/sh"; + } + errno = 0; + execl(shell, shell, "-c", exe_cmd, (char *)NULL); + } + else + { /* We can run this directly. */ + errno = 0; + execvp(args[0], args); + } + + save_errno = errno; + IF_FREE(buf); + IF_FREE(args); + errno = save_errno; + return; +} + +static Eina_Bool +_ecore_exe_data_generic_handler(void *data, Ecore_Fd_Handler *fd_handler, Ecore_Exe_Flags flags) +{ + Ecore_Exe *exe; + int child_fd; + int event_type; + + exe = data; + + /* Sort out what sort of handler we are. */ + if (flags & ECORE_EXE_PIPE_READ) + { + flags = ECORE_EXE_PIPE_READ; + event_type = ECORE_EXE_EVENT_DATA; + child_fd = exe->child_fd_read; + } + else + { + flags = ECORE_EXE_PIPE_ERROR; + event_type = ECORE_EXE_EVENT_ERROR; + child_fd = exe->child_fd_error; + } + + if ((fd_handler) + && (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))) + { + unsigned char *inbuf; + int inbuf_num; + + /* Get any left over data from last time. */ + if (flags & ECORE_EXE_PIPE_READ) + { + inbuf = exe->read_data_buf; + inbuf_num = exe->read_data_size; + exe->read_data_buf = NULL; + exe->read_data_size = 0; + } + else + { + inbuf = exe->error_data_buf; + inbuf_num = exe->error_data_size; + exe->error_data_buf = NULL; + exe->error_data_size = 0; + } + + for (;;) + { + int num, lost_exe; + char buf[READBUFSIZ]; + + lost_exe = 0; + errno = 0; + if ((num = read(child_fd, buf, READBUFSIZ)) < 1) + /* FIXME: SPEED/SIZE TRADE OFF - add a smaller READBUFSIZE + * (currently 64k) to inbuf, use that instead of buf, and + * save ourselves a memcpy(). */ + { + lost_exe = ((errno == EIO) || + (errno == EBADF) || + (errno == EPIPE) || + (errno == EINVAL) || (errno == ENOSPC)); + if ((errno != EAGAIN) && (errno != EINTR)) + perror("_ecore_exe_generic_handler() read problem "); + } + if (num > 0) + { /* data got read. */ + inbuf = realloc(inbuf, inbuf_num + num); + memcpy(inbuf + inbuf_num, buf, num); + inbuf_num += num; + } + else + { /* No more data to read. */ + if (inbuf) + { + Ecore_Exe_Event_Data *e; + + /* Stash the data away for later. */ + if (flags & ECORE_EXE_PIPE_READ) + { + exe->read_data_buf = inbuf; + exe->read_data_size = inbuf_num; + } + else + { + exe->error_data_buf = inbuf; + exe->error_data_size = inbuf_num; + } + + if (!(exe->flags & ECORE_EXE_PIPE_AUTO)) + { + e = ecore_exe_event_data_get(exe, flags); + if (e) /* Send the event. */ + ecore_event_add(event_type, e, + _ecore_exe_event_exe_data_free, + NULL); + } + } + if (lost_exe) + { + if (flags & ECORE_EXE_PIPE_READ) + { + if (exe->read_data_size) + INF("There are %d bytes left unsent from the dead exe %s.", + exe->read_data_size, exe->cmd); + } + else + { + if (exe->error_data_size) + INF("There are %d bytes left unsent from the dead exe %s.", + exe->error_data_size, exe->cmd); + } + /* Thought about this a bit. If the exe has actually + * died, this won't do any harm as it must have died + * recently and the pid has not had a chance to recycle. + * It is also a paranoid catchall, coz the usual ecore_signal + * mechenism should kick in. But let's give it a good + * kick in the head anyway. + */ + ecore_exe_terminate(exe); + } + break; + } + } + } + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_ecore_exe_data_error_handler(void *data, Ecore_Fd_Handler *fd_handler) +{ + return _ecore_exe_data_generic_handler(data, fd_handler, + ECORE_EXE_PIPE_ERROR); +} + +static Eina_Bool +_ecore_exe_data_read_handler(void *data, Ecore_Fd_Handler *fd_handler) +{ + return _ecore_exe_data_generic_handler(data, fd_handler, + ECORE_EXE_PIPE_READ); +} + +static Eina_Bool +_ecore_exe_data_write_handler(void *data, Ecore_Fd_Handler *fd_handler __UNUSED__) +{ + Ecore_Exe *exe; + + exe = data; + if ((exe->write_fd_handler) && + (ecore_main_fd_handler_active_get + (exe->write_fd_handler, ECORE_FD_WRITE))) + _ecore_exe_flush(exe); + + /* If we have sent all there is to send, and we need to close the pipe, then close it. */ + if ((exe->close_stdin == 1) + && (exe->write_data_size == exe->write_data_offset)) + { + int ok = 0; + int result; + + INF("Closing stdin for %s", exe->cmd); + /* if (exe->child_fd_write != -1) E_NO_ERRNO(result, fsync(exe->child_fd_write), ok); This a) doesn't work, and b) isn't needed. */ + IF_FN_DEL(ecore_main_fd_handler_del, exe->write_fd_handler); + if (exe->child_fd_write != -1) + E_NO_ERRNO(result, close(exe->child_fd_write), ok); + exe->child_fd_write = -1; + IF_FREE(exe->write_data_buf); + } + + return ECORE_CALLBACK_RENEW; +} + +static void +_ecore_exe_flush(Ecore_Exe *exe) +{ + int count; + + /* check whether we need to write anything at all. */ + if ((exe->child_fd_write == -1) || (!exe->write_data_buf)) + return; + if (exe->write_data_size == exe->write_data_offset) + return; + + count = write(exe->child_fd_write, + (char *)exe->write_data_buf + exe->write_data_offset, + exe->write_data_size - exe->write_data_offset); + if (count < 1) + { + if (errno == EIO || errno == EBADF || errno == EPIPE || errno == EINVAL || errno == ENOSPC) /* we lost our exe! */ + { + ecore_exe_terminate(exe); + if (exe->write_fd_handler) + ecore_main_fd_handler_active_set(exe->write_fd_handler, 0); + } + } + else + { + exe->write_data_offset += count; + if (exe->write_data_offset >= exe->write_data_size) + { /* Nothing left to write, clean up. */ + exe->write_data_size = 0; + exe->write_data_offset = 0; + IF_FREE(exe->write_data_buf); + if (exe->write_fd_handler) + ecore_main_fd_handler_active_set(exe->write_fd_handler, 0); + } + } +} + +static void +_ecore_exe_event_exe_data_free(void *data __UNUSED__, void *ev) +{ + Ecore_Exe_Event_Data *e; + + e = ev; + ecore_exe_event_data_free(e); +} + +static Ecore_Exe_Event_Add * +_ecore_exe_event_add_new(void) +{ + Ecore_Exe_Event_Add *e; + + e = calloc(1, sizeof(Ecore_Exe_Event_Add)); + return e; +} + +static void +_ecore_exe_event_add_free(void *data __UNUSED__, void *ev) +{ + Ecore_Exe_Event_Add *e; + + e = ev; + free(e); +} + +void * +_ecore_exe_event_del_new(void) +{ + Ecore_Exe_Event_Del *e; + + e = calloc(1, sizeof(Ecore_Exe_Event_Del)); + return e; +} + +void +_ecore_exe_event_del_free(void *data __UNUSED__, void *ev) +{ + Ecore_Exe_Event_Del *e; + + e = ev; + if (e->exe) + ecore_exe_free(e->exe); + free(e); +} + +static void +_ecore_exe_dead_attach(Ecore_Exe *exe) +{ + struct _ecore_exe_dead_exe *dead; + + if (exe->doomsday_clock_dead) return; + dead = calloc(1, sizeof(struct _ecore_exe_dead_exe)); + if (dead) + { + dead->pid = exe->pid; + dead->cmd = strdup(exe->cmd); + IF_FN_DEL(ecore_timer_del, exe->doomsday_clock); + exe->doomsday_clock = + ecore_timer_add(10.0, _ecore_exe_make_sure_its_dead, dead); + exe->doomsday_clock_dead = dead; + } +} diff --git a/tests/suite/ecore/src/lib/ecore_getopt.c b/tests/suite/ecore/src/lib/ecore_getopt.c new file mode 100644 index 0000000000..5b1c7bf9ea --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_getopt.c @@ -0,0 +1,1735 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_ALLOCA_H +# include <alloca.h> +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include <malloc.h> +# define alloca _alloca +#else +# include <stddef.h> +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> + +#ifdef ENABLE_NLS +# include <libintl.h> +#else +# define gettext(x) (x) +# define dgettext(domain, x) (x) +#endif + +#define _(x) dgettext("ecore", x) + +#ifdef _WIN32_WCE +# include <Evil.h> +#endif + +#include "Ecore.h" +#include "Ecore_Getopt.h" + +static const char *prog = NULL; +static char **argv = NULL; +static int argc = 0; +static int cols = 80; +static int helpcol = 80 / 3; + +static void +_ecore_getopt_help_print_replace_program(FILE *fp, const Ecore_Getopt *parser __UNUSED__, const char *text) +{ + do + { + const char *d = strchr(text, '%'); + + if (!d) + { + fputs(text, fp); + break; + } + + if (fwrite(text, 1, d - text, fp) != (size_t)(d - text)) + return; + d++; + if (strncmp(d, "prog", sizeof("prog") - 1) == 0) + { + fputs(prog ? prog : "???", fp); + d += sizeof("prog") - 1; + } + else + { + if (d[0] == '%') + d++; + fputc('%', fp); + } + + text = d; + } + while (text[0] != '\0'); + + fputc('\n', fp); +} + +static void +_ecore_getopt_version(FILE *fp, const Ecore_Getopt *parser) +{ + fputs(_("Version:"), fp); + fputc(' ', fp); + _ecore_getopt_help_print_replace_program(fp, parser, parser->version); +} + +static void +_ecore_getopt_help_usage(FILE *fp, const Ecore_Getopt *parser) +{ + fputs(_("Usage:"), fp); + fputc(' ', fp); + + if (!parser->usage) + { + fprintf(fp, _("%s [options]\n"), prog); + return; + } + + _ecore_getopt_help_print_replace_program(fp, parser, gettext(parser->usage)); +} + +static int +_ecore_getopt_help_line(FILE *fp, const int base, const int total, int used, const char *text, int len) +{ + int linebreak = 0; + do + { + /* process line considering spaces (new line and tabs are spaces!) */ + while ((used < total) && (len > 0)) + { + const char *space = NULL; + int i, todo; + + todo = total - used; + if (todo > len) + todo = len; + + for (i = 0; i < todo; i++) + if (isspace(text[i])) + { + space = text + i; + break; + } + + if (space) + { + i = fwrite(text, 1, i, fp); + i++; + text += i; + len -= i; + used += i; + + if (linebreak) + { + linebreak = 0; + continue; + } + + if (space[0] == '\n') + break; + else if (space[0] == '\t') + { + int c; + + used--; + c = ((used / 8) + 1) * 8; + if (c < total) + { + for (; used < c; used++) + fputc(' ', fp); + } + else + { + text--; + len++; + break; + } + } + else if (used < total) + fputc(space[0], fp); + } + else + { + i = fwrite(text, 1, i, fp); + text += i; + len -= i; + used += i; + } + linebreak = 0; + } + if (len <= 0) + break; + linebreak = 1; + fputc('\n', fp); + for (used = 0; used < base; used++) + fputc(' ', fp); + } + while (1); + + return used; +} + +static void +_ecore_getopt_help_description(FILE *fp, const Ecore_Getopt *parser) +{ + const char *p, *prg, *ver; + int used, prglen, verlen; + + p = gettext(parser->description); + if (!p) + return; + + fputc('\n', fp); + + prg = prog ? prog : "???"; + ver = parser->version ? parser->version : "???"; + + prglen = strlen(prg); + verlen = strlen(ver); + + used = 0; + + do + { + const char *d = strchr(p, '%'); + + if (!d) + { + _ecore_getopt_help_line(fp, 0, cols, used, p, strlen(p)); + break; + } + + used = _ecore_getopt_help_line(fp, 0, cols, used, p, d - p); + d++; + if (strncmp(d, "prog", sizeof("prog") - 1) == 0) + { + used = _ecore_getopt_help_line(fp, 0, cols, used, prg, prglen); + d += sizeof("prog") - 1; + } + else if (strncmp(d, "version", sizeof("version") - 1) == 0) + { + used = _ecore_getopt_help_line(fp, 0, cols, used, ver, verlen); + d += sizeof("version") - 1; + } + else + { + if (d[0] == '%') + d++; + used = _ecore_getopt_help_line(fp, 0, cols, used, "%", 1); + } + + p = d; + } + while (p[0] != '\0'); + + fputs("\n\n", fp); +} + +static void +_ecore_getopt_copyright(FILE *fp, const Ecore_Getopt *parser) +{ + const char *txt = gettext(parser->copyright); + fputs(_("Copyright:"), fp); + fputs("\n ", fp); + _ecore_getopt_help_line + (fp, 3, cols, 3, txt, strlen(txt)); + fputc('\n', fp); +} + +static void +_ecore_getopt_license(FILE *fp, const Ecore_Getopt *parser) +{ + const char *txt = gettext(parser->license); + fputs(_("License:"), fp); + fputs("\n ", fp); + _ecore_getopt_help_line + (fp, 3, cols, 3, txt, strlen(txt)); + fputc('\n', fp); +} + +static Ecore_Getopt_Desc_Arg_Requirement +_ecore_getopt_desc_arg_requirement(const Ecore_Getopt_Desc *desc) +{ + switch (desc->action) + { + case ECORE_GETOPT_ACTION_STORE: + return desc->action_param.store.arg_req; + case ECORE_GETOPT_ACTION_STORE_CONST: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_STORE_TRUE: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_STORE_FALSE: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_CHOICE: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES; + case ECORE_GETOPT_ACTION_APPEND: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES; + case ECORE_GETOPT_ACTION_COUNT: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_CALLBACK: + return desc->action_param.callback.arg_req; + case ECORE_GETOPT_ACTION_HELP: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_VERSION: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + default: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + } +} + +static void +_ecore_getopt_help_desc_setup_metavar(const Ecore_Getopt_Desc *desc, char *metavar, int *metavarlen, int maxsize) +{ + if (desc->metavar) + { + const char *txt = gettext(desc->metavar); + *metavarlen = strlen(txt); + if (*metavarlen > maxsize - 1) + *metavarlen = maxsize - 1; + + memcpy(metavar, txt, *metavarlen); + metavar[*metavarlen] = '\0'; + } + else if (desc->longname) + { + int i; + + *metavarlen = strlen(desc->longname); + if (*metavarlen > maxsize - 1) + *metavarlen = maxsize - 1; + + for (i = 0; i < *metavarlen; i++) + metavar[i] = toupper(desc->longname[i]); + metavar[i] = '\0'; + } +} + +static int +_ecore_getopt_help_desc_show_arg(FILE *fp, Ecore_Getopt_Desc_Arg_Requirement requirement, const char *metavar, int metavarlen) +{ + int used; + + if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + return 0; + + used = 0; + + if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL) + { + fputc('[', fp); + used++; + } + + if (requirement != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + { + fputc('=', fp); + fputs(metavar, fp); + used += metavarlen + 1; + } + + if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL) + { + fputc(']', fp); + used++; + } + + return used; +} + +static int +_ecore_getopt_help_desc_store(FILE *fp, const int base, const int total, int used, const Ecore_Getopt_Desc *desc) +{ + const Ecore_Getopt_Desc_Store *store = &desc->action_param.store; + char buf[64]; + const char *str; + size_t len; + + fputc('\n', fp); + for (used = 0; used < base; used++) + fputc(' ', fp); + + switch (store->type) + { + case ECORE_GETOPT_TYPE_STR: + str = "STR"; + len = sizeof("STR") - 1; + break; + case ECORE_GETOPT_TYPE_BOOL: + str = "BOOL"; + len = sizeof("BOOL") - 1; + break; + case ECORE_GETOPT_TYPE_SHORT: + str = "SHORT"; + len = sizeof("SHORT") - 1; + break; + case ECORE_GETOPT_TYPE_INT: + str = "INT"; + len = sizeof("INT") - 1; + break; + case ECORE_GETOPT_TYPE_LONG: + str = "LONG"; + len = sizeof("LONG") - 1; + break; + case ECORE_GETOPT_TYPE_USHORT: + str = "USHORT"; + len = sizeof("USHORT") - 1; + break; + case ECORE_GETOPT_TYPE_UINT: + str = "UINT"; + len = sizeof("UINT") - 1; + break; + case ECORE_GETOPT_TYPE_ULONG: + str = "ULONG"; + len = sizeof("ULONG") - 1; + break; + case ECORE_GETOPT_TYPE_DOUBLE: + str = "DOUBLE"; + len = sizeof("DOUBLE") - 1; + break; + default: + str = "???"; + len = sizeof("???") - 1; + } + + used = _ecore_getopt_help_line + (fp, base, total, used, _("Type: "), strlen(_("Type: "))); + used = _ecore_getopt_help_line(fp, base, total, used, str, len); + + if (store->arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES) + goto end; + + used = _ecore_getopt_help_line + (fp, base, total, used, ". ", sizeof(". ") - 1); + + switch (store->type) + { + case ECORE_GETOPT_TYPE_STR: + str = store->def.strv; + len = str ? strlen(str) : 0; + break; + case ECORE_GETOPT_TYPE_BOOL: + str = store->def.boolv ? "true" : "false"; + len = strlen(str); + break; + case ECORE_GETOPT_TYPE_SHORT: + str = buf; + len = snprintf(buf, sizeof(buf), "%hd", store->def.shortv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_INT: + str = buf; + len = snprintf(buf, sizeof(buf), "%d", store->def.intv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_LONG: + str = buf; + len = snprintf(buf, sizeof(buf), "%ld", store->def.longv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_USHORT: + str = buf; + len = snprintf(buf, sizeof(buf), "%hu", store->def.ushortv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_UINT: + str = buf; + len = snprintf(buf, sizeof(buf), "%u", store->def.uintv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_ULONG: + str = buf; + len = snprintf(buf, sizeof(buf), "%lu", store->def.ulongv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_DOUBLE: + str = buf; + len = snprintf(buf, sizeof(buf), "%f", store->def.doublev); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + default: + str = "???"; + len = sizeof("???") - 1; + } + + used = _ecore_getopt_help_line + (fp, base, total, used, _("Default: "), strlen(_("Default: "))); + used = _ecore_getopt_help_line(fp, base, total, used, str, len); + + end: + return _ecore_getopt_help_line(fp, base, total, used, ".", 1); +} + +static int +_ecore_getopt_help_desc_choices(FILE *fp, const int base, const int total, int used, const Ecore_Getopt_Desc *desc) +{ + const char *const *itr; + const char sep[] = ", "; + const int seplen = sizeof(sep) - 1; + + if (used > 0) + { + fputc('\n', fp); + used = 0; + } + for (; used < base; used++) + fputc(' ', fp); + + used = _ecore_getopt_help_line + (fp, base, total, used, _("Choices: "), strlen(_("Choices: "))); + + for (itr = desc->action_param.choices; *itr; itr++) + { + used = _ecore_getopt_help_line + (fp, base, total, used, *itr, strlen(*itr)); + if (itr[1]) + used = _ecore_getopt_help_line(fp, base, total, used, sep, seplen); + } + + return _ecore_getopt_help_line(fp, base, total, used, ".", 1); +} + +static void +_ecore_getopt_help_desc(FILE *fp, const Ecore_Getopt_Desc *desc) +{ + Ecore_Getopt_Desc_Arg_Requirement arg_req; + char metavar[32] = "ARG"; + int metavarlen = 3; + int used; + + arg_req = _ecore_getopt_desc_arg_requirement(desc); + if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + _ecore_getopt_help_desc_setup_metavar + (desc, metavar, &metavarlen, sizeof(metavar)); + + fputs(" ", fp); + used = 2; + + if (desc->shortname) + { + fputc('-', fp); + fputc(desc->shortname, fp); + used += 2; + used += _ecore_getopt_help_desc_show_arg + (fp, arg_req, metavar, metavarlen); + } + + if (desc->shortname && desc->longname) + { + fputs(", ", fp); + used += 2; + } + + if (desc->longname) + { + int namelen = strlen(desc->longname); + + fputs("--", fp); + fputs(desc->longname, fp); + used += 2 + namelen; + used += _ecore_getopt_help_desc_show_arg + (fp, arg_req, metavar, metavarlen); + } + + if (!desc->help) + goto end; + + if (used + 3 >= helpcol) + { + fputc('\n', fp); + used = 0; + } + + for (; used < helpcol; used++) + fputc(' ', fp); + + used = _ecore_getopt_help_line + (fp, helpcol, cols, used, desc->help, strlen(desc->help)); + + switch (desc->action) + { + case ECORE_GETOPT_ACTION_STORE: + _ecore_getopt_help_desc_store(fp, helpcol, cols, used, desc); + break; + case ECORE_GETOPT_ACTION_CHOICE: + _ecore_getopt_help_desc_choices(fp, helpcol, cols, used, desc); + break; + default: + break; + } + + end: + fputc('\n', fp); +} + +static unsigned char +_ecore_getopt_desc_is_sentinel(const Ecore_Getopt_Desc *desc) +{ + return (desc->shortname == '\0') && (!desc->longname); +} + +static void +_ecore_getopt_help_options(FILE *fp, const Ecore_Getopt *parser) +{ + const Ecore_Getopt_Desc *desc; + + fputs(_("Options:\n"), fp); + + for (desc = parser->descs; !_ecore_getopt_desc_is_sentinel(desc); desc++) + _ecore_getopt_help_desc(fp, desc); + + fputc('\n', fp); +} + +/** + * Show nicely formatted help message for the given parser. + * + * Message will be print to stderr. + */ +void +ecore_getopt_help(FILE *fp, const Ecore_Getopt *parser) +{ + const char *var; + + if (!parser) return; + + if (argc < 1) + { + ecore_app_args_get(&argc, &argv); + if ((argc > 0) && (argv[0])) + prog = argv[0]; + else + prog = parser->prog; + } + + var = getenv("COLUMNS"); + if (var) + { + cols = atoi(var); + if (cols < 20) + cols = 20; + + helpcol = cols / 3; + } + + _ecore_getopt_help_usage(fp, parser); + _ecore_getopt_help_description(fp, parser); + _ecore_getopt_help_options(fp, parser); +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_parse_find_long(const Ecore_Getopt *parser, const char *name) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + const char *p = strchr(name, '='); + int len = 0; + + if (p) + len = p - name; + + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + { + if (!desc->longname) + continue; + + if (p) + { + if ((strncmp(name, desc->longname, len) == 0) && + (desc->longname[len] == '\0')) + return desc; + } + else + { + if (strcmp(name, desc->longname) == 0) + return desc; + } + } + + return NULL; +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_parse_find_short(const Ecore_Getopt *parser, char name) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + if (name == desc->shortname) + return desc; + return NULL; +} + +static int +_ecore_getopt_parse_find_nonargs_base(const Ecore_Getopt *parser, int argc, char **argv) +{ + char **nonargs; + int src, dst, used, base; + + nonargs = alloca(sizeof(char*) * argc); + src = 1; + dst = 1; + used = 0; + base = 0; + while (src < argc) + { + const Ecore_Getopt_Desc *desc; + Ecore_Getopt_Desc_Arg_Requirement arg_req; + char *arg = argv[src]; + + if (arg[0] != '-') + goto found_nonarg; + + if (arg[1] == '-') + { + if (arg[2] == '\0') /* explicit end of options, "--" */ + { + base = 1; + break; + } + desc = _ecore_getopt_parse_find_long(parser, arg + 2); + } + else + desc = _ecore_getopt_parse_find_short(parser, arg[1]); + + if (!desc) + { + if (arg[1] == '-') + fprintf(stderr, _("ERROR: unknown option --%s.\n"), arg + 2); + else + fprintf(stderr, _("ERROR: unknown option -%c.\n"), arg[1]); + if (parser->strict) + { + memmove(argv + dst, nonargs, used * sizeof(char *)); + return -1; + } + else + goto found_nonarg; + } + + if (src != dst) + argv[dst] = argv[src]; + src++; + dst++; + + arg_req = _ecore_getopt_desc_arg_requirement(desc); + if (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + continue; + + if (strchr(arg, '=')) + continue; + + if ((src >= argc) || (argv[src][0] == '-')) + continue; + + if (src != dst) + argv[dst] = argv[src]; + src++; + dst++; + continue; + + found_nonarg: + nonargs[used] = arg; + used++; + src++; + } + + if (!base) /* '--' not found */ + base = dst; + else + { + base = dst; + if (src != dst) + argv[dst] = argv[src]; + dst++; + } + + memmove(argv + dst, nonargs, used * sizeof(char *)); + return base; +} + +static void +_ecore_getopt_desc_print_error(const Ecore_Getopt_Desc *desc, const char *fmt, ...) +{ + va_list ap; + + fputs(_("ERROR: "), stderr); + + if (desc->shortname) + { + fputc('-', stderr); + fputc(desc->shortname, stderr); + } + + if (desc->shortname && desc->longname) + fputs(", ", stderr); + + if (desc->longname) + { + fputs("--", stderr); + fputs(desc->longname, stderr); + } + + fputs(": ", stderr); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static unsigned char +_ecore_getopt_parse_bool(const char *str, unsigned char *v) +{ + if ((strcmp(str, "0") == 0) || + (strcasecmp(str, "f") == 0) || + (strcasecmp(str, "false") == 0) || + (strcasecmp(str, "no") == 0) || + (strcasecmp(str, "off") == 0) + ) + { + *v = 0; + return 1; + } + else if ((strcmp(str, "1") == 0) || + (strcasecmp(str, "t") == 0) || + (strcasecmp(str, "true") == 0) || + (strcasecmp(str, "yes") == 0) || + (strcasecmp(str, "on") == 0) + ) + { + *v = 1; + return 1; + } + + return 0; +} + +static unsigned char +_ecore_getopt_parse_long(const char *str, long int *v) +{ + char *endptr = NULL; + *v = strtol(str, &endptr, 0); + return endptr > str; +} + +static unsigned char +_ecore_getopt_parse_double(const char *str, double *v) +{ + char *endptr = NULL; + *v = strtod(str, &endptr); + return endptr > str; +} + +static unsigned char +_ecore_getopt_parse_store(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *value, const char *arg_val) +{ + const Ecore_Getopt_Desc_Store *store = &desc->action_param.store; + long int v; + double d; + unsigned char b; + + if (!value->ptrp) + { + _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n")); + return 0; + } + + switch (store->arg_req) + { + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO: + goto use_optional; + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL: + if (!arg_val) + goto use_optional; + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES: + break; + } + + switch (store->type) + { + case ECORE_GETOPT_TYPE_STR: + *value->strp = (char *)arg_val; + return 1; + case ECORE_GETOPT_TYPE_BOOL: + if (_ecore_getopt_parse_bool(arg_val, &b)) + { + *value->boolp = b; + return 1; + } + else + { + _ecore_getopt_desc_print_error + (desc, _("unknown boolean value %s.\n"), arg_val); + return 0; + } + case ECORE_GETOPT_TYPE_SHORT: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->shortp = v; + return 1; + case ECORE_GETOPT_TYPE_INT: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->intp = v; + return 1; + case ECORE_GETOPT_TYPE_LONG: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->longp = v; + return 1; + case ECORE_GETOPT_TYPE_USHORT: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->ushortp = v; + return 1; + case ECORE_GETOPT_TYPE_UINT: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->uintp = v; + return 1; + case ECORE_GETOPT_TYPE_ULONG: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->ulongp = v; + return 1; + case ECORE_GETOPT_TYPE_DOUBLE: + if (!_ecore_getopt_parse_double(arg_val, &d)) + goto error; + *value->doublep = d; + break; + } + + return 1; + + error: + _ecore_getopt_desc_print_error + (desc, _("invalid number format %s\n"), arg_val); + return 0; + + use_optional: + switch (store->type) + { + case ECORE_GETOPT_TYPE_STR: + *value->strp = (char *)store->def.strv; + break; + case ECORE_GETOPT_TYPE_BOOL: + *value->boolp = store->def.boolv; + break; + case ECORE_GETOPT_TYPE_SHORT: + *value->shortp = store->def.shortv; + break; + case ECORE_GETOPT_TYPE_INT: + *value->intp = store->def.intv; + break; + case ECORE_GETOPT_TYPE_LONG: + *value->longp = store->def.longv; + break; + case ECORE_GETOPT_TYPE_USHORT: + *value->ushortp = store->def.ushortv; + break; + case ECORE_GETOPT_TYPE_UINT: + *value->uintp = store->def.uintv; + break; + case ECORE_GETOPT_TYPE_ULONG: + *value->ulongp = store->def.ulongv; + break; + case ECORE_GETOPT_TYPE_DOUBLE: + *value->doublep = store->def.doublev; + break; + } + + return 1; +} + +static unsigned char +_ecore_getopt_parse_store_const(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__) +{ + if (!val->ptrp) + { + _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n")); + return 0; + } + + *val->ptrp = (void *)desc->action_param.store_const; + return 1; +} + +static unsigned char +_ecore_getopt_parse_store_true(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__) +{ + if (!val->boolp) + { + _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n")); + return 0; + } + *val->boolp = 1; + return 1; +} + +static unsigned char +_ecore_getopt_parse_store_false(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__) +{ + if (!val->boolp) + { + _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n")); + return 0; + } + *val->boolp = 0; + return 1; +} + +static unsigned char +_ecore_getopt_parse_choice(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + const char * const *pchoice; + + if (!val->strp) + { + _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n")); + return 0; + } + + pchoice = desc->action_param.choices; + for (; *pchoice; pchoice++) + if (strcmp(*pchoice, arg_val) == 0) + { + *val->strp = (char *)*pchoice; + return 1; + } + + _ecore_getopt_desc_print_error + (desc, _("invalid choice \"%s\". Valid values are: "), arg_val); + + pchoice = desc->action_param.choices; + for (; *pchoice; pchoice++) + { + fputs(*pchoice, stderr); + if (pchoice[1]) + fputs(", ", stderr); + } + + fputs(".\n", stderr); + return 0; +} + +static unsigned char +_ecore_getopt_parse_append(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + void *data; + long int v; + double d; + unsigned char b; + + if (!arg_val) + { + _ecore_getopt_desc_print_error + (desc, _("missing parameter to append.\n")); + return 0; + } + + if (!val->listp) + { + _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n")); + return 0; + } + + switch (desc->action_param.append_type) + { + case ECORE_GETOPT_TYPE_STR: + data = strdup(arg_val); + break; + case ECORE_GETOPT_TYPE_BOOL: + { + if (_ecore_getopt_parse_bool(arg_val, &b)) + { + data = malloc(sizeof(unsigned char)); + if (data) + *(unsigned char *)data = b; + } + else + { + _ecore_getopt_desc_print_error + (desc, _("unknown boolean value %s.\n"), arg_val); + return 0; + } + } + break; + case ECORE_GETOPT_TYPE_SHORT: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(short)); + if (data) + *(short *)data = (short)v; + } + break; + case ECORE_GETOPT_TYPE_INT: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(int)); + if (data) + *(int *)data = (int)v; + } + break; + case ECORE_GETOPT_TYPE_LONG: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(long)); + if (data) + *(long *)data = v; + } + break; + case ECORE_GETOPT_TYPE_USHORT: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(unsigned short)); + if (data) + *(unsigned short *)data = (unsigned short)v; + } + break; + case ECORE_GETOPT_TYPE_UINT: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(unsigned int)); + if (data) + *(unsigned int *)data = (unsigned int)v; + } + break; + case ECORE_GETOPT_TYPE_ULONG: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(unsigned long)); + if (data) + *(unsigned long *)data = v; + } + break; + case ECORE_GETOPT_TYPE_DOUBLE: + { + if (!_ecore_getopt_parse_double(arg_val, &d)) + goto error; + data = malloc(sizeof(double)); + if (data) + *(double *)data = d; + } + break; + default: + { + _ecore_getopt_desc_print_error(desc, _("could not parse value.\n")); + return 0; + } + } + + *val->listp = eina_list_append(*val->listp, data); + return 1; + + error: + _ecore_getopt_desc_print_error + (desc, _("invalid number format %s\n"), arg_val); + return 0; +} + +static unsigned char +_ecore_getopt_parse_count(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__) +{ + if (!val->intp) + { + _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n")); + return 0; + } + + (*val->intp)++; + return 1; +} + +static unsigned char +_ecore_getopt_parse_callback(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + const Ecore_Getopt_Desc_Callback *cb = &desc->action_param.callback; + + switch (cb->arg_req) + { + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO: + arg_val = cb->def; + break; + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL: + if (!arg_val) + arg_val = cb->def; + break; + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES: + break; + } + + if (cb->arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + { + if ((!arg_val) || (arg_val[0] == '\0')) + { + _ecore_getopt_desc_print_error(desc, _("missing parameter.\n")); + return 0; + } + + if (!val->ptrp) + { + _ecore_getopt_desc_print_error + (desc, _("value has no pointer set.\n")); + return 0; + } + } + + if (!cb->func) + { + _ecore_getopt_desc_print_error(desc, _("missing callback function!\n")); + return 0; + } + + return cb->func(parser, desc, arg_val, (void *)cb->data, val); +} + +static unsigned char +_ecore_getopt_parse_help(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc __UNUSED__, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__) +{ + if (val->boolp) + (*val->boolp) = 1; + ecore_getopt_help(stdout, parser); + return 1; +} + +static unsigned char +_ecore_getopt_parse_version(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__) +{ + if (val->boolp) + (*val->boolp) = 1; + if (!parser->version) + { + _ecore_getopt_desc_print_error(desc, _("no version was defined.\n")); + return 0; + } + _ecore_getopt_version(stdout, parser); + return 1; +} + +static unsigned char +_ecore_getopt_parse_copyright(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__) +{ + if (val->boolp) + (*val->boolp) = 1; + if (!parser->copyright) + { + _ecore_getopt_desc_print_error(desc, _("no copyright was defined.\n")); + return 0; + } + _ecore_getopt_copyright(stdout, parser); + return 1; +} + +static unsigned char +_ecore_getopt_parse_license(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__) +{ + if (val->boolp) + (*val->boolp) = 1; + if (!parser->license) + { + _ecore_getopt_desc_print_error(desc, _("no license was defined.\n")); + return 0; + } + _ecore_getopt_license(stdout, parser); + return 1; +} + +static unsigned char +_ecore_getopt_desc_handle(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *value, const char *arg_val) +{ + switch (desc->action) + { + case ECORE_GETOPT_ACTION_STORE: + return _ecore_getopt_parse_store(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_STORE_CONST: + return _ecore_getopt_parse_store_const(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_STORE_TRUE: + return _ecore_getopt_parse_store_true(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_STORE_FALSE: + return _ecore_getopt_parse_store_false(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_CHOICE: + return _ecore_getopt_parse_choice(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_APPEND: + return _ecore_getopt_parse_append(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_COUNT: + return _ecore_getopt_parse_count(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_CALLBACK: + return _ecore_getopt_parse_callback(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_HELP: + return _ecore_getopt_parse_help(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_VERSION: + return _ecore_getopt_parse_version(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_COPYRIGHT: + return _ecore_getopt_parse_copyright(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_LICENSE: + return _ecore_getopt_parse_license(parser, desc, value, arg_val); + default: + return 0; + } +} + +static unsigned char +_ecore_getopt_parse_arg_long(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc __UNUSED__, char **argv, int *idx, int *nonargs, const char *arg) +{ + const Ecore_Getopt_Desc *desc; + Ecore_Getopt_Desc_Arg_Requirement arg_req; + const char *arg_val; + int desc_idx; + Ecore_Getopt_Value *value; + unsigned char ret; + + desc = _ecore_getopt_parse_find_long(parser, arg); + if (!desc) + { + fprintf(stderr, _("ERROR: unknown option --%s, ignored.\n"), arg); + if (parser->strict) + return 0; + + (*idx)++; + return 1; + } + + (*idx)++; + + arg_req = _ecore_getopt_desc_arg_requirement(desc); + if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + { + arg_val = strchr(arg, '='); + if (arg_val) + arg_val++; + else + { + if ((*idx < *nonargs) && (argv[*idx][0] != '-')) + { + arg_val = argv[*idx]; + (*idx)++; + } + else + arg_val = NULL; + } + + if (arg_val && arg_val[0] == '\0') + arg_val = NULL; + + if ((!arg_val) && (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES)) + { + fprintf + (stderr, _("ERROR: option --%s requires an argument!\n"), arg); + if (parser->strict) + return 0; + return 1; + } + } + else + arg_val = NULL; + + desc_idx = desc - parser->descs; + value = values + desc_idx; + ret = _ecore_getopt_desc_handle(parser, desc, value, arg_val); + if ((!ret) && parser->strict) + return 0; + + return 1; +} + +static unsigned char +_ecore_getopt_parse_arg_short(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc __UNUSED__, char **argv, int *idx, int *nonargs, const char *arg) +{ + int run = 1; + while (run && (arg[0] != '\0')) + { + int opt = arg[0]; + const Ecore_Getopt_Desc *desc; + Ecore_Getopt_Desc_Arg_Requirement arg_req; + const char *arg_val; + int desc_idx; + Ecore_Getopt_Value *value; + unsigned char ret; + + desc = _ecore_getopt_parse_find_short(parser, arg[0]); + if (!desc) + { + fprintf + (stderr, _("ERROR: unknown option -%c, ignored.\n"), arg[0]); + if (parser->strict) + return 0; + + arg++; + continue; + } + + arg++; + + arg_req = _ecore_getopt_desc_arg_requirement(desc); + if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + { + (*idx)++; + run = 0; + + if (arg[0] == '=') + arg_val = arg + 1; + else if (arg[0] != '\0') + arg_val = arg; + else + { + if ((*idx < *nonargs) && (argv[*idx][0] != '-')) + { + arg_val = argv[*idx]; + (*idx)++; + } + else + arg_val = NULL; + } + + if (arg_val && arg_val[0] == '\0') + arg_val = NULL; + + if ((!arg_val) && + (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES)) + { + fprintf + (stderr, _("ERROR: option -%c requires an argument!\n"), + opt); + if (parser->strict) + return 0; + return 1; + } + } + else + arg_val = NULL; + + desc_idx = desc - parser->descs; + value = values + desc_idx; + ret = _ecore_getopt_desc_handle(parser, desc, value, arg_val); + if ((!ret) && parser->strict) + return 0; + } + + if (run) + (*idx)++; + + return 1; +} + +static unsigned char +_ecore_getopt_parse_arg(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv, int *idx, int *nonargs) +{ + char *arg = argv[*idx]; + + if (arg[0] != '-') + { + char **dst, **src, **src_end; + + dst = argv + *idx; + src = dst + 1; + src_end = src + *nonargs - *idx - 1; + + for (; src < src_end; src++, dst++) + *dst = *src; + + *dst = arg; + (*nonargs)--; + return 1; + } + + if (arg[1] == '-') + return _ecore_getopt_parse_arg_long + (parser, values, argc, argv, idx, nonargs, arg + 2); + else + return _ecore_getopt_parse_arg_short + (parser, values, argc, argv, idx, nonargs, arg + 1); +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_parse_find_short_other(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *orig) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + const char c = orig->shortname; + + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + { + if (desc == orig) + return NULL; + + if (c == desc->shortname) + return desc; + } + + return NULL; +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_parse_find_long_other(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *orig) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + const char *name = orig->longname; + + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + { + if (desc == orig) + return NULL; + + if (desc->longname && (strcmp(name, desc->longname) == 0)) + return desc; + } + + return NULL; +} + +/** + * Check parser for duplicate entries, print them out. + * + * @return 1 if there are duplicates, 0 otherwise. + */ +unsigned char +ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + { + if (desc->shortname) + { + const Ecore_Getopt_Desc *other; + other = _ecore_getopt_parse_find_short_other(parser, desc); + if (other) + { + _ecore_getopt_desc_print_error + (desc, "short name -%c already exists.", desc->shortname); + + if (other->longname) + fprintf(stderr, " Other is --%s.\n", other->longname); + else + fputc('\n', stderr); + return 1; + } + } + + if (desc->longname) + { + const Ecore_Getopt_Desc *other; + other = _ecore_getopt_parse_find_long_other(parser, desc); + if (other) + { + _ecore_getopt_desc_print_error + (desc, "long name --%s already exists.", desc->longname); + + if (other->shortname) + fprintf(stderr, " Other is -%c.\n", other->shortname); + else + fputc('\n', stderr); + return 1; + } + } + } + return 0; +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_find_help(const Ecore_Getopt *parser) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + if (desc->action == ECORE_GETOPT_ACTION_HELP) + return desc; + return NULL; +} + +/** + * Parse command line parameters. + * + * Walks the command line parameters and parse them based on @a parser + * description, doing actions based on @c parser->descs->action, like + * showing help text, license, copyright, storing values in values and + * so on. + * + * It is expected that values is of the same size than @c parser->descs, + * options that do not need a value it will be left untouched. + * + * All values are expected to be initialized before use. Options with + * action @c ECORE_GETOPT_ACTION_STORE and non required arguments + * (others than @c ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES), are expected + * to provide a value in @c def to be used. + * + * The following actions will store 1 on value as a boolean + * (@c value->boolp) if it's not NULL to indicate these actions were executed: + * - @c ECORE_GETOPT_ACTION_HELP + * - @c ECORE_GETOPT_ACTION_VERSION + * - @c ECORE_GETOPT_ACTION_COPYRIGHT + * - @c ECORE_GETOPT_ACTION_LICENSE + * + * Just @c ECORE_GETOPT_ACTION_APPEND will allocate memory and thus + * need to be freed. For consistency between all of appended subtypes, + * @c eina_list->data will contain an allocated memory with the value, + * that is, for @c ECORE_GETOPT_TYPE_STR it will contain a copy of the + * argument, @c ECORE_GETOPT_TYPE_INT a pointer to an allocated + * integer and so on. + * + * If parser is in strict mode (see @c Ecore_Getopt->strict), then any + * error will abort parsing and -1 is returned. Otherwise it will try + * to continue as far as possible. + * + * This function may reorder @a argv elements. + * + * Translation of help strings (description), metavar, usage, license + * and copyright may be translated, standard/global gettext() call + * will be applied on them if ecore was compiled with such support. + * + * @param parser description of how to work. + * @param value where to store values, it is assumed that this is a vector + * of the same size as @c parser->descs. Values should be previously + * initialized. + * @param argc how many elements in @a argv. If not provided it will be + * retrieved with ecore_app_args_get(). + * @param argv command line parameters. + * + * @return index of first non-option parameter or -1 on error. + */ +int +ecore_getopt_parse(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv) +{ + int i, nonargs; + + if (!parser) + { + fputs(_("ERROR: no parser provided.\n"), stderr); + return -1; + } + if (!values) + { + fputs(_("ERROR: no values provided.\n"), stderr); + return -1; + } + + if ((argc < 1) || (!argv)) + ecore_app_args_get(&argc, &argv); + + if (argc < 1) + { + fputs(_("ERROR: no arguments provided.\n"), stderr); + return -1; + } + + if (argv[0]) + prog = argv[0]; + else + prog = parser->prog; + + nonargs = _ecore_getopt_parse_find_nonargs_base(parser, argc, argv); + if (nonargs < 0) + goto error; + + if (nonargs > argc) + nonargs = argc; + + i = 1; + while (i < nonargs) + if (!_ecore_getopt_parse_arg(parser, values, argc, argv, &i, &nonargs)) + goto error; + + return nonargs; + + error: + { + const Ecore_Getopt_Desc *help; + fputs(_("ERROR: invalid options found."), stderr); + + help = _ecore_getopt_find_help(parser); + if (!help) + fputc('\n', stderr); + else if (help->longname) + fprintf(stderr, _(" See --%s.\n"), help->longname); + else + fprintf(stderr, _(" See -%c.\n"), help->shortname); + } + + return -1; +} + +/** + * Utility to free list and nodes allocated by @a ECORE_GETOPT_ACTION_APPEND. + * + * @param list pointer to list to be freed. + * @return always NULL, so you can easily make your list head NULL. + */ +Eina_List * +ecore_getopt_list_free(Eina_List *list) +{ + void *data; + + EINA_LIST_FREE(list, data) + free(data); + return NULL; +} + +/** + * Helper ecore_getopt callback to parse geometry (x:y:w:h). + * + * Storage must be a pointer to @c Eina_Rectangle and will be used to + * store the four values passed in the given string. + * + * @c callback_data value is ignored, you can safely use @c NULL. + */ +unsigned char +ecore_getopt_callback_geometry_parse(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc __UNUSED__, const char *str, void *data __UNUSED__, Ecore_Getopt_Value *storage) +{ + Eina_Rectangle *v = (Eina_Rectangle *)storage->ptrp; + + if (sscanf(str, "%d:%d:%d:%d", &v->x, &v->y, &v->w, &v->h) != 4) + { + fprintf(stderr, _("ERROR: incorrect geometry value '%s'\n"), str); + return 0; + } + + return 1; +} + +/** + * Helper ecore_getopt callback to parse geometry size (WxH). + * + * Storage must be a pointer to @c Eina_Rectangle and will be used to + * store the two values passed in the given string and 0 in the x and y + * fields. + * + * @c callback_data value is ignored, you can safely use @c NULL. + */ +unsigned char +ecore_getopt_callback_size_parse(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc __UNUSED__, const char *str, void *data __UNUSED__, Ecore_Getopt_Value *storage) +{ + Eina_Rectangle *v = (Eina_Rectangle *)storage->ptrp; + + if (sscanf(str, "%dx%d", &v->w, &v->h) != 2) + { + fprintf(stderr, _("ERROR: incorrect size value '%s'\n"), str); + return 0; + } + v->x = 0; + v->y = 0; + + return 1; +} diff --git a/tests/suite/ecore/src/lib/ecore_glib.c b/tests/suite/ecore/src/lib/ecore_glib.c new file mode 100644 index 0000000000..0972776ef0 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_glib.c @@ -0,0 +1,286 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#include "Ecore.h" +#include "ecore_private.h" + +#ifdef HAVE_GLIB +#include <glib.h> + +static Eina_Bool _ecore_glib_active = EINA_FALSE; +static Ecore_Select_Function _ecore_glib_select_original; +static GCond *_ecore_glib_cond = NULL; +static GPollFD *_ecore_glib_fds = NULL; +static size_t _ecore_glib_fds_size = 0; +static const size_t ECORE_GLIB_FDS_INITIAL = 128; +static const size_t ECORE_GLIB_FDS_STEP = 8; +static const size_t ECORE_GLIB_FDS_MAX_FREE = 256; + +static Eina_Bool +_ecore_glib_fds_resize(size_t size) +{ + void *tmp = realloc(_ecore_glib_fds, sizeof(GPollFD) * size); + + if (!tmp) + { + ERR("Could not realloc from %zu to %zu buckets.", + _ecore_glib_fds_size, size); + return EINA_FALSE; + } + + _ecore_glib_fds = tmp; + _ecore_glib_fds_size = size; + return EINA_TRUE; +} + +static int +_ecore_glib_context_query(GMainContext *ctx, int priority, int *p_timer) +{ + int reqfds; + + if (_ecore_glib_fds_size == 0) + { + if (!_ecore_glib_fds_resize(ECORE_GLIB_FDS_INITIAL)) return -1; + } + + while (1) + { + size_t size; + + reqfds = g_main_context_query + (ctx, priority, p_timer, _ecore_glib_fds, _ecore_glib_fds_size); + if (reqfds <= (int)_ecore_glib_fds_size) break; + + size = (1 + reqfds / ECORE_GLIB_FDS_STEP) * ECORE_GLIB_FDS_STEP; + if (!_ecore_glib_fds_resize(size)) return -1; + } + + if (reqfds + ECORE_GLIB_FDS_MAX_FREE < _ecore_glib_fds_size) + { + size_t size; + + size = (1 + reqfds / ECORE_GLIB_FDS_MAX_FREE) * ECORE_GLIB_FDS_MAX_FREE; + _ecore_glib_fds_resize(size); + } + + return reqfds; +} + +static int +_ecore_glib_context_poll_from(const GPollFD *pfds, int count, fd_set *rfds, fd_set *wfds, fd_set *efds) +{ + const GPollFD *itr = pfds, *itr_end = pfds + count; + int glib_fds = -1; + + for (; itr < itr_end; itr++) + { + if (glib_fds < itr->fd) + glib_fds = itr->fd; + + if (itr->events & G_IO_IN) + FD_SET(itr->fd, rfds); + if (itr->events & G_IO_OUT) + FD_SET(itr->fd, wfds); + if (itr->events & (G_IO_HUP | G_IO_ERR)) + FD_SET(itr->fd, efds); + } + + return glib_fds + 1; +} + +static int +_ecore_glib_context_poll_to(GPollFD *pfds, int count, const fd_set *rfds, const fd_set *wfds, const fd_set *efds, int ready) +{ + GPollFD *itr = pfds, *itr_end = pfds + count; + + for (; itr < itr_end && ready > 0; itr++) + { + itr->revents = 0; + if (FD_ISSET(itr->fd, rfds)) + { + itr->revents |= G_IO_IN; + ready--; + } + if (FD_ISSET(itr->fd, wfds)) + { + itr->revents |= G_IO_OUT; + ready--; + } + if (FD_ISSET(itr->fd, efds)) + { + itr->revents |= G_IO_ERR; + ready--; + } + } + return ready; +} + +static int +_ecore_glib_select__locked(GMainContext *ctx, int ecore_fds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *ecore_timeout) +{ + int priority, maxfds, glib_fds, reqfds, reqtimeout, ret; + struct timeval *timeout, glib_timeout; + + g_main_context_prepare(ctx, &priority); + reqfds = _ecore_glib_context_query(ctx, priority, &reqtimeout); + if (reqfds < 0) goto error; + + glib_fds = _ecore_glib_context_poll_from + (_ecore_glib_fds, reqfds, rfds, wfds, efds); + + if (reqtimeout == -1) + timeout = ecore_timeout; + else + { + glib_timeout.tv_sec = reqtimeout / 1000; + glib_timeout.tv_usec = (reqtimeout % 1000) * 1000; + + if (!ecore_timeout || timercmp(ecore_timeout, &glib_timeout, >)) + timeout = &glib_timeout; + else + timeout = ecore_timeout; + } + + maxfds = (ecore_fds >= glib_fds) ? ecore_fds : glib_fds; + ret = _ecore_glib_select_original(maxfds, rfds, wfds, efds, timeout); + + ret = _ecore_glib_context_poll_to + (_ecore_glib_fds, reqfds, rfds, wfds, efds, ret); + + if (g_main_context_check(ctx, priority, _ecore_glib_fds, reqfds)) + g_main_context_dispatch(ctx); + + return ret; + + error: + return _ecore_glib_select_original + (ecore_fds, rfds, wfds, efds, ecore_timeout); +} + +static int +_ecore_glib_select(int ecore_fds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *ecore_timeout) +{ + GStaticMutex lock = G_STATIC_MUTEX_INIT; + GMutex *mutex = g_static_mutex_get_mutex(&lock); + GMainContext *ctx = g_main_context_default(); + int ret; + + if (g_main_context_acquire(ctx)) + g_mutex_lock(mutex); + else + { + if (!_ecore_glib_cond) + _ecore_glib_cond = g_cond_new(); + + while (!g_main_context_wait(ctx, _ecore_glib_cond, mutex)) + g_thread_yield(); + } + + ret = _ecore_glib_select__locked + (ctx, ecore_fds, rfds, wfds, efds, ecore_timeout); + + g_mutex_unlock(mutex); + g_main_context_release(ctx); + + return ret; +} +#endif + +void +_ecore_glib_init(void) +{ +} + +void +_ecore_glib_shutdown(void) +{ +#ifdef HAVE_GLIB + if (!_ecore_glib_active) return; + _ecore_glib_active = EINA_FALSE; + + if (ecore_main_loop_select_func_get() == _ecore_glib_select) + ecore_main_loop_select_func_set(_ecore_glib_select_original); + + if (_ecore_glib_fds) + { + free(_ecore_glib_fds); + _ecore_glib_fds = NULL; + } + _ecore_glib_fds_size = 0; + + if (_ecore_glib_cond) + { + g_cond_free(_ecore_glib_cond); + _ecore_glib_cond = NULL; + } +#endif +} + +/** + * Request ecore to integrate GLib's main loop. + * + * This will add a small overhead during every main loop interaction + * by checking glib's default main context (used by its main loop). If + * it have events to be checked (timers, file descriptors or idlers), + * then these will be polled alongside with Ecore's own events, then + * dispatched before Ecore's. This is done by calling + * ecore_main_loop_select_func_set(). + * + * This will cooperate with previously set + * ecore_main_loop_select_func_set() by calling the old + * function. Similarly, if you want to override + * ecore_main_loop_select_func_set() after main loop is integrated, + * call the new select function set by this call (get it by calling + * ecore_main_loop_select_func_get() right after + * ecore_main_loop_glib_integrate()). + * + * This is useful to use GMainLoop libraries, like GTK, GUPnP, + * LibSoup, GConf and more. Adobe Flash plugin and other plugins + * systems depend on this as well. + * + * Once initialized/integrated, it will be valid until Ecore is + * completely shut down. + * + * @note this is only available if Ecore was compiled with GLib support. + * + * @return @c EINA_TRUE on success of @c EINA_FALSE if it failed, + * likely no GLib support in Ecore. + */ +EAPI Eina_Bool +ecore_main_loop_glib_integrate(void) +{ +#ifdef HAVE_GLIB + void *func; + + if (_ecore_glib_active) return EINA_TRUE; + func = ecore_main_loop_select_func_get(); + if (func == _ecore_glib_select) return EINA_TRUE; + _ecore_glib_select_original = func; + ecore_main_loop_select_func_set(_ecore_glib_select); + _ecore_glib_active = EINA_TRUE; + return EINA_TRUE; +#else + fputs("ERROR: no glib support in ecore.\n", stderr); + return EINA_FALSE; +#endif +} + +Eina_Bool _ecore_glib_always_integrate = 1; + +/** + * Disable always integrating glib + * + * If ecore is compiled with --enable-glib-integration-always (to always + * call ecore_main_loop_glib_integrate() when ecore_init() is called), then + * calling this before calling ecore_init() will disable the integration. + * This is for apps that explicitly do not want this to happen for whatever + * reasons they may have. + */ +EAPI void +ecore_main_loop_glib_always_integrate_disable(void) +{ + _ecore_glib_always_integrate = 0; +} diff --git a/tests/suite/ecore/src/lib/ecore_idle_enterer.c b/tests/suite/ecore/src/lib/ecore_idle_enterer.c new file mode 100644 index 0000000000..2b827ce93e --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_idle_enterer.c @@ -0,0 +1,171 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +#include "Ecore.h" +#include "ecore_private.h" + + +struct _Ecore_Idle_Enterer +{ + EINA_INLIST; + ECORE_MAGIC; + Ecore_Task_Cb func; + void *data; + int references; + Eina_Bool delete_me : 1; +}; + + +static Ecore_Idle_Enterer *idle_enterers = NULL; +static Ecore_Idle_Enterer *idle_enterer_current = NULL; +static int idle_enterers_delete_me = 0; + +/** + * Add an idle enterer handler. + * @param func The function to call when entering an idle state. + * @param data The data to be passed to the @p func call + * @return A handle to the idle enterer callback if successful. Otherwise, + * NULL is returned. + * @ingroup Idle_Group + */ +EAPI Ecore_Idle_Enterer * +ecore_idle_enterer_add(Ecore_Task_Cb func, const void *data) +{ + Ecore_Idle_Enterer *ie; + + if (!func) return NULL; + ie = calloc(1, sizeof(Ecore_Idle_Enterer)); + if (!ie) return NULL; + ECORE_MAGIC_SET(ie, ECORE_MAGIC_IDLE_ENTERER); + ie->func = func; + ie->data = (void *)data; + idle_enterers = (Ecore_Idle_Enterer *) eina_inlist_append(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie)); + return ie; +} + +/** + * Add an idle enterer handler at the start of the list so it gets called earlier than others. + * @param func The function to call when entering an idle state. + * @param data The data to be passed to the @p func call + * @return A handle to the idle enterer callback if successful. Otherwise, + * NULL is returned. + * @ingroup Idle_Group + */ +EAPI Ecore_Idle_Enterer * +ecore_idle_enterer_before_add(Ecore_Task_Cb func, const void *data) +{ + Ecore_Idle_Enterer *ie; + + if (!func) return NULL; + ie = calloc(1, sizeof(Ecore_Idle_Enterer)); + if (!ie) return NULL; + ECORE_MAGIC_SET(ie, ECORE_MAGIC_IDLE_ENTERER); + ie->func = func; + ie->data = (void *)data; + idle_enterers = (Ecore_Idle_Enterer *) eina_inlist_prepend(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie)); + return ie; +} + +/** + * Delete an idle enterer callback. + * @param idle_enterer The idle enterer to delete + * @return The data pointer passed to the idler enterer callback on success. + * NULL otherwise. + * @ingroup Idle_Group + */ +EAPI void * +ecore_idle_enterer_del(Ecore_Idle_Enterer *idle_enterer) +{ + if (!ECORE_MAGIC_CHECK(idle_enterer, ECORE_MAGIC_IDLE_ENTERER)) + { + ECORE_MAGIC_FAIL(idle_enterer, ECORE_MAGIC_IDLE_ENTERER, + "ecore_idle_enterer_del"); + return NULL; + } + EINA_SAFETY_ON_TRUE_RETURN_VAL(idle_enterer->delete_me, NULL); + idle_enterer->delete_me = 1; + idle_enterers_delete_me = 1; + return idle_enterer->data; +} + +void +_ecore_idle_enterer_shutdown(void) +{ + Ecore_Idle_Enterer *ie; + while ((ie = idle_enterers)) + { + idle_enterers = (Ecore_Idle_Enterer *) eina_inlist_remove(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(idle_enterers)); + ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE); + free(ie); + } + idle_enterers_delete_me = 0; + idle_enterer_current = NULL; +} + +void +_ecore_idle_enterer_call(void) +{ + if (!idle_enterer_current) + { + /* regular main loop, start from head */ + idle_enterer_current = idle_enterers; + } + else + { + /* recursive main loop, continue from where we were */ + idle_enterer_current = + (Ecore_Idle_Enterer *)EINA_INLIST_GET(idle_enterer_current)->next; + } + + while (idle_enterer_current) + { + Ecore_Idle_Enterer *ie = (Ecore_Idle_Enterer *)idle_enterer_current; + if (!ie->delete_me) + { + ie->references++; + if (!ie->func(ie->data)) + { + if (!ie->delete_me) ecore_idle_enterer_del(ie); + } + ie->references--; + } + if (idle_enterer_current) /* may have changed in recursive main loops */ + idle_enterer_current = + (Ecore_Idle_Enterer *)EINA_INLIST_GET(idle_enterer_current)->next; + } + if (idle_enterers_delete_me) + { + Ecore_Idle_Enterer *l; + int deleted_idler_enterers_in_use = 0; + + for (l = idle_enterers; l;) + { + Ecore_Idle_Enterer *ie = l; + l = (Ecore_Idle_Enterer *) EINA_INLIST_GET(l)->next; + if (ie->delete_me) + { + if (ie->references) + { + deleted_idler_enterers_in_use++; + continue; + } + + idle_enterers = (Ecore_Idle_Enterer *) eina_inlist_remove(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie)); + ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE); + free(ie); + } + } + if (!deleted_idler_enterers_in_use) + idle_enterers_delete_me = 0; + } +} + +int +_ecore_idle_enterer_exist(void) +{ + if (idle_enterers) return 1; + return 0; +} diff --git a/tests/suite/ecore/src/lib/ecore_idle_exiter.c b/tests/suite/ecore/src/lib/ecore_idle_exiter.c new file mode 100644 index 0000000000..d8234e3001 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_idle_exiter.c @@ -0,0 +1,148 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +#include "Ecore.h" +#include "ecore_private.h" + + +struct _Ecore_Idle_Exiter +{ + EINA_INLIST; + ECORE_MAGIC; + Ecore_Task_Cb func; + void *data; + int references; + Eina_Bool delete_me : 1; +}; + + +static Ecore_Idle_Exiter *idle_exiters = NULL; +static Ecore_Idle_Exiter *idle_exiter_current = NULL; +static int idle_exiters_delete_me = 0; + +/** + * Add an idle exiter handler. + * @param func The function to call when exiting an idle state. + * @param data The data to be passed to the @p func call + * @return A handle to the idle exiter callback on success. NULL otherwise. + * @ingroup Idle_Group + */ +EAPI Ecore_Idle_Exiter * +ecore_idle_exiter_add(Ecore_Task_Cb func, const void *data) +{ + Ecore_Idle_Exiter *ie; + + if (!func) return NULL; + ie = calloc(1, sizeof(Ecore_Idle_Exiter)); + if (!ie) return NULL; + ECORE_MAGIC_SET(ie, ECORE_MAGIC_IDLE_EXITER); + ie->func = func; + ie->data = (void *)data; + idle_exiters = (Ecore_Idle_Exiter *) eina_inlist_append(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(ie)); + return ie; +} + +/** + * Delete an idle exiter handler from the list to be run on exiting idle state. + * @param idle_exiter The idle exiter to delete + * @return The data pointer that was being being passed to the handler if + * successful. NULL otherwise. + * @ingroup Idle_Group + */ +EAPI void * +ecore_idle_exiter_del(Ecore_Idle_Exiter *idle_exiter) +{ + if (!ECORE_MAGIC_CHECK(idle_exiter, ECORE_MAGIC_IDLE_EXITER)) + { + ECORE_MAGIC_FAIL(idle_exiter, ECORE_MAGIC_IDLE_EXITER, + "ecore_idle_exiter_del"); + return NULL; + } + EINA_SAFETY_ON_TRUE_RETURN_VAL(idle_exiter->delete_me, NULL); + idle_exiter->delete_me = 1; + idle_exiters_delete_me = 1; + return idle_exiter->data; +} + +void +_ecore_idle_exiter_shutdown(void) +{ + Ecore_Idle_Exiter *ie; + while ((ie = idle_exiters)) + { + idle_exiters = (Ecore_Idle_Exiter *) eina_inlist_remove(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(idle_exiters)); + ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE); + free(ie); + } + idle_exiters_delete_me = 0; + idle_exiter_current = NULL; +} + +void +_ecore_idle_exiter_call(void) +{ + if (!idle_exiter_current) + { + /* regular main loop, start from head */ + idle_exiter_current = idle_exiters; + } + else + { + /* recursive main loop, continue from where we were */ + idle_exiter_current = + (Ecore_Idle_Exiter *)EINA_INLIST_GET(idle_exiter_current)->next; + } + + while (idle_exiter_current) + { + Ecore_Idle_Exiter *ie = (Ecore_Idle_Exiter *)idle_exiter_current; + if (!ie->delete_me) + { + ie->references++; + if (!ie->func(ie->data)) + { + if (!ie->delete_me) ecore_idle_exiter_del(ie); + } + ie->references--; + } + if (idle_exiter_current) /* may have changed in recursive main loops */ + idle_exiter_current = + (Ecore_Idle_Exiter *)EINA_INLIST_GET(idle_exiter_current)->next; + } + if (idle_exiters_delete_me) + { + Ecore_Idle_Exiter *l; + int deleted_idler_exiters_in_use = 0; + + for (l = idle_exiters; l;) + { + Ecore_Idle_Exiter *ie = l; + + l = (Ecore_Idle_Exiter *) EINA_INLIST_GET(l)->next; + if (ie->delete_me) + { + if (ie->references) + { + deleted_idler_exiters_in_use++; + continue; + } + + idle_exiters = (Ecore_Idle_Exiter *) eina_inlist_remove(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(ie)); + ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE); + free(ie); + } + } + if (!deleted_idler_exiters_in_use) + idle_exiters_delete_me = 0; + } +} + +int +_ecore_idle_exiter_exist(void) +{ + if (idle_exiters) return 1; + return 0; +} diff --git a/tests/suite/ecore/src/lib/ecore_idler.c b/tests/suite/ecore/src/lib/ecore_idler.c new file mode 100644 index 0000000000..8f1c8206b6 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_idler.c @@ -0,0 +1,154 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +#include "Ecore.h" +#include "ecore_private.h" + + +struct _Ecore_Idler +{ + EINA_INLIST; + ECORE_MAGIC; + Ecore_Task_Cb func; + void *data; + int references; + Eina_Bool delete_me : 1; +}; + + +static Ecore_Idler *idlers = NULL; +static Ecore_Idler *idler_current = NULL; +static int idlers_delete_me = 0; + +/** + * Add an idler handler. + * @param func The function to call when idling. + * @param data The data to be passed to this @p func call. + * @return A idler handle if successfully added. NULL otherwise. + * @ingroup Idle_Group + * + * Add an idler handle to the event loop, returning a handle on success and + * NULL otherwise. The function @p func will be called repeatedly while + * no other events are ready to be processed, as long as it returns 1 + * (or ECORE_CALLBACK_RENEW). A return of 0 (or ECORE_CALLBACK_CANCEL) deletes + * the idler. + * + * Idlers are useful for progressively prossessing data without blocking. + */ +EAPI Ecore_Idler * +ecore_idler_add(Ecore_Task_Cb func, const void *data) +{ + Ecore_Idler *ie; + + if (!func) return NULL; + ie = calloc(1, sizeof(Ecore_Idler)); + if (!ie) return NULL; + ECORE_MAGIC_SET(ie, ECORE_MAGIC_IDLER); + ie->func = func; + ie->data = (void *)data; + idlers = (Ecore_Idler *) eina_inlist_append(EINA_INLIST_GET(idlers), EINA_INLIST_GET(ie)); + return ie; +} + +/** + * Delete an idler callback from the list to be executed. + * @param idler The handle of the idler callback to delete + * @return The data pointer passed to the idler callback on success. NULL + * otherwise. + * @ingroup Idle_Group + */ +EAPI void * +ecore_idler_del(Ecore_Idler *idler) +{ + if (!ECORE_MAGIC_CHECK(idler, ECORE_MAGIC_IDLER)) + { + ECORE_MAGIC_FAIL(idler, ECORE_MAGIC_IDLER, + "ecore_idler_del"); + return NULL; + } + EINA_SAFETY_ON_TRUE_RETURN_VAL(idler->delete_me, NULL); + idler->delete_me = 1; + idlers_delete_me = 1; + return idler->data; +} + +void +_ecore_idler_shutdown(void) +{ + Ecore_Idler *ie; + while ((ie = idlers)) + { + idlers = (Ecore_Idler *) eina_inlist_remove(EINA_INLIST_GET(idlers), EINA_INLIST_GET(idlers)); + ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE); + free(ie); + } + idlers_delete_me = 0; + idler_current = NULL; +} + +int +_ecore_idler_call(void) +{ + if (!idler_current) + { + /* regular main loop, start from head */ + idler_current = idlers; + } + else + { + /* recursive main loop, continue from where we were */ + idler_current = (Ecore_Idler *)EINA_INLIST_GET(idler_current)->next; + } + + while (idler_current) + { + Ecore_Idler *ie = (Ecore_Idler *)idler_current; + if (!ie->delete_me) + { + ie->references++; + if (!ie->func(ie->data)) + { + if (!ie->delete_me) ecore_idler_del(ie); + } + ie->references--; + } + if (idler_current) /* may have changed in recursive main loops */ + idler_current = (Ecore_Idler *)EINA_INLIST_GET(idler_current)->next; + } + if (idlers_delete_me) + { + Ecore_Idler *l; + int deleted_idlers_in_use = 0; + for (l = idlers; l;) + { + Ecore_Idler *ie = l; + l = (Ecore_Idler *) EINA_INLIST_GET(l)->next; + if (ie->delete_me) + { + if (ie->references) + { + deleted_idlers_in_use++; + continue; + } + + idlers = (Ecore_Idler *) eina_inlist_remove(EINA_INLIST_GET(idlers), EINA_INLIST_GET(ie)); + ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE); + free(ie); + } + } + if (!deleted_idlers_in_use) + idlers_delete_me = 0; + } + if (idlers) return 1; + return 0; +} + +int +_ecore_idler_exist(void) +{ + if (idlers) return 1; + return 0; +} diff --git a/tests/suite/ecore/src/lib/ecore_job.c b/tests/suite/ecore/src/lib/ecore_job.c new file mode 100644 index 0000000000..cd519f732c --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_job.c @@ -0,0 +1,106 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +#include "Ecore.h" +#include "ecore_private.h" + +static Eina_Bool _ecore_job_event_handler(void *data, int type, void *ev); +static void _ecore_job_event_free(void *data, void *ev); + +static int ecore_event_job_type = 0; +static Ecore_Event_Handler* _ecore_job_handler = NULL; + +struct _Ecore_Job +{ + ECORE_MAGIC; + Ecore_Event *event; + Ecore_Cb func; + void *data; +}; + +void +_ecore_job_init(void) +{ + ecore_event_job_type = ecore_event_type_new(); + _ecore_job_handler = ecore_event_handler_add(ecore_event_job_type, _ecore_job_event_handler, NULL); +} + +void +_ecore_job_shutdown(void) +{ + ecore_event_handler_del(_ecore_job_handler); + _ecore_job_handler = NULL; +} + +/** + * Add a job to the event queue. + * @param func The function to call when the job gets handled. + * @param data Data pointer to be passed to the job function when the job is + * handled. + * @return The handle of the job. @c NULL is returned if the job could not be + * added to the queue. + * @ingroup Ecore_Job_Group + * @note Once the job has been executed, the job handle is invalid. + */ +EAPI Ecore_Job * +ecore_job_add(Ecore_Cb func, const void *data) +{ + Ecore_Job *job; + + if (!func) return NULL; + + job = calloc(1, sizeof(Ecore_Job)); + if (!job) return NULL; + ECORE_MAGIC_SET(job, ECORE_MAGIC_JOB); + job->event = ecore_event_add(ecore_event_job_type, job, _ecore_job_event_free, NULL); + if (!job->event) + { + free(job); + return NULL; + } + job->func = func; + job->data = (void *)data; + return job; +} + +/** + * Delete a queued job that has not yet been executed. + * @param job Handle of the job to delete. + * @return The data pointer that was to be passed to the job. + * @ingroup Ecore_Job_Group + */ +EAPI void * +ecore_job_del(Ecore_Job *job) +{ + void *data; + + if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_JOB)) + { + ECORE_MAGIC_FAIL(job, ECORE_MAGIC_JOB, + "ecore_job_del"); + return NULL; + } + data = job->data; + ECORE_MAGIC_SET(job, ECORE_MAGIC_NONE); + ecore_event_del(job->event); + return data; +} + +static Eina_Bool +_ecore_job_event_handler(void *data __UNUSED__, int type __UNUSED__, void *ev) +{ + Ecore_Job *job; + + job = ev; + job->func(job->data); + return ECORE_CALLBACK_CANCEL; +} + +static void +_ecore_job_event_free(void *data __UNUSED__, void *ev) +{ + free(ev); +} diff --git a/tests/suite/ecore/src/lib/ecore_main.c b/tests/suite/ecore/src/lib/ecore_main.c new file mode 100644 index 0000000000..de507dae0d --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_main.c @@ -0,0 +1,1453 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include <winsock2.h> +# undef WIN32_LEAN_AND_MEAN +# ifndef USER_TIMER_MINIMUM +# define USER_TIMER_MINIMUM 0x0a +# endif +#endif + +#ifdef __SUNPRO_C +# include <ieeefp.h> +# include <string.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> + +#ifndef _MSC_VER +#include <sys/time.h> +# include <unistd.h> +#else +# include <float.h> +#endif + +#define FIX_HZ 1 + +#ifdef FIX_HZ +# ifndef _MSC_VER +# include <sys/param.h> +# endif +# ifndef HZ +# define HZ 100 +# endif +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "Ecore.h" +#include "ecore_private.h" + +#ifdef HAVE_SYS_EPOLL_H +# define HAVE_EPOLL +# include <sys/epoll.h> +#endif + +#ifdef USE_G_MAIN_LOOP +#include <glib.h> +#endif + +struct _Ecore_Fd_Handler +{ + EINA_INLIST; + ECORE_MAGIC; + int fd; + Ecore_Fd_Handler_Flags flags; + Ecore_Fd_Cb func; + void *data; + Ecore_Fd_Cb buf_func; + void *buf_data; + Ecore_Fd_Prep_Cb prep_func; + void *prep_data; + int references; + Eina_Bool read_active : 1; + Eina_Bool write_active : 1; + Eina_Bool error_active : 1; + Eina_Bool delete_me : 1; +}; + +#ifdef _WIN32 +struct _Ecore_Win32_Handler +{ + EINA_INLIST; + ECORE_MAGIC; + HANDLE h; + Ecore_Fd_Win32_Cb func; + void *data; + int references; + Eina_Bool delete_me : 1; +}; +#endif + + +static int _ecore_main_select(double timeout); +static void _ecore_main_prepare_handlers(void); +static void _ecore_main_fd_handlers_cleanup(void); +#ifndef _WIN32 +static void _ecore_main_fd_handlers_bads_rem(void); +#endif +static void _ecore_main_fd_handlers_call(void); +static int _ecore_main_fd_handlers_buf_call(void); +#ifndef USE_G_MAIN_LOOP +static void _ecore_main_loop_iterate_internal(int once_only); +#endif + +#ifdef _WIN32 +static int _ecore_main_win32_select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); +static void _ecore_main_win32_handlers_cleanup(void); +#endif + +static int in_main_loop = 0; +static int do_quit = 0; +static Ecore_Fd_Handler *fd_handlers = NULL; +static Ecore_Fd_Handler *fd_handler_current = NULL; +static int fd_handlers_delete_me = 0; +#ifdef _WIN32 +static Ecore_Win32_Handler *win32_handlers = NULL; +static Ecore_Win32_Handler *win32_handler_current = NULL; +static int win32_handlers_delete_me = 0; +#endif + +#ifdef _WIN32 +static Ecore_Select_Function main_loop_select = _ecore_main_win32_select; +#else +static Ecore_Select_Function main_loop_select = select; +#endif + +static double t1 = 0.0; +static double t2 = 0.0; + +#ifdef HAVE_EPOLL +static int epoll_fd = -1; +#endif + +#ifdef USE_G_MAIN_LOOP +static GSource *ecore_epoll_source; +static GPollFD ecore_epoll_fd; +static guint ecore_epoll_id; +static GMainLoop* ecore_main_loop; +static gboolean ecore_idling; +static gboolean ecore_fds_ready; +#endif + +#ifdef HAVE_EPOLL +static inline int _ecore_poll_events_from_fdh(Ecore_Fd_Handler *fdh) +{ + int events = 0; + if (fdh->flags & ECORE_FD_READ) events |= EPOLLIN; + if (fdh->flags & ECORE_FD_WRITE) events |= EPOLLOUT; + if (fdh->flags & ECORE_FD_ERROR) events |= EPOLLERR; + return events; +} +#else +static inline int _ecore_poll_events_from_fdh(Ecore_Fd_Handler *fdh __UNUSED__) +{ + return 0; +} +#endif + +#ifdef HAVE_EPOLL +static inline int _ecore_main_fdh_epoll_add(Ecore_Fd_Handler *fdh) +{ + int r = 0; + struct epoll_event ev; + + memset(&ev, 0, sizeof (ev)); + ev.events = _ecore_poll_events_from_fdh(fdh); + ev.data.ptr = fdh; + INF("adding poll on %d %08x", fdh->fd, ev.events); + r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fdh->fd, &ev); + return r; +} +#else +static inline int _ecore_main_fdh_epoll_add(Ecore_Fd_Handler *fdh __UNUSED__) +{ + return 0; +} +#endif + +#ifdef HAVE_EPOLL +static inline void _ecore_main_fdh_epoll_del(Ecore_Fd_Handler *fdh) +{ + struct epoll_event ev; + + memset(&ev, 0, sizeof (ev)); + INF("removing poll on %d", fdh->fd); + /* could get an EBADF if somebody closed the FD before removing it */ + if ((epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fdh->fd, &ev) < 0) && + (errno != EBADF)) + { + ERR("Failed to delete epoll fd %d! (errno=%d)", fdh->fd, errno); + } +} +#else +static inline void _ecore_main_fdh_epoll_del(Ecore_Fd_Handler *fdh __UNUSED__) +{ +} +#endif + +#ifdef HAVE_EPOLL +static inline int _ecore_main_fdh_epoll_modify(Ecore_Fd_Handler *fdh) +{ + int r = 0; + struct epoll_event ev; + + memset(&ev, 0, sizeof (ev)); + ev.events = _ecore_poll_events_from_fdh(fdh); + ev.data.ptr = fdh; + INF("modifing epoll on %d to %08x", fdh->fd, ev.events); + r = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fdh->fd, &ev); + return r; +} +#else +static inline int _ecore_main_fdh_epoll_modify(Ecore_Fd_Handler *fdh __UNUSED__) +{ + return 0; +} +#endif + +#ifdef HAVE_EPOLL +static inline int _ecore_main_fdh_epoll_mark_active(void) +{ + struct epoll_event ev[32]; + int i, ret; + + memset(&ev, 0, sizeof (ev)); + ret = epoll_wait(epoll_fd, ev, sizeof(ev) / sizeof(struct epoll_event), 0); + if (ret < 0) + { + if (errno == EINTR) return -1; + ERR("epoll_wait failed %d", errno); + return -1; + } + + for (i = 0; i < ret; i++) + { + Ecore_Fd_Handler *fdh; + + fdh = ev[i].data.ptr; + if (!ECORE_MAGIC_CHECK(fdh, ECORE_MAGIC_FD_HANDLER)) + { + ECORE_MAGIC_FAIL(fdh, ECORE_MAGIC_FD_HANDLER, + "_ecore_main_fdh_epoll_mark_active"); + continue; + } + if (fdh->delete_me) + { + ERR("deleted fd in epoll"); + continue; + } + if (ev->events & EPOLLIN) + fdh->read_active = 1; + if (ev->events & EPOLLOUT) + fdh->write_active = 1; + if (ev->events & EPOLLERR) + fdh->error_active = 1; + } + + return ret; +} +#endif + +#ifdef USE_G_MAIN_LOOP + +/* like we are about to enter main_loop_select in _ecore_main_select */ +static gboolean +_ecore_main_gsource_prepare(GSource *source, gint *next_time) +{ + double t = _ecore_timer_next_get(); + gboolean running; + + INF("enter, next timeout in %.1f", t); + in_main_loop++; + + if (!ecore_idling) + { + while (_ecore_timer_call(_ecore_time_loop_time)); + _ecore_timer_cleanup(); + + /* when idling, busy loop checking the fds only */ + if (!ecore_idling) _ecore_idle_enterer_call(); + } + + /* don't check fds if somebody quit */ + running = g_main_loop_is_running(ecore_main_loop); + if (running) + { + /* only set idling state in dispatch */ + if (ecore_idling && !_ecore_idler_exist()) + { + if (_ecore_timers_exists()) + { + double t = _ecore_timer_next_get(); + *next_time = (t / 1000.0); + } + else + *next_time = -1; + } + else + *next_time = 0; + + _ecore_main_prepare_handlers(); + } + + in_main_loop--; + INF("leave, timeout = %d", *next_time); + + /* ready if we're not running (about to quit) */ + return !running; +} + +static gboolean +_ecore_main_gsource_check(GSource *source) +{ + INF("enter"); + in_main_loop++; + + ecore_fds_ready = (_ecore_main_fdh_epoll_mark_active() > 0); + _ecore_main_fd_handlers_cleanup(); + + _ecore_time_loop_time = ecore_time_get(); + _ecore_timer_enable_new(); + + in_main_loop--; + INF("leave"); + + return TRUE; /* always dispatch */ +} + +/* like we just came out of main_loop_select in _ecore_main_select */ +static gboolean +_ecore_main_gsource_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) +{ + gboolean events_ready, timers_ready, idlers_ready, signals_ready; + double next_time = _ecore_timer_next_get(); + + events_ready = _ecore_event_exist(); + timers_ready = _ecore_timers_exists() && (0.0 <= next_time); + idlers_ready = _ecore_idler_exist(); + signals_ready = (_ecore_signal_count_get() > 0); + + in_main_loop++; + INF("enter idling=%d fds=%d events=%d signals=%d timers=%d (next=%.2f) idlers=%d", + ecore_idling, ecore_fds_ready, events_ready, signals_ready, + _ecore_timers_exists(), next_time, idlers_ready); + + if (ecore_idling && events_ready) + { + INF("calling idle exiters"); + _ecore_idle_exiter_call(); + ecore_idling = 0; + } + else if (!ecore_idling && !events_ready) + { + INF("start idling"); + ecore_idling = 1; + } + + if (ecore_idling) + { + INF("calling idler"); + _ecore_idler_call(); + + events_ready = _ecore_event_exist(); + timers_ready = _ecore_timers_exists() && (0.0 <= next_time); + idlers_ready = _ecore_idler_exist(); + + if ((ecore_fds_ready || events_ready || timers_ready || idlers_ready || signals_ready)) + { + INF("calling idle exiters"); + _ecore_idle_exiter_call(); + ecore_idling = 0; + } + } + + /* process events */ + if (!ecore_idling) + { + INF("work"); + _ecore_main_fd_handlers_call(); + _ecore_main_fd_handlers_buf_call(); + while (_ecore_signal_count_get()) _ecore_signal_call(); + _ecore_event_call(); + _ecore_main_fd_handlers_cleanup(); + } + + in_main_loop--; + + INF("leave"); + + return TRUE; /* what should be returned here? */ +} + +static void +_ecore_main_gsource_finalize(GSource *source) +{ + INF("finalize"); +} + +static GSourceFuncs ecore_gsource_funcs = +{ + .prepare = _ecore_main_gsource_prepare, + .check = _ecore_main_gsource_check, + .dispatch = _ecore_main_gsource_dispatch, + .finalize = _ecore_main_gsource_finalize, +}; + +#endif + +void +_ecore_main_loop_init(void) +{ + INF("enter"); +#ifdef HAVE_EPOLL + epoll_fd = epoll_create(1); + if (epoll_fd < 0) + CRIT("Failed to create epoll fd!"); +#endif + +#ifdef USE_G_MAIN_LOOP + ecore_epoll_source = g_source_new(&ecore_gsource_funcs, sizeof (GSource)); + if (!ecore_epoll_source) + CRIT("Failed to create glib source for epoll!"); + else + { + ecore_epoll_fd.fd = epoll_fd; + ecore_epoll_fd.events = G_IO_IN; + ecore_epoll_fd.revents = 0; + g_source_add_poll(ecore_epoll_source, &ecore_epoll_fd); + ecore_epoll_id = g_source_attach(ecore_epoll_source, NULL); + if (ecore_epoll_id <= 0) + CRIT("Failed to attach glib source to default context"); + } +#endif + INF("leave"); +} + +void +_ecore_main_loop_shutdown(void) +{ +#ifdef USE_G_MAIN_LOOP + if (ecore_epoll_source) + { + g_source_destroy(ecore_epoll_source); + ecore_epoll_source = NULL; + } +#endif + +#ifdef HAVE_EPOLL + if (epoll_fd >= 0) + { + close(epoll_fd); + epoll_fd = -1; + } +#endif +} + + +/** + * @defgroup Ecore_Main_Loop_Group Main Loop Functions + * + * These functions control the Ecore event handling loop. This loop is + * designed to work on embedded systems all the way to large and + * powerful mutli-cpu workstations. + * + * It serialises all system signals and events into a single event + * queue, that can be easily processed without needing to worry about + * concurrency. A properly written, event-driven program using this + * kind of programming does not need threads. It makes the program very + * robust and easy to follow. + * + * Here is an example of simple program and its basic event loop flow: + * @image html prog_flow.png + * + * For examples of setting up and using a main loop, see + * @ref event_handler_example.c and @ref timer_example.c. + */ + +/** + * Runs a single iteration of the main loop to process everything on the + * queue. + * @ingroup Ecore_Main_Loop_Group + */ +EAPI void +ecore_main_loop_iterate(void) +{ +#ifndef USE_G_MAIN_LOOP + _ecore_main_loop_iterate_internal(1); +#else + g_main_context_iteration(NULL, 1); +#endif +} + +/** + * Runs the application main loop. + * + * This function will not return until @ref ecore_main_loop_quit is called. + * + * @ingroup Ecore_Main_Loop_Group + */ +EAPI void +ecore_main_loop_begin(void) +{ +#ifndef USE_G_MAIN_LOOP + in_main_loop++; + while (do_quit == 0) _ecore_main_loop_iterate_internal(0); + do_quit = 0; + in_main_loop--; +#else + ecore_main_loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(ecore_main_loop); +#endif +} + +/** + * Quits the main loop once all the events currently on the queue have + * been processed. + * @ingroup Ecore_Main_Loop_Group + */ +EAPI void +ecore_main_loop_quit(void) +{ +#ifndef USE_G_MAIN_LOOP + do_quit = 1; +#else + INF("enter"); + g_main_loop_quit(ecore_main_loop); + INF("leave"); +#endif +} + +/** + * Sets the function to use when monitoring multiple file descriptors, + * and waiting until one of more of the file descriptors before ready + * for some class of I/O operation. + * + * This function will be used instead of the system call select and + * could possible be used to integrate the Ecore event loop with an + * external event loop. + * + * @warning you don't know how to use, don't even try to use it. + * + * @ingroup Ecore_Main_Loop_Group + */ +EAPI void +ecore_main_loop_select_func_set(Ecore_Select_Function func) +{ + main_loop_select = func; +} + +/** + * Gets the select function set by ecore_select_func_set(), + * or the native select function if none was set. + * + * @ingroup Ecore_Main_Loop_Group + */ +EAPI void * +ecore_main_loop_select_func_get(void) +{ + return main_loop_select; +} + +/** + * @defgroup Ecore_FD_Handler_Group File Event Handling Functions + * + * Functions that deal with file descriptor handlers. + */ + +/** + * Adds a callback for activity on the given file descriptor. + * + * @p func will be called during the execution of @ref ecore_main_loop_begin + * when the file descriptor is available for reading, or writing, or both. + * + * Normally the return value from the @p func is "zero means this handler is + * finished and can be deleted" as is usual for handler callbacks. However, + * if the @p buf_func is supplied, then the return value from the @p func is + * "non zero means the handler should be called again in a tight loop". + * + * @p buf_func is called during event loop handling to check if data that has + * been read from the file descriptor is in a buffer and is available to + * read. Some systems (notably xlib) handle their own buffering, and would + * otherwise not work with select(). These systems should use a @p buf_func. + * This is a most annoying hack, only ecore_x uses it, so refer to that for + * an example. NOTE - @p func should probably return "one" always if + * @p buf_func is used, to avoid confusion with the other return value + * semantics. + * + * @param fd The file descriptor to watch. + * @param flags To watch it for read (@c ECORE_FD_READ) and/or + * (@c ECORE_FD_WRITE) write ability. @c ECORE_FD_ERROR + * + * @param func The callback function. + * @param data The data to pass to the callback. + * @param buf_func The function to call to check if any data has been + * buffered and already read from the fd. Can be @c NULL. + * @param buf_data The data to pass to the @p buf_func function. + * @return A fd handler handle if successful. @c NULL otherwise. + * @ingroup Ecore_FD_Handler_Group + */ +EAPI Ecore_Fd_Handler * +ecore_main_fd_handler_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, + Ecore_Fd_Cb buf_func, const void *buf_data) +{ + Ecore_Fd_Handler *fdh; + + if ((fd < 0) || (flags == 0) || (!func)) return NULL; + + fdh = calloc(1, sizeof(Ecore_Fd_Handler)); + if (!fdh) return NULL; + ECORE_MAGIC_SET(fdh, ECORE_MAGIC_FD_HANDLER); + fdh->fd = fd; + fdh->flags = flags; + if (0 > _ecore_main_fdh_epoll_add(fdh)) + { + ERR("Failed to add epoll fd %d (errno = %d)!", fd, errno); + free(fdh); + return NULL; + } + fdh->read_active = 0; + fdh->write_active = 0; + fdh->error_active = 0; + fdh->delete_me = 0; + fdh->func = func; + fdh->data = (void *)data; + fdh->buf_func = buf_func; + fdh->buf_data = (void *)buf_data; + fd_handlers = (Ecore_Fd_Handler *) + eina_inlist_append(EINA_INLIST_GET(fd_handlers), + EINA_INLIST_GET(fdh)); + return fdh; +} + +#ifdef _WIN32 +EAPI Ecore_Win32_Handler * +ecore_main_win32_handler_add(void *h, Ecore_Fd_Win32_Cb func, const void *data) +{ + Ecore_Win32_Handler *wh; + + if (!h || !func) return NULL; + + wh = calloc(1, sizeof(Ecore_Win32_Handler)); + if (!wh) return NULL; + ECORE_MAGIC_SET(wh, ECORE_MAGIC_WIN32_HANDLER); + wh->h = (HANDLE)h; + wh->delete_me = 0; + wh->func = func; + wh->data = (void *)data; + win32_handlers = (Ecore_Win32_Handler *) + eina_inlist_append(EINA_INLIST_GET(win32_handlers), + EINA_INLIST_GET(wh)); + return wh; +} +#else +EAPI Ecore_Win32_Handler * +ecore_main_win32_handler_add(void *h __UNUSED__, Ecore_Fd_Win32_Cb func __UNUSED__, + const void *data __UNUSED__) +{ + return NULL; +} +#endif + +/** + * Deletes the given FD handler. + * @param fd_handler The given FD handler. + * @return The data pointer set using @ref ecore_main_fd_handler_add, + * for @p fd_handler on success. @c NULL otherwise. + * @ingroup Ecore_FD_Handler_Group + * + * Beware that if the fd is already closed, ecore may complain if it uses + * epoll internally, and that in some rare cases this may be able to cause + * crashes and instability. Remember to delete your fd handlers before the + * fd's they listen to are closed. + */ +EAPI void * +ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler) +{ + if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) + { + ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, + "ecore_main_fd_handler_del"); + return NULL; + } + fd_handler->delete_me = 1; + fd_handlers_delete_me = 1; + _ecore_main_fdh_epoll_del(fd_handler); + return fd_handler->data; +} + +#ifdef _WIN32 +EAPI void * +ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler) +{ + if (!ECORE_MAGIC_CHECK(win32_handler, ECORE_MAGIC_WIN32_HANDLER)) + { + ECORE_MAGIC_FAIL(win32_handler, ECORE_MAGIC_WIN32_HANDLER, + "ecore_main_win32_handler_del"); + return NULL; + } + win32_handler->delete_me = 1; + win32_handlers_delete_me = 1; + return win32_handler->data; +} +#else +EAPI void * +ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler __UNUSED__) +{ + return NULL; +} +#endif + +EAPI void +ecore_main_fd_handler_prepare_callback_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Prep_Cb func, const void *data) +{ + if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) + { + ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, + "ecore_main_fd_handler_prepare_callback_set"); + return; + } + fd_handler->prep_func = func; + fd_handler->prep_data = (void *) data; +} + +/** + * Retrieves the file descriptor that the given handler is handling. + * @param fd_handler The given FD handler. + * @return The file descriptor the handler is watching. + * @ingroup Ecore_FD_Handler_Group + */ +EAPI int +ecore_main_fd_handler_fd_get(Ecore_Fd_Handler *fd_handler) +{ + if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) + { + ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, + "ecore_main_fd_handler_fd_get"); + return -1; + } + return fd_handler->fd; +} + +/** + * Return if read, write or error, or a combination thereof, is active on the + * file descriptor of the given FD handler. + * @param fd_handler The given FD handler. + * @param flags The flags, @c ECORE_FD_READ, @c ECORE_FD_WRITE or + * @c ECORE_FD_ERROR to query. + * @return #EINA_TRUE if any of the given flags are active. #EINA_FALSE otherwise. + * @ingroup Ecore_FD_Handler_Group + */ +EAPI Eina_Bool +ecore_main_fd_handler_active_get(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags) +{ + int ret = EINA_FALSE; + + if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) + { + ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, + "ecore_main_fd_handler_active_get"); + return EINA_FALSE; + } + if ((flags & ECORE_FD_READ) && (fd_handler->read_active)) ret = EINA_TRUE; + if ((flags & ECORE_FD_WRITE) && (fd_handler->write_active)) ret = EINA_TRUE; + if ((flags & ECORE_FD_ERROR) && (fd_handler->error_active)) ret = EINA_TRUE; + return ret; +} + +/** + * Set what active streams the given FD handler should be monitoring. + * @param fd_handler The given FD handler. + * @param flags The flags to be watching. + * @ingroup Ecore_FD_Handler_Group + */ +EAPI void +ecore_main_fd_handler_active_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags) +{ + if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) + { + ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, + "ecore_main_fd_handler_active_set"); + return; + } + fd_handler->flags = flags; + if (0 > _ecore_main_fdh_epoll_modify(fd_handler)) + { + ERR("Failed to mod epoll fd %d!", fd_handler->fd); + } +} + +void +_ecore_main_shutdown(void) +{ + if (in_main_loop) + { + ERR("\n" + "*** ECORE WARINING: Calling ecore_shutdown() while still in the main loop.\n" + "*** Program may crash or behave strangely now."); + return; + } + while (fd_handlers) + { + Ecore_Fd_Handler *fdh; + + fdh = fd_handlers; + fd_handlers = (Ecore_Fd_Handler *) eina_inlist_remove(EINA_INLIST_GET(fd_handlers), + EINA_INLIST_GET(fdh)); + ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE); + free(fdh); + } + fd_handlers_delete_me = 0; + fd_handler_current = NULL; + +#ifdef _WIN32 + while (win32_handlers) + { + Ecore_Win32_Handler *wh; + + wh = win32_handlers; + win32_handlers = (Ecore_Win32_Handler *) eina_inlist_remove(EINA_INLIST_GET(win32_handlers), + EINA_INLIST_GET(wh)); + ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE); + free(wh); + } + win32_handlers_delete_me = 0; + win32_handler_current = NULL; +#endif +} + +static void +_ecore_main_prepare_handlers(void) +{ + Ecore_Fd_Handler *fdh; + + /* call the prepare callback for all handlers */ + EINA_INLIST_FOREACH(fd_handlers, fdh) + { + if (!fdh->delete_me && fdh->prep_func) + { + fdh->references++; + fdh->prep_func (fdh->prep_data, fdh); + fdh->references--; + } + } +} + +static int +_ecore_main_select(double timeout) +{ + struct timeval tv, *t; + fd_set rfds, wfds, exfds; + int max_fd; + int ret; + + t = NULL; + if ((!finite(timeout)) || (timeout == 0.0)) /* finite() tests for NaN, too big, too small, and infinity. */ + { + tv.tv_sec = 0; + tv.tv_usec = 0; + t = &tv; + } + else if (timeout > 0.0) + { + int sec, usec; + +#ifdef FIX_HZ + timeout += (0.5 / HZ); + sec = (int)timeout; + usec = (int)((timeout - (double)sec) * 1000000); +#else + sec = (int)timeout; + usec = (int)((timeout - (double)sec) * 1000000); +#endif + tv.tv_sec = sec; + tv.tv_usec = usec; + t = &tv; + } + max_fd = 0; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&exfds); + + /* call the prepare callback for all handlers */ + _ecore_main_prepare_handlers(); +#ifndef HAVE_EPOLL + Ecore_Fd_Handler *fdh; + + EINA_INLIST_FOREACH(fd_handlers, fdh) + { + if (!fdh->delete_me) + { + if (fdh->flags & ECORE_FD_READ) + { + FD_SET(fdh->fd, &rfds); + if (fdh->fd > max_fd) max_fd = fdh->fd; + } + if (fdh->flags & ECORE_FD_WRITE) + { + FD_SET(fdh->fd, &wfds); + if (fdh->fd > max_fd) max_fd = fdh->fd; + } + if (fdh->flags & ECORE_FD_ERROR) + { + FD_SET(fdh->fd, &exfds); + if (fdh->fd > max_fd) max_fd = fdh->fd; + } + } + } +#else /* HAVE_EPOLL */ + /* polling on the epoll fd will wake when an fd in the epoll set is active */ + FD_SET(epoll_fd, &rfds); + max_fd = epoll_fd; +#endif /* HAVE_EPOLL */ + + if (_ecore_signal_count_get()) return -1; + + ret = main_loop_select(max_fd + 1, &rfds, &wfds, &exfds, t); + + _ecore_time_loop_time = ecore_time_get(); + if (ret < 0) + { +#ifndef _WIN32 + if (errno == EINTR) return -1; + else if (errno == EBADF) _ecore_main_fd_handlers_bads_rem(); +#endif + } + if (ret > 0) + { +#ifdef HAVE_EPOLL + _ecore_main_fdh_epoll_mark_active(); +#else /* HAVE_EPOLL */ + Ecore_Fd_Handler *fdh; + + EINA_INLIST_FOREACH(fd_handlers, fdh) + { + if (!fdh->delete_me) + { + if (FD_ISSET(fdh->fd, &rfds)) + fdh->read_active = 1; + if (FD_ISSET(fdh->fd, &wfds)) + fdh->write_active = 1; + if (FD_ISSET(fdh->fd, &exfds)) + fdh->error_active = 1; + } + } +#endif /* HAVE_EPOLL */ + _ecore_main_fd_handlers_cleanup(); +#ifdef _WIN32 + _ecore_main_win32_handlers_cleanup(); +#endif + return 1; + } + return 0; +} + +#ifndef _WIN32 +static void +_ecore_main_fd_handlers_bads_rem(void) +{ + Ecore_Fd_Handler *fdh; + Eina_Inlist *l; + int found = 0; + + ERR("Removing bad fds"); + for (l = EINA_INLIST_GET(fd_handlers); l; ) + { + fdh = (Ecore_Fd_Handler *) l; + l = l->next; + errno = 0; + + if ((fcntl(fdh->fd, F_GETFD) < 0) && (errno == EBADF)) + { + ERR("Found bad fd at index %d", fdh->fd); + if (fdh->flags & ECORE_FD_ERROR) + { + ERR("Fd set for error! calling user"); + fdh->references++; + if (!fdh->func(fdh->data, fdh)) + { + ERR("Fd function err returned 0, remove it"); + fdh->delete_me = 1; + fd_handlers_delete_me = 1; + found++; + } + fdh->references--; + } + else + { + ERR("Problematic fd found at %d! setting it for delete", fdh->fd); + fdh->delete_me = 1; + fd_handlers_delete_me = 1; + found++; + } + } + } + if (found == 0) + { +#ifdef HAVE_GLIB + ERR("No bad fd found. Maybe a foreign fd from glib?"); +#else + ERR("No bad fd found. EEEK!"); +#endif + } + _ecore_main_fd_handlers_cleanup(); +} +#endif + +static void +_ecore_main_fd_handlers_cleanup(void) +{ + Ecore_Fd_Handler *fdh; + Eina_Inlist *l; + int deleted_in_use = 0; + + if (!fd_handlers_delete_me) return; + for (l = EINA_INLIST_GET(fd_handlers); l; ) + { + fdh = (Ecore_Fd_Handler *) l; + + l = l->next; + if (fdh->delete_me) + { + if (fdh->references) + { + deleted_in_use++; + continue; + } + + fd_handlers = (Ecore_Fd_Handler *) + eina_inlist_remove(EINA_INLIST_GET(fd_handlers), + EINA_INLIST_GET(fdh)); + ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE); + free(fdh); + } + } + if (!deleted_in_use) fd_handlers_delete_me = 0; +} + +#ifdef _WIN32 +static void +_ecore_main_win32_handlers_cleanup(void) +{ + Ecore_Win32_Handler *wh; + Eina_Inlist *l; + int deleted_in_use = 0; + + if (!win32_handlers_delete_me) return; + for (l = EINA_INLIST_GET(win32_handlers); l; ) + { + wh = (Ecore_Win32_Handler *)l; + + l = l->next; + if (wh->delete_me) + { + if (wh->references) + { + deleted_in_use++; + continue; + } + + win32_handlers = (Ecore_Win32_Handler *) + eina_inlist_remove(EINA_INLIST_GET(win32_handlers), + EINA_INLIST_GET(wh)); + ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE); + free(wh); + } + } + if (!deleted_in_use) win32_handlers_delete_me = 0; +} +#endif + +static void +_ecore_main_fd_handlers_call(void) +{ + if (!fd_handler_current) + { + /* regular main loop, start from head */ + fd_handler_current = fd_handlers; + } + else + { + /* recursive main loop, continue from where we were */ + fd_handler_current = (Ecore_Fd_Handler *)EINA_INLIST_GET(fd_handler_current)->next; + } + + while (fd_handler_current) + { + Ecore_Fd_Handler *fdh = fd_handler_current; + + if (!fdh->delete_me) + { + if ((fdh->read_active) || + (fdh->write_active) || + (fdh->error_active)) + { + fdh->references++; + if (!fdh->func(fdh->data, fdh)) + { + fdh->delete_me = 1; + fd_handlers_delete_me = 1; + } + fdh->references--; + + fdh->read_active = 0; + fdh->write_active = 0; + fdh->error_active = 0; + } + } + + if (fd_handler_current) /* may have changed in recursive main loops */ + fd_handler_current = (Ecore_Fd_Handler *)EINA_INLIST_GET(fd_handler_current)->next; + } +} + +static int +_ecore_main_fd_handlers_buf_call(void) +{ + Ecore_Fd_Handler *fdh; + int ret; + + ret = 0; + EINA_INLIST_FOREACH(fd_handlers, fdh) + { + if (!fdh->delete_me) + { + if (fdh->buf_func) + { + fdh->references++; + if (fdh->buf_func(fdh->buf_data, fdh)) + { + ret |= fdh->func(fdh->data, fdh); + fdh->read_active = 1; + } + fdh->references--; + } + } + } + return ret; +} + +#ifndef USE_G_MAIN_LOOP +static void +_ecore_main_loop_iterate_internal(int once_only) +{ + double next_time = -1.0; + int have_event = 0; + int have_signal; + + in_main_loop++; + /* expire any timers */ + while (_ecore_timer_call(_ecore_time_loop_time)); + _ecore_timer_cleanup(); + + /* process signals into events .... */ + while (_ecore_signal_count_get()) _ecore_signal_call(); + if (_ecore_event_exist()) + { + _ecore_idle_enterer_call(); + have_event = 1; + _ecore_main_select(0.0); + _ecore_time_loop_time = ecore_time_get(); + _ecore_timer_enable_new(); + goto process_events; + } + /* call idle enterers ... */ + if (!once_only) _ecore_idle_enterer_call(); + else + { + have_event = have_signal = 0; + + if (_ecore_main_select(0.0) > 0) have_event = 1; + if (_ecore_signal_count_get() > 0) have_signal = 1; + if (have_signal || have_event) + { + _ecore_time_loop_time = ecore_time_get(); + _ecore_timer_enable_new(); + goto process_events; + } + } + + /* if these calls caused any buffered events to appear - deal with them */ + _ecore_main_fd_handlers_buf_call(); + + /* if there are any - jump to processing them */ + if (_ecore_event_exist()) + { + have_event = 1; + _ecore_main_select(0.0); + _ecore_time_loop_time = ecore_time_get(); + _ecore_timer_enable_new(); + goto process_events; + } + if (once_only) + { + _ecore_idle_enterer_call(); + in_main_loop--; + _ecore_time_loop_time = ecore_time_get(); + _ecore_timer_enable_new(); + return; + } + + if (_ecore_fps_debug) + { + t2 = ecore_time_get(); + if ((t1 > 0.0) && (t2 > 0.0)) + _ecore_fps_debug_runtime_add(t2 - t1); + } + start_loop: + /* any timers re-added as a result of these are allowed to go */ + _ecore_timer_enable_new(); + if (do_quit) + { + _ecore_time_loop_time = ecore_time_get(); + in_main_loop--; + _ecore_timer_enable_new(); + return; + } + if (!_ecore_event_exist()) + { + /* init flags */ + have_event = have_signal = 0; + next_time = _ecore_timer_next_get(); + /* no timers */ + if (next_time < 0) + { + /* no idlers */ + if (!_ecore_idler_exist()) + { + if (_ecore_main_select(-1.0) > 0) have_event = 1; + } + /* idlers */ + else + { + for (;;) + { + if (!_ecore_idler_call()) goto start_loop; + if (_ecore_event_exist()) break; + if (_ecore_main_select(0.0) > 0) have_event = 1; + if (_ecore_signal_count_get() > 0) have_signal = 1; + if (have_event || have_signal) break; + if (_ecore_timers_exists()) goto start_loop; + if (do_quit) break; + } + } + } + /* timers */ + else + { + /* no idlers */ + if (!_ecore_idler_exist()) + { + if (_ecore_main_select(next_time) > 0) have_event = 1; + } + /* idlers */ + else + { + for (;;) + { + if (!_ecore_idler_call()) goto start_loop; + if (_ecore_event_exist()) break; + if (_ecore_main_select(0.0) > 0) have_event = 1; + if (_ecore_signal_count_get() > 0) have_signal = 1; + if (have_event || have_signal) break; + next_time = _ecore_timer_next_get(); + if (next_time <= 0) break; + if (do_quit) break; + } + } + } + _ecore_time_loop_time = ecore_time_get(); + } + if (_ecore_fps_debug) t1 = ecore_time_get(); + /* we came out of our "wait state" so idle has exited */ + if (!once_only) _ecore_idle_exiter_call(); + /* call the fd handler per fd that became alive... */ + /* this should read or write any data to the monitored fd and then */ + /* post events onto the ecore event pipe if necessary */ + process_events: + _ecore_main_fd_handlers_call(); + _ecore_main_fd_handlers_buf_call(); + /* process signals into events .... */ + while (_ecore_signal_count_get()) _ecore_signal_call(); + /* handle events ... */ + _ecore_event_call(); + _ecore_main_fd_handlers_cleanup(); + + if (once_only) _ecore_idle_enterer_call(); + in_main_loop--; +} +#endif + +#ifdef _WIN32 +static int +_ecore_main_win32_select(int nfds __UNUSED__, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *tv) +{ + HANDLE objects[MAXIMUM_WAIT_OBJECTS]; + int sockets[MAXIMUM_WAIT_OBJECTS]; + Ecore_Fd_Handler *fdh; + Ecore_Win32_Handler *wh; + unsigned int objects_nbr = 0; + unsigned int handles_nbr = 0; + unsigned int events_nbr = 0; + DWORD result; + DWORD timeout; + MSG msg; + unsigned int i; + int res; + + /* Create an event object per socket */ + EINA_INLIST_FOREACH(fd_handlers, fdh) + { + WSAEVENT event; + long network_event; + + network_event = 0; + if (FD_ISSET(fdh->fd, readfds)) + network_event |= FD_READ; + if (FD_ISSET(fdh->fd, writefds)) + network_event |= FD_WRITE; + if (FD_ISSET(fdh->fd, exceptfds)) + network_event |= FD_OOB; + + if (network_event) + { + event = WSACreateEvent(); + WSAEventSelect(fdh->fd, event, network_event); + objects[objects_nbr] = event; + sockets[events_nbr] = fdh->fd; + events_nbr++; + objects_nbr++; + } + } + + /* store the HANDLEs in the objects to wait for */ + EINA_INLIST_FOREACH(win32_handlers, wh) + { + objects[objects_nbr] = wh->h; + handles_nbr++; + objects_nbr++; + } + + /* Empty the queue before waiting */ + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* Wait for any message sent or posted to this queue */ + /* or for one of the passed handles be set to signaled. */ + if (!tv) + timeout = INFINITE; + else + timeout = (DWORD)((tv->tv_sec * 1000.0) + (tv->tv_usec / 1000.0)); + + if (timeout == 0) return 0; + + result = MsgWaitForMultipleObjects(objects_nbr, (const HANDLE *)objects, EINA_FALSE, + timeout, QS_ALLINPUT); + + FD_ZERO(readfds); + FD_ZERO(writefds); + FD_ZERO(exceptfds); + + /* The result tells us the type of event we have. */ + if (result == WAIT_FAILED) + { + char *msg; + + msg = evil_last_error_get(); + ERR(" * %s\n", msg); + free(msg); + res = -1; + } + else if (result == WAIT_TIMEOUT) + { + /* ERR("time out\n"); */ + res = 0; + } + else if (result == (WAIT_OBJECT_0 + objects_nbr)) + { + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + res = 0; + } + else if ((result >= 0) && (result < WAIT_OBJECT_0 + events_nbr)) + { + WSANETWORKEVENTS network_event; + + WSAEnumNetworkEvents(sockets[result], objects[result], &network_event); + + if (network_event.lNetworkEvents & FD_READ) + FD_SET(sockets[result], readfds); + if (network_event.lNetworkEvents & FD_WRITE) + FD_SET(sockets[result], writefds); + if (network_event.lNetworkEvents & FD_OOB) + FD_SET(sockets[result], exceptfds); + + res = 1; + } + else if ((result >= (WAIT_OBJECT_0 + events_nbr)) && + (result < (WAIT_OBJECT_0 + objects_nbr))) + { + if (!win32_handler_current) + { + /* regular main loop, start from head */ + win32_handler_current = win32_handlers; + } + else + { + /* recursive main loop, continue from where we were */ + win32_handler_current = (Ecore_Win32_Handler *)EINA_INLIST_GET(win32_handler_current)->next; + } + + while (win32_handler_current) + { + wh = win32_handler_current; + + if (objects[result - WAIT_OBJECT_0] == wh->h) + { + if (!wh->delete_me) + { + wh->references++; + if (!wh->func(wh->data, wh)) + { + wh->delete_me = 1; + win32_handlers_delete_me = 1; + } + wh->references--; + } + } + if (win32_handler_current) /* may have changed in recursive main loops */ + win32_handler_current = (Ecore_Win32_Handler *)EINA_INLIST_GET(win32_handler_current)->next; + } + res = 1; + } + else + { + ERR("unknown result...\n"); + res = -1; + } + + /* Remove event objects again */ + for (i = 0; i < events_nbr; i++) WSACloseEvent(objects[i]); + + return res; +} +#endif diff --git a/tests/suite/ecore/src/lib/ecore_pipe.c b/tests/suite/ecore/src/lib/ecore_pipe.c new file mode 100644 index 0000000000..8a8aca5ffe --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_pipe.c @@ -0,0 +1,595 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "Ecore.h" +#include "ecore_private.h" + +/* How of then we should retry to write to the pipe */ +#define ECORE_PIPE_WRITE_RETRY 6 + +/* + * On Windows, pipe() is implemented with sockets. + * Contrary to Linux, Windows uses different functions + * for sockets and fd's: write() is for fd's and send + * is for sockets. So I need to put some win32 code + * here. I can't think of a solution where the win32 + * code is in Evil and not here. + */ + +#ifdef _WIN32 + +# include <winsock2.h> + +# define pipe_write(fd, buffer, size) send((fd), (char *)(buffer), size, 0) +# define pipe_read(fd, buffer, size) recv((fd), (char *)(buffer), size, 0) +# define pipe_close(fd) closesocket(fd) +# define PIPE_FD_INVALID INVALID_SOCKET +# define PIPE_FD_ERROR SOCKET_ERROR + +#else + +# include <unistd.h> +# include <fcntl.h> + +# define pipe_write(fd, buffer, size) write((fd), buffer, size) +# define pipe_read(fd, buffer, size) read((fd), buffer, size) +# define pipe_close(fd) close(fd) +# define PIPE_FD_INVALID -1 +# define PIPE_FD_ERROR -1 + +#endif /* ! _WIN32 */ + +struct _Ecore_Pipe +{ + ECORE_MAGIC; + int fd_read; + int fd_write; + Ecore_Fd_Handler *fd_handler; + const void *data; + Ecore_Pipe_Cb handler; + unsigned int len; + size_t already_read; + void *passed_data; +}; + + +static Eina_Bool _ecore_pipe_read(void *data, Ecore_Fd_Handler *fd_handler); + +/** + * @defgroup Ecore_Pipe_Group Pipe wrapper + * + * These functions wrap the pipe / write / read functions to + * easily integrate a loop that is in its own thread to the ecore + * main loop. + * + * The ecore_pipe_add() function creates file descriptors (sockets on + * Windows) and attach an handle to the ecore main loop. That handle is + * called when data is read in the pipe. To write data in the pipe, + * just call ecore_pipe_write(). When you are done, just call + * ecore_pipe_del(). + * + * Here is an example that uses the pipe wrapper with a Gstreamer + * pipeline. For each decoded frame in the Gstreamer thread, a handle + * is called in the ecore thread. + * + * @code#include <gst/gst.h> + * #include <Ecore.h> + * + * static int nbr = 0; + * + * static GstElement *_buid_pipeline (gchar *filename, Ecore_Pipe *pipe); + * + * static void new_decoded_pad_cb (GstElement *demuxer, + * GstPad *new_pad, + * gpointer user_data); + * + * static void handler(void *data, void *buf, unsigned int len) + * { + * GstBuffer *buffer = *((GstBuffer **)buf); + * + * printf ("handler : %p\n", buffer); + * printf ("frame : %d %p %lld %p\n", nbr++, data, (long long)GST_BUFFER_DURATION(buffer), buffer); + * gst_buffer_unref (buffer); + * } + * + * + * static void handoff (GstElement* object, + * GstBuffer* arg0, + * GstPad* arg1, + * gpointer user_data) + * { + * Ecore_Pipe *pipe; + * + * pipe = (Ecore_Pipe *)user_data; + * printf ("handoff : %p\n", arg0); + * gst_buffer_ref (arg0); + * ecore_pipe_write(pipe, &arg0, sizeof(arg0)); + * } + * + * int + * main (int argc, char *argv[]) + * { + * GstElement *pipeline; + * char *filename; + * Ecore_Pipe *pipe; + * + * gst_init (&argc, &argv); + * + * if (!ecore_init ()) + * { + * gst_deinit (); + * return 0; + * } + * + * pipe = ecore_pipe_add (handler); + * if (!pipe) + * { + * ecore_shutdown (); + * gst_deinit (); + * return 0; + * } + * + * if (argc < 2) { + * g_print ("usage: %s file.avi\n", argv[0]); + * ecore_pipe_del (pipe); + * ecore_shutdown (); + * gst_deinit (); + * return 0; + * } + * filename = argv[1]; + * + * pipeline = _buid_pipeline (filename, pipe); + * if (!pipeline) { + * g_print ("Error during the pipeline building\n"); + * ecore_pipe_del (pipe); + * ecore_shutdown (); + * gst_deinit (); + * return -1; + * } + * + * gst_element_set_state (pipeline, GST_STATE_PLAYING); + * + * ecore_main_loop_begin(); + * + * ecore_pipe_del (pipe); + * ecore_shutdown (); + * gst_deinit (); + * + * return 0; + * } + * + * static void + * new_decoded_pad_cb (GstElement *demuxer, + * GstPad *new_pad, + * gpointer user_data) + * { + * GstElement *decoder; + * GstPad *pad; + * GstCaps *caps; + * gchar *str; + * + * caps = gst_pad_get_caps (new_pad); + * str = gst_caps_to_string (caps); + * + * if (g_str_has_prefix (str, "video/")) { + * decoder = GST_ELEMENT (user_data); + * + * pad = gst_element_get_pad (decoder, "sink"); + * if (GST_PAD_LINK_FAILED (gst_pad_link (new_pad, pad))) { + * g_warning ("Failed to link %s:%s to %s:%s", GST_DEBUG_PAD_NAME (new_pad), + * GST_DEBUG_PAD_NAME (pad)); + * } + * } + * g_free (str); + * gst_caps_unref (caps); + * } + * + * static GstElement * + * _buid_pipeline (gchar *filename, Ecore_Pipe *pipe) + * { + * GstElement *pipeline; + * GstElement *filesrc; + * GstElement *demuxer; + * GstElement *decoder; + * GstElement *sink; + GstStateChangeReturn res; + * + * pipeline = gst_pipeline_new ("pipeline"); + * if (!pipeline) + * return NULL; + * + * filesrc = gst_element_factory_make ("filesrc", "filesrc"); + * if (!filesrc) { + * printf ("no filesrc"); + * goto failure; + * } + * g_object_set (G_OBJECT (filesrc), "location", filename, NULL); + * + * demuxer = gst_element_factory_make ("oggdemux", "demuxer"); + * if (!demuxer) { + * printf ("no demux"); + * goto failure; + * } + * + * decoder = gst_element_factory_make ("theoradec", "decoder"); + * if (!decoder) { + * printf ("no dec"); + * goto failure; + * } + * + * g_signal_connect (demuxer, "pad-added", + * G_CALLBACK (new_decoded_pad_cb), decoder); + * + * sink = gst_element_factory_make ("fakesink", "sink"); + * if (!sink) { + * printf ("no sink"); + * goto failure; + * } + * g_object_set (G_OBJECT (sink), "sync", EINA_TRUE, NULL); + * g_object_set (G_OBJECT (sink), "signal-handoffs", EINA_TRUE, NULL); + * g_signal_connect (sink, "handoff", + * G_CALLBACK (handoff), pipe); + * + * gst_bin_add_many (GST_BIN (pipeline), + * filesrc, demuxer, decoder, sink, NULL); + * + * if (!gst_element_link (filesrc, demuxer)) + * goto failure; + * if (!gst_element_link (decoder, sink)) + * goto failure; + * + * res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + * if (res == GST_STATE_CHANGE_FAILURE) + * goto failure; + * + * res = gst_element_get_state( pipeline, NULL, NULL, GST_CLOCK_TIME_NONE ); + * if (res != GST_STATE_CHANGE_SUCCESS) + * goto failure; + * + * return pipeline; + * + * failure: + * gst_object_unref (GST_OBJECT (pipeline)); + * return NULL; + * } + * @endcode + */ + + +/** + * Create two file descriptors (sockets on Windows). Add + * a callback that will be called when the file descriptor that + * is listened receives data. An event is also put in the event + * queue when data is received. + * + * @param handler The handler called when data is received. + * @param data Data to pass to @p handler when it is called. + * @return A newly created Ecore_Pipe object if successful. + * @c NULL otherwise. + * @ingroup Ecore_Pipe_Group + */ +EAPI Ecore_Pipe * +ecore_pipe_add(Ecore_Pipe_Cb handler, const void *data) +{ + Ecore_Pipe *p; + int fds[2]; + + if (!handler) return NULL; + + p = (Ecore_Pipe *)calloc(1, sizeof(Ecore_Pipe)); + if (!p) return NULL; + + if (pipe(fds)) + { + free(p); + return NULL; + } + + ECORE_MAGIC_SET(p, ECORE_MAGIC_PIPE); + p->fd_read = fds[0]; + p->fd_write = fds[1]; + p->handler = handler; + p->data = data; + + fcntl(p->fd_read, F_SETFL, O_NONBLOCK); + p->fd_handler = ecore_main_fd_handler_add(p->fd_read, + ECORE_FD_READ, + _ecore_pipe_read, + p, + NULL, NULL); + return p; +} + +/** + * Free an Ecore_Pipe object created with ecore_pipe_add(). + * + * @param p The Ecore_Pipe object to be freed. + * @return The pointer to the private data + * @ingroup Ecore_Pipe_Group + */ +EAPI void * +ecore_pipe_del(Ecore_Pipe *p) +{ + void *data; + + if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE)) + { + ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_del"); + return NULL; + } + if (p->fd_handler) ecore_main_fd_handler_del(p->fd_handler); + if (p->fd_read != PIPE_FD_INVALID) pipe_close(p->fd_read); + if (p->fd_write != PIPE_FD_INVALID) pipe_close(p->fd_write); + data = (void *)p->data; + free(p); + return data; +} + +/** + * Close the read end of an Ecore_Pipe object created with ecore_pipe_add(). + * + * @param p The Ecore_Pipe object. + * @ingroup Ecore_Pipe_Group + */ +EAPI void +ecore_pipe_read_close(Ecore_Pipe *p) +{ + if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE)) + { + ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_read_close"); + return; + } + ecore_main_fd_handler_del(p->fd_handler); + p->fd_handler = NULL; + pipe_close(p->fd_read); + p->fd_read = PIPE_FD_INVALID; +} + +/** + * Close the write end of an Ecore_Pipe object created with ecore_pipe_add(). + * + * @param p The Ecore_Pipe object. + * @ingroup Ecore_Pipe_Group + */ +EAPI void +ecore_pipe_write_close(Ecore_Pipe *p) +{ + if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE)) + { + ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_write_close"); + return; + } + pipe_close(p->fd_write); + p->fd_write = PIPE_FD_INVALID; +} + +/** + * Write on the file descriptor the data passed as parameter. + * + * @param p The Ecore_Pipe object. + * @param buffer The data to write into the pipe. + * @param nbytes The size of the @p buffer in bytes + * @return Returns EINA_TRUE on a successful write, EINA_FALSE on an error + * @ingroup Ecore_Pipe_Group + */ +EAPI Eina_Bool +ecore_pipe_write(Ecore_Pipe *p, const void *buffer, unsigned int nbytes) +{ + ssize_t ret; + size_t already_written = 0; + int retry = ECORE_PIPE_WRITE_RETRY; + + if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE)) + { + ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_write"); + return EINA_FALSE; + } + + if (p->fd_write == PIPE_FD_INVALID) return EINA_FALSE; + + /* First write the len into the pipe */ + do + { + ret = pipe_write(p->fd_write, &nbytes, sizeof(nbytes)); + if (ret == sizeof(nbytes)) + { + retry = ECORE_PIPE_WRITE_RETRY; + break; + } + else if (ret > 0) + { + /* XXX What should we do here? */ + ERR("The length of the data was not written complete" + " to the pipe"); + return EINA_FALSE; + } + else if (ret == PIPE_FD_ERROR && errno == EPIPE) + { + pipe_close(p->fd_write); + p->fd_write = PIPE_FD_INVALID; + return EINA_FALSE; + } + else if (ret == PIPE_FD_ERROR && errno == EINTR) + /* try it again */ + ; + else + { + ERR("An unhandled error (ret: %zd errno: %d)" + "occurred while writing to the pipe the length", + ret, errno); + } + } + while (retry--); + + if (retry != ECORE_PIPE_WRITE_RETRY) return EINA_FALSE; + + /* and now pass the data to the pipe */ + do + { + ret = pipe_write(p->fd_write, + ((unsigned char *)buffer) + already_written, + nbytes - already_written); + + if (ret == (ssize_t)(nbytes - already_written)) + return EINA_TRUE; + else if (ret >= 0) + { + already_written -= ret; + continue; + } + else if (ret == PIPE_FD_ERROR && errno == EPIPE) + { + pipe_close(p->fd_write); + p->fd_write = PIPE_FD_INVALID; + return EINA_FALSE; + } + else if (ret == PIPE_FD_ERROR && errno == EINTR) + /* try it again */ + ; + else + { + ERR("An unhandled error (ret: %zd errno: %d)" + "occurred while writing to the pipe the length", + ret, errno); + } + } + while (retry--); + + return EINA_FALSE; +} + +/* Private function */ + +static Eina_Bool +_ecore_pipe_read(void *data, Ecore_Fd_Handler *fd_handler __UNUSED__) +{ + Ecore_Pipe *p; + double start_time; + + p = (Ecore_Pipe *)data; + start_time = ecore_time_get(); + + do + { + ssize_t ret; + + /* if we already have read some data we don't need to read the len + * but to finish the already started job + */ + if (p->len == 0) + { + /* read the len of the passed data */ + ret = pipe_read(p->fd_read, &p->len, sizeof(p->len)); + + /* catch the non error case first */ + if (ret == sizeof(p->len)) + ; + else if (ret > 0) + { + /* XXX What should we do here? */ + ERR("Only read %zd bytes from the pipe, although" + " we need to read %zd bytes.", ret, sizeof(p->len)); + } + else if (ret == 0) + { + p->handler((void *)p->data, NULL, 0); + pipe_close(p->fd_read); + p->fd_read = PIPE_FD_INVALID; + p->fd_handler = NULL; + return ECORE_CALLBACK_CANCEL; + } +#ifndef _WIN32 + else if ((ret == PIPE_FD_ERROR) && ((errno == EINTR) || (errno == EAGAIN))) + return ECORE_CALLBACK_RENEW; + else + { + ERR("An unhandled error (ret: %zd errno: %d)" + "occurred while reading from the pipe the length", + ret, errno); + return ECORE_CALLBACK_RENEW; + } +#else + else /* ret == PIPE_FD_ERROR is the only other case on Windows */ + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + p->handler((void *)p->data, NULL, 0); + pipe_close(p->fd_read); + p->fd_read = PIPE_FD_INVALID; + p->fd_handler = NULL; + return ECORE_CALLBACK_CANCEL; + } + } +#endif + } + + if (!p->passed_data) + p->passed_data = malloc(p->len); + + /* and read the passed data */ + ret = pipe_read(p->fd_read, + ((unsigned char *)p->passed_data) + p->already_read, + p->len - p->already_read); + + /* catch the non error case first */ + if (ret == (ssize_t)(p->len - p->already_read)) + { + p->handler((void *)p->data, p->passed_data, p->len); + free(p->passed_data); + /* reset all values to 0 */ + p->passed_data = NULL; + p->already_read = 0; + p->len = 0; + } + else if (ret >= 0) + { + p->already_read += ret; + return ECORE_CALLBACK_RENEW; + } + else if (ret == 0) + { + p->handler((void *)p->data, NULL, 0); + pipe_close(p->fd_read); + p->fd_read = PIPE_FD_INVALID; + p->fd_handler = NULL; + return ECORE_CALLBACK_CANCEL; + } +#ifndef _WIN32 + else if (ret == PIPE_FD_ERROR && (errno == EINTR || errno == EAGAIN)) + return ECORE_CALLBACK_RENEW; + else + { + ERR("An unhandled error (ret: %zd errno: %d)" + "occurred while reading from the pipe the data", + ret, errno); + return ECORE_CALLBACK_RENEW; + } +#else + else /* ret == PIPE_FD_ERROR is the only other case on Windows */ + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + p->handler((void *)p->data, NULL, 0); + pipe_close(p->fd_read); + p->fd_read = PIPE_FD_INVALID; + p->fd_handler = NULL; + return ECORE_CALLBACK_CANCEL; + } + else + break; + } +#endif + } + while (ecore_time_get() - start_time < ecore_animator_frametime_get()); + + return ECORE_CALLBACK_RENEW; +} diff --git a/tests/suite/ecore/src/lib/ecore_poll.c b/tests/suite/ecore/src/lib/ecore_poll.c new file mode 100644 index 0000000000..d5bc6badf5 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_poll.c @@ -0,0 +1,442 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +#include "Ecore.h" +#include "ecore_private.h" + + +struct _Ecore_Poller +{ + EINA_INLIST; + ECORE_MAGIC; + int ibit; + unsigned char delete_me : 1; + Ecore_Task_Cb func; + void *data; +}; + + +static Ecore_Timer *timer = NULL; +static int min_interval = -1; +static int interval_incr = 0; +static int at_tick = 0; +static int just_added_poller = 0; +static int poller_delete_count = 0; +static int poller_walking = 0; +static double poll_interval = 0.125; +static double poll_cur_interval = 0.0; +static double last_tick = 0.0; +static Ecore_Poller *pollers[16] = +{ + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL +}; +static unsigned short poller_counters[16] = +{ + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 +}; + +static void _ecore_poller_next_tick_eval(void); +static Eina_Bool _ecore_poller_cb_timer(void *data); + +static void +_ecore_poller_next_tick_eval(void) +{ + int i; + double interval; + + min_interval = -1; + for (i = 0; i < 15; i++) + { + if (pollers[i]) + { + min_interval = i; + break; + } + } + if (min_interval < 0) + { + /* no pollers */ + if (timer) + { + ecore_timer_del(timer); + timer = NULL; + } + return; + } + interval_incr = (1 << min_interval); + interval = interval_incr * poll_interval; + /* we are at the tick callback - so no need to do inter-tick adjustments + * so we can fasttrack this as t -= last_tick in theory is 0.0 (though + * in practice it will be a very very very small value. also the tick + * callback will adjust the timer interval at the end anyway */ + if (at_tick) + { + if (!timer) + timer = ecore_timer_add(interval, _ecore_poller_cb_timer, NULL); + } + else + { + double t; + + if (!timer) + timer = ecore_timer_add(interval, _ecore_poller_cb_timer, NULL); + else + { + t = ecore_time_get(); + if (interval != poll_cur_interval) + { + t -= last_tick; /* time since we last ticked */ + /* delete the timer and reset it to tick off in the new + * time interval. at the tick this will be adjusted */ + ecore_timer_del(timer); + timer = ecore_timer_add(interval - t, + _ecore_poller_cb_timer, NULL); + } + } + } + poll_cur_interval = interval; +} + +static Eina_Bool +_ecore_poller_cb_timer(void *data __UNUSED__) +{ + int i; + Ecore_Poller *poller, *l; + int changes = 0; + + at_tick++; + last_tick = ecore_time_get(); + /* we have 16 counters - each incriments every time the poller counter + * "ticks". it incriments by the minimum interval (which can be 1, 2, 4, + * 7, 16 etc. up to 32768) */ + for (i = 0; i < 15; i++) + { + poller_counters[i] += interval_incr; + /* wrap back to 0 if we exceed out loop count for the counter */ + if (poller_counters[i] >= (1 << i)) poller_counters[i] = 0; + } + + just_added_poller = 0; + /* walk the pollers now */ + poller_walking++; + for (i = 0; i < 15; i++) + { + /* if the counter is @ 0 - this means that counter "went off" this + * tick interval, so run all pollers hooked to that counter */ + if (poller_counters[i] == 0) + { + EINA_INLIST_FOREACH(pollers[i], poller) + { + if (!poller->delete_me) + { + if (!poller->func(poller->data)) + { + if (!poller->delete_me) + { + poller->delete_me = 1; + poller_delete_count++; + } + } + } + } + } + } + poller_walking--; + + /* handle deletes afterwards */ + if (poller_delete_count > 0) + { + /* FIXME: walk all pollers and remove deleted ones */ + for (i = 0; i < 15; i++) + { + for (l = pollers[i]; l;) + { + poller = l; + l = (Ecore_Poller *) EINA_INLIST_GET(l)->next; + if (poller->delete_me) + { + pollers[i] = (Ecore_Poller *) eina_inlist_remove(EINA_INLIST_GET(pollers[i]), EINA_INLIST_GET(poller)); + free(poller); + poller_delete_count--; + changes++; + if (poller_delete_count <= 0) break; + } + } + if (poller_delete_count <= 0) break; + } + } + /* if we deleted or added any pollers, then we need to re-evaluate our + * minimum poll interval */ + if ((changes > 0) || (just_added_poller > 0)) + _ecore_poller_next_tick_eval(); + + just_added_poller = 0; + poller_delete_count = 0; + + at_tick--; + + /* if the timer was deleted then there is no point returning 1 - ambiguous + * if we do as it im plies "keep running me" but we have been deleted + * anyway */ + if (!timer) return ECORE_CALLBACK_CANCEL; + + /* adjust interval */ + ecore_timer_interval_set(timer, poll_cur_interval); + return ECORE_CALLBACK_RENEW; +} + +/** + * @defgroup Ecore_Poll_Group Ecore Poll Functions + * + * These functions are for the need to poll information, but provide a shared + * abstracted API to pool such polling to minimise wakeup and ensure all the + * polling happens in as few spots as possible areound a core poll interval. + * For now only 1 core poller type is supprted: ECORE_POLLER_CORE + */ + + +/** + * Sets the time between ticks (in seconds) for the given ticker clock. + * @param type The ticker type to adjust + * @param poll_time The time (in seconds) between ticks of the clock + * @ingroup Ecore_Poller_Group + * + * This will adjust the time between ticks of the given ticker type defined + * by @p type to the time period defined by @p poll_time. + */ +EAPI void +ecore_poller_poll_interval_set(Ecore_Poller_Type type __UNUSED__, double poll_time) +{ + poll_interval = poll_time; + _ecore_poller_next_tick_eval(); +} + +/** + * Gets the time between ticks (in seconds) for the given ticker clock. + * @param type The ticker type to query + * @return The time in seconds between ticks of the ticker clock + * @ingroup Ecore_Poller_Group + * + * This will get the time between ticks of the specifider ticker clock. + */ +EAPI double +ecore_poller_poll_interval_get(Ecore_Poller_Type type __UNUSED__) +{ + return poll_interval; +} + +/** + * Creates a poller to call the given function at a particular tick interval. + * @param type The ticker type to attach the poller to + * @param interval The poll interval + * @param func The given function. If @p func returns 1, the poller is + * rescheduled for the next tick interval. + * @param data Data to pass to @p func when it is called. + * @return A poller object on success. @c NULL on failure. + * @ingroup Ecore_Poller_Group + * + * This function adds a poller callback that is to be called regularly + * along with all other poller callbacks so the pollers are synchronized with + * all other pollers running off the same poller type and at the same tick + * interval. This should be used for polling things when polling is desired + * or required, and you do not have specific requirements on the exact times + * to poll and want to avoid extra process wakeups for polling. This will + * save power as the CPU has more of a chance to go into a low power state + * the longer it is asleep for, so this should be used if you are at all + * power conscious. + * + * The @p type parameter defines the poller tick type (there is a virtual + * clock ticking all the time - though ecore avoids making it tick when + * there will not be any work to do at that tick point). There is only one + * ticker at the moment - that is ECORE_POLLER_CORE. This is here for future + * expansion if multiple clocks with different frequencies are really required. + * The default time between ticks for the ECORE_POLLER_CORE ticker is 0.125 + * seconds. + * + * The @p interval is the number of ticker ticks that will pass by in between + * invocations of the @p func callback. This must be between 1 and 32768 + * inclusive, and must be a power of 2 (i.e. 1, 2, 4, 8, 16, ... 16384, 32768). + * If it is 1, then the function will be called every tick. if it is 2, then it + * will be called every 2nd tick, if it is 8, then every 8th tick etc. Exactly + * which tick is undefined, as only the interval between calls can be defined. + * Ecore will endeavour to keep pollers synchronised and to call as many in + * 1 wakeup event as possible. + * + * This function adds a poller and returns its handle on success and NULL on + * failure. The function @p func will be called at tick intervals described + * above. The function will be passed the @p data pointer as its parameter. + * + * When the poller @p func is called, it must return a value of either + * 1 (or ECORE_CALLBACK_RENEW) or 0 (or ECORE_CALLBACK_CANCEL). If it + * returns 1, it will be called again at the next tick, or if it returns + * 0 it will be deleted automatically making any references/handles for it + * invalid. + */ +EAPI Ecore_Poller * +ecore_poller_add(Ecore_Poller_Type type __UNUSED__, int interval, Ecore_Task_Cb func, const void *data) +{ + Ecore_Poller *poller; + int ibit; + + if (!func) return NULL; + if (interval < 1) interval = 1; + + poller = calloc(1, sizeof(Ecore_Poller)); + if (!poller) return NULL; + ECORE_MAGIC_SET(poller, ECORE_MAGIC_POLLER); + /* interval MUST be a power of 2, so enforce it */ + if (interval < 1) interval = 1; + ibit = -1; + while (interval != 0) + { + ibit++; + interval >>= 1; + } + /* only allow up to 32768 - i.e. ibit == 15, so limit it */ + if (ibit > 15) ibit = 15; + + poller->ibit = ibit; + poller->func = func; + poller->data = (void *)data; + pollers[poller->ibit] = (Ecore_Poller *) eina_inlist_prepend(EINA_INLIST_GET(pollers[poller->ibit]), EINA_INLIST_GET(poller)); + if (poller_walking) + just_added_poller++; + else + _ecore_poller_next_tick_eval(); + return poller; +} + +/** + * Changes the polling interval rate of @p poller. + * + * @param poller The Ecore_Poller to change the interval of + * @param interval The tick interval to set; must be a power of 2 but <= 32768 + * @return Returns true on success, false on failure + * + * This allows the changing of a poller's polling interval. It is useful when you want to alter + * a poll rate without deleting and re-creating a poller. + * @ingroup Ecore_Poller_Group + */ +EAPI Eina_Bool +ecore_poller_poller_interval_set(Ecore_Poller *poller, int interval) +{ + int ibit; + + if (!ECORE_MAGIC_CHECK(poller, ECORE_MAGIC_POLLER)) + { + ECORE_MAGIC_FAIL(poller, ECORE_MAGIC_POLLER, + "ecore_poller_poller_interval_set"); + return EINA_FALSE; + } + + /* interval MUST be a power of 2, so enforce it */ + if (interval < 1) interval = 1; + ibit = -1; + while (interval != 0) + { + ibit++; + interval >>= 1; + } + /* only allow up to 32768 - i.e. ibit == 15, so limit it */ + if (ibit > 15) ibit = 15; + /* if interval specified is the same as interval set, return true without wasting time */ + if (poller->ibit == ibit) + return EINA_TRUE; + pollers[poller->ibit] = (Ecore_Poller *) eina_inlist_remove(EINA_INLIST_GET(pollers[poller->ibit]), EINA_INLIST_GET(poller)); + poller->ibit = ibit; + pollers[poller->ibit] = (Ecore_Poller *) eina_inlist_prepend(EINA_INLIST_GET(pollers[poller->ibit]), EINA_INLIST_GET(poller)); + if (poller_walking) + just_added_poller++; + else + _ecore_poller_next_tick_eval(); + return EINA_TRUE; +} + +/** + * Gets the polling interval rate of @p poller. + * + * @param poller The Ecore_Poller to change the interval of + * @return Returns the interval, in ticks, that @p poller polls at + * + * This returns a poller's polling interval, or 0 on error. + * @ingroup Ecore_Poller_Group + */ +EAPI int +ecore_poller_poller_interval_get(Ecore_Poller *poller) +{ + int ibit, interval = 1; + + if (!ECORE_MAGIC_CHECK(poller, ECORE_MAGIC_POLLER)) + { + ECORE_MAGIC_FAIL(poller, ECORE_MAGIC_POLLER, + "ecore_poller_poller_interval_get"); + return 0; + } + + ibit = poller->ibit; + while (ibit != 0) + { + ibit--; + interval <<= 1; + } + return interval; +} + +/** + * Delete the specified poller from the timer list. + * @param poller The poller to delete. + * @return The data pointer set for the timer when @ref ecore_poller_add was + * called. @c NULL is returned if the function is unsuccessful. + * @ingroup Ecore_Poller_Group + * + * Note: @p poller must be a valid handle. If the poller function has already + * returned 0, the handle is no longer valid (and does not need to be delete). + */ +EAPI void * +ecore_poller_del(Ecore_Poller *poller) +{ + void *data; + + if (!ECORE_MAGIC_CHECK(poller, ECORE_MAGIC_POLLER)) + { + ECORE_MAGIC_FAIL(poller, ECORE_MAGIC_POLLER, + "ecore_poller_del"); + return NULL; + } + /* we are walking the poller list - a bad idea to remove from it while + * walking it, so just flag it as delete_me and come back to it after + * the loop has finished */ + if (poller_walking > 0) + { + poller_delete_count++; + poller->delete_me = 1; + return poller->data; + } + /* not in loop so safe - delete immediately */ + data = poller->data; + pollers[poller->ibit] = (Ecore_Poller *) eina_inlist_remove(EINA_INLIST_GET(pollers[poller->ibit]), EINA_INLIST_GET(poller)); + free(poller); + _ecore_poller_next_tick_eval(); + return data; +} + +void +_ecore_poller_shutdown(void) +{ + int i; + Ecore_Poller *poller; + + for (i = 0; i < 15; i++) + { + while ((poller = pollers[i])) + { + pollers[i] = (Ecore_Poller *) eina_inlist_remove(EINA_INLIST_GET(pollers[i]), EINA_INLIST_GET(pollers[i])); + free(poller); + } + } +} diff --git a/tests/suite/ecore/src/lib/ecore_private.h b/tests/suite/ecore/src/lib/ecore_private.h new file mode 100644 index 0000000000..5f08f8ab51 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_private.h @@ -0,0 +1,208 @@ +#ifndef _ECORE_PRIVATE_H +#define _ECORE_PRIVATE_H + +extern int _ecore_log_dom ; +#ifdef _ECORE_DEFAULT_LOG_DOM +# undef _ECORE_DEFAULT_LOG_DOM +#endif +#define _ECORE_DEFAULT_LOG_DOM _ecore_log_dom + +#ifdef ECORE_DEFAULT_LOG_COLOR +# undef ECORE_DEFAULT_LOG_COLOR +#endif +#define ECORE_DEFAULT_LOG_COLOR EINA_COLOR_BLUE + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__) + +#ifdef DBG +# undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__) + +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__) + +#ifdef WRN +# undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__) + +#ifdef CRIT +# undef CRIT +#endif +#define CRIT(...) EINA_LOG_DOM_CRIT(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__) + +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + +#ifndef MIN +# define MIN(x, y) (((x) > (y)) ? (y) : (x)) +#endif + +#ifndef MAX +# define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef ABS +# define ABS(x) ((x) < 0 ? -(x) : (x)) +#endif + +#ifndef CLAMP +# define CLAMP(x, min, max) (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x))) +#endif + +#define EVAS_FRAME_QUEUING 1 /* for test */ + +#define READBUFSIZ 65536 + +#define ECORE_MAGIC_NONE 0x1234fedc +#define ECORE_MAGIC_EXE 0xf7e812f5 +#define ECORE_MAGIC_TIMER 0xf7d713f4 +#define ECORE_MAGIC_IDLER 0xf7c614f3 +#define ECORE_MAGIC_IDLE_ENTERER 0xf7b515f2 +#define ECORE_MAGIC_IDLE_EXITER 0xf7601afd +#define ECORE_MAGIC_FD_HANDLER 0xf7a416f1 +#define ECORE_MAGIC_EVENT_HANDLER 0xf79317f0 +#define ECORE_MAGIC_EVENT_FILTER 0xf78218ff +#define ECORE_MAGIC_EVENT 0xf77119fe +#define ECORE_MAGIC_ANIMATOR 0xf7643ea5 +#define ECORE_MAGIC_POLLER 0xf7568127 +#define ECORE_MAGIC_PIPE 0xf7458226 +#define ECORE_MAGIC_WIN32_HANDLER 0xf7e8f1a3 +#define ECORE_MAGIC_JOB 0x76543210 + + +#define ECORE_MAGIC Ecore_Magic __magic + +#define ECORE_MAGIC_SET(d, m) (d)->__magic = (m) +#define ECORE_MAGIC_CHECK(d, m) ((d) && ((d)->__magic == (m))) +#define ECORE_MAGIC_FAIL(d, m, fn) _ecore_magic_fail((d), (d) ? (d)->__magic : 0, (m), (fn)); + +/* undef the following, we want our version */ +#undef FREE +#define FREE(ptr) free(ptr); ptr = NULL; + +#undef IF_FREE +#define IF_FREE(ptr) if (ptr) free(ptr); ptr = NULL; + +#undef IF_FN_DEL +#define IF_FN_DEL(_fn, ptr) if (ptr) { _fn(ptr); ptr = NULL; } + +EAPI void ecore_print_warning(const char *function, const char *sparam); + +/* convenience macros for checking pointer parameters for non-NULL */ +#undef CHECK_PARAM_POINTER_RETURN +#define CHECK_PARAM_POINTER_RETURN(sparam, param, ret) \ + if (!(param)) \ + { \ + ecore_print_warning(__FUNCTION__, sparam); \ + return ret; \ + } + +#undef CHECK_PARAM_POINTER +#define CHECK_PARAM_POINTER(sparam, param) \ + if (!(param)) \ + { \ + ecore_print_warning(__FUNCTION__, sparam); \ + return; \ + } + +typedef unsigned int Ecore_Magic; + +EAPI void _ecore_magic_fail(const void *d, Ecore_Magic m, Ecore_Magic req_m, const char *fname); + +void _ecore_time_init(void); + +void _ecore_timer_shutdown(void); +void _ecore_timer_cleanup(void); +void _ecore_timer_enable_new(void); +double _ecore_timer_next_get(void); +int _ecore_timers_exists(void); +int _ecore_timer_call(double when); + +void _ecore_idler_shutdown(void); +int _ecore_idler_call(void); +int _ecore_idler_exist(void); + +void _ecore_idle_enterer_shutdown(void); +void _ecore_idle_enterer_call(void); +int _ecore_idle_enterer_exist(void); + +void _ecore_idle_exiter_shutdown(void); +void _ecore_idle_exiter_call(void); +int _ecore_idle_exiter_exist(void); + +void _ecore_event_shutdown(void); +int _ecore_event_exist(void); +Ecore_Event *_ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data); +void _ecore_event_call(void); + +Ecore_Timer *_ecore_exe_doomsday_clock_get(Ecore_Exe *exe); +void _ecore_exe_doomsday_clock_set(Ecore_Exe *exe, Ecore_Timer *dc); + +EAPI void *_ecore_event_signal_user_new(void); +void *_ecore_event_signal_hup_new(void); +void *_ecore_event_signal_exit_new(void); +void *_ecore_event_signal_power_new(void); +void *_ecore_event_signal_realtime_new(void); + +void _ecore_main_shutdown(void); + +#ifdef _WIN32 +static inline void _ecore_signal_shutdown(void) { } +static inline void _ecore_signal_init(void) { } +static inline int _ecore_signal_count_get(void) { return 0; } +static inline void _ecore_signal_call(void) { } +#else +void _ecore_signal_shutdown(void); +void _ecore_signal_init(void); +int _ecore_signal_count_get(void); +void _ecore_signal_call(void); +#endif + +void _ecore_exe_init(void); +void _ecore_exe_shutdown(void); +#ifndef _WIN32 +Ecore_Exe *_ecore_exe_find(pid_t pid); +void *_ecore_exe_event_del_new(void); +void _ecore_exe_event_del_free(void *data, void *ev); +#endif + +void _ecore_animator_shutdown(void); + +void _ecore_poller_shutdown(void); + +EAPI void *_ecore_list2_append (void *in_list, void *in_item); +EAPI void *_ecore_list2_prepend (void *in_list, void *in_item); +EAPI void *_ecore_list2_append_relative (void *in_list, void *in_item, void *in_relative); +EAPI void *_ecore_list2_prepend_relative (void *in_list, void *in_item, void *in_relative); +EAPI void *_ecore_list2_remove (void *in_list, void *in_item); +EAPI void *_ecore_list2_find (void *in_list, void *in_item); + +void _ecore_fps_debug_init(void); +void _ecore_fps_debug_shutdown(void); +void _ecore_fps_debug_runtime_add(double t); + +void _ecore_thread_init(void); +void _ecore_thread_shutdown(void); + +void _ecore_glib_init(void); +void _ecore_glib_shutdown(void); + +void _ecore_job_init(void); +void _ecore_job_shutdown(void); + +void _ecore_main_loop_init(void); +void _ecore_main_loop_shutdown(void); + +extern int _ecore_fps_debug; +extern double _ecore_time_loop_time; +extern Eina_Bool _ecore_glib_always_integrate; + +#endif diff --git a/tests/suite/ecore/src/lib/ecore_signal.c b/tests/suite/ecore/src/lib/ecore_signal.c new file mode 100644 index 0000000000..10a4711bed --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_signal.c @@ -0,0 +1,620 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include <assert.h> + +#include "Ecore.h" +#include "ecore_private.h" + +/* make mono happy - this is evil though... */ +#undef SIGPWR +/* valgrind in some versions/setups uses SIGRT's... hmmm */ +#undef SIGRTMIN + +typedef void (*Signal_Handler)(int sig, siginfo_t *si, void *foo); + +static void _ecore_signal_callback_set(int sig, Signal_Handler func); +static void _ecore_signal_callback_ignore(int sig, siginfo_t *si, void *foo); +static void _ecore_signal_callback_sigchld(int sig, siginfo_t *si, void *foo); +static void _ecore_signal_callback_sigusr1(int sig, siginfo_t *si, void *foo); +static void _ecore_signal_callback_sigusr2(int sig, siginfo_t *si, void *foo); +static void _ecore_signal_callback_sighup(int sig, siginfo_t *si, void *foo); +static void _ecore_signal_callback_sigquit(int sig, siginfo_t *si, void *foo); +static void _ecore_signal_callback_sigint(int sig, siginfo_t *si, void *foo); +static void _ecore_signal_callback_sigterm(int sig, siginfo_t *si, void *foo); +#ifdef SIGPWR +static void _ecore_signal_callback_sigpwr(int sig, siginfo_t *si, void *foo); +#endif + +#ifdef SIGRTMIN +static void _ecore_signal_callback_sigrt(int sig, siginfo_t *si, void *foo); +#endif + +static Eina_Bool _ecore_signal_exe_exit_delay(void *data); + +//#define MAXSIGQ 256 // 32k +#define MAXSIGQ 64 // 8k + +static volatile sig_atomic_t sig_count = 0; +static volatile sig_atomic_t sigchld_count = 0; +static volatile sig_atomic_t sigusr1_count = 0; +static volatile sig_atomic_t sigusr2_count = 0; +static volatile sig_atomic_t sighup_count = 0; +static volatile sig_atomic_t sigquit_count = 0; +static volatile sig_atomic_t sigint_count = 0; +static volatile sig_atomic_t sigterm_count = 0; +#ifdef SIGPWR +static volatile sig_atomic_t sigpwr_count = 0; +#endif +#ifdef SIGRTMIN +static volatile sig_atomic_t *sigrt_count = NULL; +#endif + +static volatile siginfo_t sigchld_info[MAXSIGQ]; +static volatile siginfo_t sigusr1_info[MAXSIGQ]; +static volatile siginfo_t sigusr2_info[MAXSIGQ]; +static volatile siginfo_t sighup_info[MAXSIGQ]; +static volatile siginfo_t sigquit_info[MAXSIGQ]; +static volatile siginfo_t sigint_info[MAXSIGQ]; +static volatile siginfo_t sigterm_info[MAXSIGQ]; +#ifdef SIGPWR +static volatile siginfo_t sigpwr_info[MAXSIGQ]; +#endif +#ifdef SIGRTMIN +static volatile siginfo_t *sigrt_info[MAXSIGQ]; +#endif + +void +_ecore_signal_shutdown(void) +{ +#ifdef SIGRTMIN + int i, num = SIGRTMAX - SIGRTMIN; +#endif + + _ecore_signal_callback_set(SIGPIPE, (Signal_Handler) SIG_DFL); + _ecore_signal_callback_set(SIGALRM, (Signal_Handler) SIG_DFL); + _ecore_signal_callback_set(SIGCHLD, (Signal_Handler) SIG_DFL); + _ecore_signal_callback_set(SIGUSR1, (Signal_Handler) SIG_DFL); + _ecore_signal_callback_set(SIGUSR2, (Signal_Handler) SIG_DFL); + _ecore_signal_callback_set(SIGHUP, (Signal_Handler) SIG_DFL); + _ecore_signal_callback_set(SIGQUIT, (Signal_Handler) SIG_DFL); + _ecore_signal_callback_set(SIGINT, (Signal_Handler) SIG_DFL); + _ecore_signal_callback_set(SIGTERM, (Signal_Handler) SIG_DFL); +#ifdef SIGPWR + _ecore_signal_callback_set(SIGPWR, (Signal_Handler) SIG_DFL); + sigpwr_count = 0; +#endif + sigchld_count = 0; + sigusr1_count = 0; + sigusr2_count = 0; + sighup_count = 0; + sigquit_count = 0; + sigint_count = 0; + sigterm_count = 0; + sig_count = 0; + +#ifdef SIGRTMIN + for (i = 0; i < num; i++) + { + _ecore_signal_callback_set(SIGRTMIN + i, (Signal_Handler) SIG_DFL); + sigrt_count[i] = 0; + } + + if (sigrt_count) + { + free((sig_atomic_t *) sigrt_count); + sigrt_count = NULL; + } + + for (i = 0; i < MAXSIGQ; i++) + { + if (sigrt_info[i]) + { + free((siginfo_t *) sigrt_info[i]); + sigrt_info[i] = NULL; + } + } +#endif +} + +void +_ecore_signal_init(void) +{ +#ifdef SIGRTMIN + int i, num = SIGRTMAX - SIGRTMIN; +#endif + + _ecore_signal_callback_set(SIGPIPE, _ecore_signal_callback_ignore); + _ecore_signal_callback_set(SIGALRM, _ecore_signal_callback_ignore); + _ecore_signal_callback_set(SIGCHLD, _ecore_signal_callback_sigchld); + _ecore_signal_callback_set(SIGUSR1, _ecore_signal_callback_sigusr1); + _ecore_signal_callback_set(SIGUSR2, _ecore_signal_callback_sigusr2); + _ecore_signal_callback_set(SIGHUP, _ecore_signal_callback_sighup); + _ecore_signal_callback_set(SIGQUIT, _ecore_signal_callback_sigquit); + _ecore_signal_callback_set(SIGINT, _ecore_signal_callback_sigint); + _ecore_signal_callback_set(SIGTERM, _ecore_signal_callback_sigterm); +#ifdef SIGPWR + _ecore_signal_callback_set(SIGPWR, _ecore_signal_callback_sigpwr); +#endif + +#ifdef SIGRTMIN + sigrt_count = calloc(1, sizeof(sig_atomic_t) * num); + assert(sigrt_count); + + for (i = 0; i < MAXSIGQ; i++) + { + sigrt_info[i] = calloc(1, sizeof(siginfo_t) * num); + assert(sigrt_info[i]); + } + + for (i = 0; i < num; i++) + _ecore_signal_callback_set(SIGRTMIN + i, _ecore_signal_callback_sigrt); +#endif +} + +int +_ecore_signal_count_get(void) +{ + return sig_count; +} + +void +_ecore_signal_call(void) +{ +#ifdef SIGRTMIN + int i, num = SIGRTMAX - SIGRTMIN; +#endif + volatile sig_atomic_t n; + sigset_t oldset, newset; + + if (sig_count == 0) return; + sigemptyset(&newset); + sigaddset(&newset, SIGPIPE); + sigaddset(&newset, SIGALRM); + sigaddset(&newset, SIGCHLD); + sigaddset(&newset, SIGUSR1); + sigaddset(&newset, SIGUSR2); + sigaddset(&newset, SIGHUP); + sigaddset(&newset, SIGQUIT); + sigaddset(&newset, SIGINT); + sigaddset(&newset, SIGTERM); +#ifdef SIGPWR + sigaddset(&newset, SIGPWR); +#endif +#ifdef SIGRTMIN + for (i = 0; i < num; i++) + sigaddset(&newset, SIGRTMIN + i); +#endif + sigprocmask(SIG_BLOCK, &newset, &oldset); + if (sigchld_count > MAXSIGQ) + WRN("%i SIGCHLD in queue. max queue size %i. losing " + "siginfo for extra signals.", sigchld_count, MAXSIGQ); + for (n = 0; n < sigchld_count; n++) + { + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + { + Ecore_Exe_Event_Del *e; + + /* FIXME: If this process is set respawn, respawn with a suitable backoff + * period for those that need too much respawning. + */ + e = _ecore_exe_event_del_new(); + if (e) + { + if (WIFEXITED(status)) + { + e->exit_code = WEXITSTATUS(status); + e->exited = 1; + } + else if (WIFSIGNALED(status)) + { + e->exit_signal = WTERMSIG(status); + e->signalled = 1; + } + e->pid = pid; + e->exe = _ecore_exe_find(pid); + + if ((n < MAXSIGQ) && (sigchld_info[n].si_signo)) + e->data = sigchld_info[n]; /* No need to clone this. */ + + if ((e->exe) && (ecore_exe_flags_get(e->exe) & (ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR))) + { + /* We want to report the Last Words of the exe, so delay this event. + * This is twice as relevant for stderr. + * There are three possibilities here - + * 1 There are no Last Words. + * 2 There are Last Words, they are not ready to be read. + * 3 There are Last Words, they are ready to be read. + * + * For 1 we don't want to delay, for 3 we want to delay. + * 2 is the problem. If we check for data now and there + * is none, then there is no way to differentiate 1 and 2. + * If we don't delay, we may loose data, but if we do delay, + * there may not be data and the exit event never gets sent. + * + * Any way you look at it, there has to be some time passed + * before the exit event gets sent. So the strategy here is + * to setup a timer event that will send the exit event after + * an arbitrary, but brief, time. + * + * This is probably paranoid, for the less paraniod, we could + * check to see for Last Words, and only delay if there are any. + * This has it's own set of problems. + */ + Ecore_Timer *doomsday_clock; + + doomsday_clock = _ecore_exe_doomsday_clock_get(e->exe); + IF_FN_DEL(ecore_timer_del, doomsday_clock); + _ecore_exe_doomsday_clock_set(e->exe, ecore_timer_add(0.1, _ecore_signal_exe_exit_delay, e)); + } + else + { + _ecore_event_add(ECORE_EXE_EVENT_DEL, e, + _ecore_exe_event_del_free, NULL); + } + } + } + sig_count--; + } + sigchld_count = 0; + + if (sigusr1_count > MAXSIGQ) + WRN("%i SIGUSR1 in queue. max queue size %i. losing " + "siginfo for extra signals.", sigusr1_count, MAXSIGQ); + for (n = 0; n < sigusr1_count; n++) + { + Ecore_Event_Signal_User *e; + + e = _ecore_event_signal_user_new(); + if (e) + { + e->number = 1; + + if ((n < MAXSIGQ) && (sigusr1_info[n].si_signo)) + e->data = sigusr1_info[n]; + + ecore_event_add(ECORE_EVENT_SIGNAL_USER, e, NULL, NULL); + } + sig_count--; + } + sigusr1_count = 0; + + if (sigusr2_count > MAXSIGQ) + WRN("%i SIGUSR2 in queue. max queue size %i. losing " + "siginfo for extra signals.", sigusr2_count, MAXSIGQ); + for (n = 0; n < sigusr2_count; n++) + { + Ecore_Event_Signal_User *e; + + e = _ecore_event_signal_user_new(); + if (e) + { + e->number = 2; + + if ((n < MAXSIGQ) && (sigusr2_info[n].si_signo)) + e->data = sigusr2_info[n]; + + ecore_event_add(ECORE_EVENT_SIGNAL_USER, e, NULL, NULL); + } + sig_count--; + } + sigusr2_count = 0; + + if (sighup_count > MAXSIGQ) + WRN("%i SIGHUP in queue. max queue size %i. losing " + "siginfo for extra signals.", sighup_count, MAXSIGQ); + for (n = 0; n < sighup_count; n++) + { + Ecore_Event_Signal_Hup *e; + + e = _ecore_event_signal_hup_new(); + if (e) + { + if ((n < MAXSIGQ) && (sighup_info[n].si_signo)) + e->data = sighup_info[n]; + + ecore_event_add(ECORE_EVENT_SIGNAL_HUP, e, NULL, NULL); + } + sig_count--; + } + sighup_count = 0; + + if (sigquit_count > MAXSIGQ) + WRN("%i SIGQUIT in queue. max queue size %i. losing " + "siginfo for extra signals.", sigquit_count, MAXSIGQ); + for (n = 0; n < sigquit_count; n++) + { + Ecore_Event_Signal_Exit *e; + + e = _ecore_event_signal_exit_new(); + if (e) + { + e->quit = 1; + + if ((n < MAXSIGQ) && (sigquit_info[n].si_signo)) + e->data = sigquit_info[n]; + + ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, e, NULL, NULL); + } + sig_count--; + } + sigquit_count = 0; + + if (sigint_count > MAXSIGQ) + WRN("%i SIGINT in queue. max queue size %i. losing " + "siginfo for extra signals.", sigint_count, MAXSIGQ); + for (n = 0; n < sigint_count; n++) + { + Ecore_Event_Signal_Exit *e; + + e = _ecore_event_signal_exit_new(); + if (e) + { + e->interrupt = 1; + + if ((n < MAXSIGQ) && (sigint_info[n].si_signo)) + e->data = sigint_info[n]; + + ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, e, NULL, NULL); + } + sig_count--; + } + sigint_count = 0; + + if (sigterm_count > MAXSIGQ) + WRN("%i SIGTERM in queue. max queue size %i. losing " + "siginfo for extra signals.", sigterm_count, MAXSIGQ); + for (n = 0; n < sigterm_count; n++) + { + Ecore_Event_Signal_Exit *e; + + e = _ecore_event_signal_exit_new(); + if (e) + { + e->terminate = 1; + + if ((n < MAXSIGQ) && (sigterm_info[n].si_signo)) + e->data = sigterm_info[n]; + + ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, e, NULL, NULL); + } + sig_count--; + } + sigterm_count = 0; + +#ifdef SIGPWR + if (sigpwr_count > MAXSIGQ) + WRN("%i SIGPWR in queue. max queue size %i. losing " + "siginfo for extra signals.", sigpwr_count, MAXSIGQ); + for (n = 0; n < sigpwr_count; n++) + { + Ecore_Event_Signal_Power *e; + + e = _ecore_event_signal_power_new(); + if (e) + { + if ((n < MAXSIGQ) && (sigpwr_info[n].si_signo)) + e->data = sigpwr_info[n]; + + ecore_event_add(ECORE_EVENT_SIGNAL_POWER, e, NULL, NULL); + } + sig_count--; + } + sigpwr_count = 0; +#endif + +#ifdef SIGRTMIN + for (i = 0; i < num; i++) + { + if (sigrt_count[i] > MAXSIGQ) + WRN("%i SIGRT%i in queue. max queue size %i. losing " + "siginfo for extra signals.", i + 1, sigrt_count[i], MAXSIGQ); + for (n = 0; n < sigrt_count[i]; n++) + { + Ecore_Event_Signal_Realtime *e; + + if ((e = _ecore_event_signal_realtime_new())) + { + e->num = i; + + if ((n < MAXSIGQ) && (sigrt_info[n][i].si_signo)) + e->data = sigrt_info[n][i]; + + ecore_event_add(ECORE_EVENT_SIGNAL_REALTIME, e, NULL, NULL); + } + sig_count--; + } + sigrt_count[i] = 0; + } +#endif + sigprocmask(SIG_SETMASK, &oldset, NULL); +} + +static void +_ecore_signal_callback_set(int sig, Signal_Handler func) +{ + struct sigaction sa; + + sa.sa_sigaction = func; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sigaction(sig, &sa, NULL); +} + +static void +_ecore_signal_callback_ignore(int sig __UNUSED__, siginfo_t *si __UNUSED__, void *foo __UNUSED__) +{ +} + +static void +_ecore_signal_callback_sigchld(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sigchld_info[n] = *si; + else + sigchld_info[n].si_signo = 0; + } + + sigchld_count++; + sig_count++; +} + +static void +_ecore_signal_callback_sigusr1(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sigusr1_info[n] = *si; + else + sigusr1_info[n].si_signo = 0; + } + sigusr1_count++; + sig_count++; +} + +static void +_ecore_signal_callback_sigusr2(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sigusr2_info[n] = *si; + else + sigusr2_info[n].si_signo = 0; + } + sigusr2_count++; + sig_count++; +} + +static void +_ecore_signal_callback_sighup(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sighup_info[n] = *si; + else + sighup_info[n].si_signo = 0; + } + sighup_count++; + sig_count++; +} + +static void +_ecore_signal_callback_sigquit(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sigquit_info[n] = *si; + else + sigquit_info[n].si_signo = 0; + } + sigquit_count++; + sig_count++; +} + +static void +_ecore_signal_callback_sigint(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sigint_info[n] = *si; + else + sigint_info[n].si_signo = 0; + } + sigint_count++; + sig_count++; +} + +static void +_ecore_signal_callback_sigterm(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sigterm_info[n] = *si; + else + sigterm_info[n].si_signo = 0; + } + sigterm_count++; + sig_count++; +} + +#ifdef SIGPWR +static void +_ecore_signal_callback_sigpwr(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sigpwr_info[n] = *si; + else + sigpwr_info[n].si_signo = 0; + } + sigpwr_count++; + sig_count++; +} +#endif + +#ifdef SIGRTMIN +static void +_ecore_signal_callback_sigrt(int sig, siginfo_t *si, void *foo __UNUSED__) +{ + volatile sig_atomic_t n; + n = sigchld_count; + if (n < MAXSIGQ) + { + if (si) + sigrt_info[n][sig - SIGRTMIN] = *si; + else + sigrt_info[n][sig - SIGRTMIN].si_signo = 0; + } + sigrt_count[sig - SIGRTMIN]++; + sig_count++; +} +#endif + +static Eina_Bool +_ecore_signal_exe_exit_delay(void *data) +{ + Ecore_Exe_Event_Del *e; + + e = data; + if (e) + { + _ecore_exe_doomsday_clock_set(e->exe, NULL); + _ecore_event_add(ECORE_EXE_EVENT_DEL, e, + _ecore_exe_event_del_free, NULL); + } + return ECORE_CALLBACK_CANCEL; +} diff --git a/tests/suite/ecore/src/lib/ecore_thread.c b/tests/suite/ecore/src/lib/ecore_thread.c new file mode 100644 index 0000000000..c8a5daf190 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_thread.c @@ -0,0 +1,1226 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#ifdef EFL_HAVE_PTHREAD +# include <pthread.h> +# ifdef __linux__ +# ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +# endif +# include <sched.h> +# include <sys/time.h> +# include <sys/resource.h> +# include <unistd.h> +# include <sys/syscall.h> +# include <errno.h> +# endif +#endif + +#include "Ecore.h" +#include "ecore_private.h" + +typedef struct _Ecore_Pthread_Worker Ecore_Pthread_Worker; +typedef struct _Ecore_Pthread Ecore_Pthread; +typedef struct _Ecore_Thread_Data Ecore_Thread_Data; + +struct _Ecore_Thread_Data +{ + void *data; + Eina_Free_Cb cb; +}; + +struct _Ecore_Pthread_Worker +{ + union { + struct { + Ecore_Cb func_blocking; + } short_run; + struct { + Ecore_Thread_Heavy_Cb func_heavy; + Ecore_Thread_Notify_Cb func_notify; + Ecore_Pipe *notify; + } feedback_run; + } u; + + Ecore_Cb func_cancel; + Ecore_Cb func_end; +#ifdef EFL_HAVE_PTHREAD + pthread_t self; + Eina_Hash *hash; + pthread_cond_t cond; + pthread_mutex_t mutex; +#endif + + const void *data; + + Eina_Bool cancel : 1; + Eina_Bool feedback_run : 1; +}; + +#ifdef EFL_HAVE_PTHREAD +typedef struct _Ecore_Pthread_Data Ecore_Pthread_Data; + +struct _Ecore_Pthread_Data +{ + Ecore_Pipe *p; + void *data; + pthread_t thread; +}; +#endif + +static int _ecore_thread_count_max = 0; +static int ECORE_THREAD_PIPE_DEL = 0; + +#ifdef EFL_HAVE_PTHREAD +static int _ecore_thread_count = 0; + +static Eina_List *_ecore_active_job_threads = NULL; +static Eina_List *_ecore_pending_job_threads = NULL; +static Eina_List *_ecore_pending_job_threads_feedback = NULL; +static Ecore_Event_Handler *del_handler = NULL; +static pthread_mutex_t _ecore_pending_job_threads_mutex = PTHREAD_MUTEX_INITIALIZER; + +static Eina_Hash *_ecore_thread_global_hash = NULL; +static pthread_rwlock_t _ecore_thread_global_hash_lock = PTHREAD_RWLOCK_INITIALIZER; +static pthread_mutex_t _ecore_thread_global_hash_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t _ecore_thread_global_hash_cond = PTHREAD_COND_INITIALIZER; +static pthread_t main_loop_thread; +static Eina_Bool have_main_loop_thread = 0; +static void +_ecore_thread_data_free(void *data) +{ + Ecore_Thread_Data *d = data; + + if (d->cb) d->cb(d->data); + free(d); +} + +static void +_ecore_thread_pipe_free(void *data __UNUSED__, void *event) +{ + Ecore_Pipe *p = event; + + ecore_pipe_del(p); +} + +static Eina_Bool +_ecore_thread_pipe_del(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__) +{ + /* This is a hack to delay pipe destruction until we are out of its internal loop. */ + return ECORE_CALLBACK_CANCEL; +} + +static void +_ecore_thread_end(Ecore_Pthread_Data *pth) +{ + Ecore_Pipe *p; + + if (pthread_join(pth->thread, (void **) &p) != 0) + return ; + + _ecore_active_job_threads = eina_list_remove(_ecore_active_job_threads, pth); + + ecore_event_add(ECORE_THREAD_PIPE_DEL, pth->p, _ecore_thread_pipe_free, NULL); + free(pth); +} + +static void +_ecore_thread_handler(void *data __UNUSED__, void *buffer, unsigned int nbyte) +{ + Ecore_Pthread_Worker *work; + + if (nbyte != sizeof (Ecore_Pthread_Worker *)) return ; + + work = *(Ecore_Pthread_Worker **)buffer; + + if (work->cancel) + { + if (work->func_cancel) + work->func_cancel((void *) work->data); + } + else + { + if (work->func_end) + work->func_end((void *) work->data); + } + + if (work->feedback_run) + ecore_pipe_del(work->u.feedback_run.notify); + pthread_cond_destroy(&work->cond); + pthread_mutex_destroy(&work->mutex); + if (work->hash) + eina_hash_free(work->hash); + free(work); +} + +static void +_ecore_notify_handler(void *data, void *buffer, unsigned int nbyte) +{ + Ecore_Pthread_Worker *work = data; + void *user_data; + + if (nbyte != sizeof (Ecore_Pthread_Worker *)) return ; + + user_data = *(void **)buffer; + + if (work->u.feedback_run.func_notify) + work->u.feedback_run.func_notify((Ecore_Thread *) work, user_data, (void *) work->data); +} + +static void +_ecore_short_job(Ecore_Pipe *end_pipe) +{ + Ecore_Pthread_Worker *work; + + while (_ecore_pending_job_threads) + { + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + + if (!_ecore_pending_job_threads) + { + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + break; + } + + work = eina_list_data_get(_ecore_pending_job_threads); + _ecore_pending_job_threads = eina_list_remove_list(_ecore_pending_job_threads, _ecore_pending_job_threads); + + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + if (!work->cancel) + work->u.short_run.func_blocking((void *) work->data); + + ecore_pipe_write(end_pipe, &work, sizeof (Ecore_Pthread_Worker *)); + } +} + +static void +_ecore_feedback_job(Ecore_Pipe *end_pipe, pthread_t thread) +{ + Ecore_Pthread_Worker *work; + + while (_ecore_pending_job_threads_feedback) + { + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + + if (!_ecore_pending_job_threads_feedback) + { + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + break; + } + + work = eina_list_data_get(_ecore_pending_job_threads_feedback); + _ecore_pending_job_threads_feedback = eina_list_remove_list(_ecore_pending_job_threads_feedback, _ecore_pending_job_threads_feedback); + + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + work->self = thread; + if (!work->cancel) + work->u.feedback_run.func_heavy((Ecore_Thread *) work, (void *) work->data); + + ecore_pipe_write(end_pipe, &work, sizeof (Ecore_Pthread_Worker *)); + } +} + +static void * +_ecore_direct_worker(Ecore_Pthread_Worker *work) +{ + Ecore_Pthread_Data *pth; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + eina_sched_prio_drop(); + + pth = malloc(sizeof (Ecore_Pthread_Data)); + if (!pth) return NULL; + + pth->p = ecore_pipe_add(_ecore_thread_handler, NULL); + if (!pth->p) + { + free(pth); + return NULL; + } + pth->thread = pthread_self(); + + work->self = pth->thread; + work->u.feedback_run.func_heavy((Ecore_Thread *) work, (void *) work->data); + + ecore_pipe_write(pth->p, &work, sizeof (Ecore_Pthread_Worker *)); + + work = malloc(sizeof (Ecore_Pthread_Worker)); + if (!work) + { + ecore_pipe_del(pth->p); + free(pth); + return NULL; + } + + work->data = pth; + work->u.short_run.func_blocking = NULL; + work->func_end = (void *) _ecore_thread_end; + work->func_cancel = NULL; + work->cancel = EINA_FALSE; + work->feedback_run = EINA_FALSE; + work->hash = NULL; + pthread_cond_init(&work->cond, NULL); + pthread_mutex_init(&work->mutex, NULL); + + ecore_pipe_write(pth->p, &work, sizeof (Ecore_Pthread_Worker *)); + + return pth->p; +} + +static void * +_ecore_thread_worker(Ecore_Pthread_Data *pth) +{ + Ecore_Pthread_Worker *work; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + eina_sched_prio_drop(); + + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + _ecore_thread_count++; + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + on_error: + if (_ecore_pending_job_threads) _ecore_short_job(pth->p); + if (_ecore_pending_job_threads_feedback) _ecore_feedback_job(pth->p, pth->thread); + + /* FIXME: Check if there is feedback running task todo, and switch to feedback run handler. */ + + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + if (_ecore_pending_job_threads) + { + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + goto on_error; + } + if (_ecore_pending_job_threads_feedback) + { + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + goto on_error; + } + + _ecore_thread_count--; + + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + work = malloc(sizeof (Ecore_Pthread_Worker)); + if (!work) return NULL; + + work->data = pth; + work->u.short_run.func_blocking = NULL; + work->func_end = (void *) _ecore_thread_end; + work->func_cancel = NULL; + work->cancel = EINA_FALSE; + work->feedback_run = EINA_FALSE; + work->hash = NULL; + pthread_cond_init(&work->cond, NULL); + pthread_mutex_init(&work->mutex, NULL); + + ecore_pipe_write(pth->p, &work, sizeof (Ecore_Pthread_Worker *)); + + return pth->p; +} + +#endif + +void +_ecore_thread_init(void) +{ + _ecore_thread_count_max = eina_cpu_count(); + if (_ecore_thread_count_max <= 0) + _ecore_thread_count_max = 1; + + ECORE_THREAD_PIPE_DEL = ecore_event_type_new(); +#ifdef EFL_HAVE_PTHREAD + del_handler = ecore_event_handler_add(ECORE_THREAD_PIPE_DEL, _ecore_thread_pipe_del, NULL); + main_loop_thread = pthread_self(); + have_main_loop_thread = 1; +#endif +} + +void +_ecore_thread_shutdown(void) +{ + /* FIXME: If function are still running in the background, should we kill them ? */ +#ifdef EFL_HAVE_PTHREAD + Ecore_Pthread_Worker *work; + Ecore_Pthread_Data *pth; + + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + + EINA_LIST_FREE(_ecore_pending_job_threads, work) + { + if (work->func_cancel) + work->func_cancel((void *)work->data); + free(work); + } + + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + EINA_LIST_FREE(_ecore_active_job_threads, pth) + { + Ecore_Pipe *p; + + pthread_cancel(pth->thread); + pthread_join(pth->thread, (void **) &p); + + ecore_pipe_del(pth->p); + } + if (_ecore_thread_global_hash) + eina_hash_free(_ecore_thread_global_hash); + ecore_event_handler_del(del_handler); + have_main_loop_thread = 0; + del_handler = NULL; +#endif +} +/** + * @addtogroup Ecore_Thread Ecore Thread Functions + * These functions allow for ecore-managed threads which integrate with ecore's main loop. + * @{ + */ + +/** + * @brief Run some blocking code in a parallel thread to avoid locking the main loop. + * @param func_blocking The function that should run in another thread. + * @param func_end The function that will be called in the main loop if the thread terminate correctly. + * @param func_cancel The function that will be called in the main loop if the thread is cancelled. + * @param data User context data to pass to all callback. + * @return A reference to the newly created thread instance, or NULL if it failed. + * + * ecore_thread_run provide a facility for easily managing blocking task in a + * parallel thread. You should provide three function. The first one, func_blocking, + * that will do the blocking work in another thread (so you should not use the + * EFL in it except Eina if you are careful). The second one, func_end, + * that will be called in Ecore main loop when func_blocking is done. So you + * can use all the EFL inside this function. The last one, func_cancel, will + * be called in the main loop if the thread is cancelled or could not run at all. + * + * Be aware, that you can't make assumption on the result order of func_end + * after many call to ecore_thread_run, as we start as much thread as the + * host CPU can handle. + */ +EAPI Ecore_Thread * +ecore_thread_run(Ecore_Cb func_blocking, + Ecore_Cb func_end, + Ecore_Cb func_cancel, + const void *data) +{ +#ifdef EFL_HAVE_PTHREAD + Ecore_Pthread_Worker *work; + Ecore_Pthread_Data *pth = NULL; + + if (!func_blocking) return NULL; + + work = malloc(sizeof (Ecore_Pthread_Worker)); + if (!work) + { + func_cancel((void *) data); + return NULL; + } + + work->u.short_run.func_blocking = func_blocking; + work->hash = NULL; + pthread_cond_init(&work->cond, NULL); + pthread_mutex_init(&work->mutex, NULL); + work->func_end = func_end; + work->func_cancel = func_cancel; + work->cancel = EINA_FALSE; + work->feedback_run = EINA_FALSE; + work->data = data; + + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + _ecore_pending_job_threads = eina_list_append(_ecore_pending_job_threads, work); + + if (_ecore_thread_count == _ecore_thread_count_max) + { + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + return (Ecore_Thread *) work; + } + + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + /* One more thread could be created. */ + pth = malloc(sizeof (Ecore_Pthread_Data)); + if (!pth) goto on_error; + + pth->p = ecore_pipe_add(_ecore_thread_handler, NULL); + if (!pth->p) goto on_error; + + if (pthread_create(&pth->thread, NULL, (void *) _ecore_thread_worker, pth) == 0) + return (Ecore_Thread *) work; + + on_error: + if (pth) + { + if (pth->p) ecore_pipe_del(pth->p); + free(pth); + } + + if (_ecore_thread_count == 0) + { + if (work->func_cancel) + work->func_cancel((void *) work->data); + free(work); + work = NULL; + } + return (Ecore_Thread *) work; +#else + /* + If no thread and as we don't want to break app that rely on this + facility, we will lock the interface until we are done. + */ + func_blocking((void *)data); + func_end((void *)data); + + return NULL; +#endif +} + +/** + * @brief Cancel a running thread. + * @param thread The thread to cancel. + * @return Will return EINA_TRUE if the thread has been cancelled, + * EINA_FALSE if it is pending. + * + * ecore_thread_cancel give the possibility to cancel a task still running. It + * will return EINA_FALSE, if the destruction is delayed or EINA_TRUE if it is + * cancelled after this call. + * + * You should use this function only in the main loop. + * + * func_end, func_cancel will destroy the handler, so don't use it after. + * And if ecore_thread_cancel return EINA_TRUE, you should not use Ecore_Thread also. + */ +EAPI Eina_Bool +ecore_thread_cancel(Ecore_Thread *thread) +{ +#ifdef EFL_HAVE_PTHREAD + Ecore_Pthread_Worker *work = (Ecore_Pthread_Worker *)thread; + Eina_List *l; + + if (!work) + return EINA_TRUE; + + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + + if ((have_main_loop_thread) && + (pthread_equal(main_loop_thread, pthread_self()))) + { + EINA_LIST_FOREACH(_ecore_pending_job_threads, l, work) + { + if ((void *) work == (void *) thread) + { + _ecore_pending_job_threads = eina_list_remove_list(_ecore_pending_job_threads, l); + + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + if (work->func_cancel) + work->func_cancel((void *) work->data); + free(work); + + return EINA_TRUE; + } + } + } + + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + /* Delay the destruction */ + ((Ecore_Pthread_Worker *)thread)->cancel = EINA_TRUE; + return EINA_FALSE; +#else + return EINA_TRUE; +#endif +} + +/** + * @brief Tell if a thread was canceled or not. + * @param thread The thread to test. + * @return EINA_TRUE if the thread is cancelled, + * EINA_FALSE if it is not. + * + * You can use this function in main loop and in the thread. + */ +EAPI Eina_Bool +ecore_thread_check(Ecore_Thread *thread) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread; + + if (!worker) return EINA_TRUE; + return worker->cancel; +} + +/** + * @brief Run some heavy code in a parallel thread to avoid locking the main loop. + * @param func_heavy The function that should run in another thread. + * @param func_notify The function that will receive the data send by func_heavy in the main loop. + * @param func_end The function that will be called in the main loop if the thread terminate correctly. + * @param func_cancel The function that will be called in the main loop if the thread is cancelled. + * @param data User context data to pass to all callback. + * @param try_no_queue If you wan't to run outside of the thread pool. + * @return A reference to the newly created thread instance, or NULL if it failed. + * + * ecore_thread_feedback_run provide a facility for easily managing heavy task in a + * parallel thread. You should provide four functions. The first one, func_heavy, + * that will do the heavy work in another thread (so you should not use the + * EFL in it except Eina and Eet if you are careful). The second one, func_notify, + * will receive the data send from the thread function (func_heavy) by ecore_thread_notify + * in the main loop (and so, can use all the EFL). Tje third, func_end, + * that will be called in Ecore main loop when func_heavy is done. So you + * can use all the EFL inside this function. The last one, func_cancel, will + * be called in the main loop also, if the thread is cancelled or could not run at all. + * + * Be aware, that you can't make assumption on the result order of func_end + * after many call to ecore_feedback_run, as we start as much thread as the + * host CPU can handle. + * + * If you set try_no_queue, it will try to run outside of the thread pool, this can bring + * the CPU down, so be careful with that. Of course if it can't start a new thread, it will + * try to use one from the pool. + */ +EAPI Ecore_Thread *ecore_thread_feedback_run(Ecore_Thread_Heavy_Cb func_heavy, + Ecore_Thread_Notify_Cb func_notify, + Ecore_Cb func_end, + Ecore_Cb func_cancel, + const void *data, + Eina_Bool try_no_queue) +{ + +#ifdef EFL_HAVE_PTHREAD + Ecore_Pthread_Worker *worker; + Ecore_Pthread_Data *pth = NULL; + + if (!func_heavy) return NULL; + + worker = malloc(sizeof (Ecore_Pthread_Worker)); + if (!worker) goto on_error; + + worker->u.feedback_run.func_heavy = func_heavy; + worker->u.feedback_run.func_notify = func_notify; + worker->hash = NULL; + pthread_cond_init(&worker->cond, NULL); + pthread_mutex_init(&worker->mutex, NULL); + worker->func_cancel = func_cancel; + worker->func_end = func_end; + worker->data = data; + worker->cancel = EINA_FALSE; + worker->feedback_run = EINA_TRUE; + + worker->u.feedback_run.notify = ecore_pipe_add(_ecore_notify_handler, worker); + + if (!try_no_queue) + { + pthread_t t; + + if (pthread_create(&t, NULL, (void *) _ecore_direct_worker, worker) == 0) + return (Ecore_Thread *) worker; + } + + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + _ecore_pending_job_threads_feedback = eina_list_append(_ecore_pending_job_threads_feedback, worker); + + if (_ecore_thread_count == _ecore_thread_count_max) + { + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + return (Ecore_Thread *) worker; + } + + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + + /* One more thread could be created. */ + pth = malloc(sizeof (Ecore_Pthread_Data)); + if (!pth) goto on_error; + + pth->p = ecore_pipe_add(_ecore_thread_handler, NULL); + if (!pth->p) goto on_error; + + if (pthread_create(&pth->thread, NULL, (void *) _ecore_thread_worker, pth) == 0) + return (Ecore_Thread *) worker; + + on_error: + if (pth) + { + if (pth->p) ecore_pipe_del(pth->p); + free(pth); + } + + if (_ecore_thread_count == 0) + { + if (func_cancel) func_cancel((void *) data); + + if (worker) + { + ecore_pipe_del(worker->u.feedback_run.notify); + free(worker); + worker = NULL; + } + } + + return (Ecore_Thread *) worker; +#else + Ecore_Pthread_Worker worker; + + (void) try_no_queue; + + /* + If no thread and as we don't want to break app that rely on this + facility, we will lock the interface until we are done. + */ + worker.u.feedback_run.func_heavy = func_heavy; + worker.u.feedback_run.func_notify = func_notify; + worker.u.feedback_run.notify = NULL; + worker.func_cancel = func_cancel; + worker.func_end = func_end; + worker.data = data; + worker.cancel = EINA_FALSE; + worker.feedback_run = EINA_TRUE; + + func_heavy((Ecore_Thread *) &worker, (void *)data); + + if (worker.cancel) func_cancel((void *)data); + else func_end((void *)data); + + return NULL; +#endif +} + +/** + * @brief Send data to main loop from worker thread. + * @param thread The current Ecore_Thread context to send data from + * @param data Data to be transmitted to the main loop + * @return EINA_TRUE if data was successfully send to main loop, + * EINA_FALSE if anything goes wrong. + * + * After a succesfull call, the data should be considered owned + * by the main loop. + * + * You should use this function only in the func_heavy call. + */ +EAPI Eina_Bool +ecore_thread_feedback(Ecore_Thread *thread, const void *data) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread; + + if (!worker) return EINA_FALSE; + if (!worker->feedback_run) return EINA_FALSE; + +#ifdef EFL_HAVE_PTHREAD + if (!pthread_equal(worker->self, pthread_self())) return EINA_FALSE; + + ecore_pipe_write(worker->u.feedback_run.notify, &data, sizeof (void *)); + + return EINA_TRUE; +#else + worker->u.feedback_run.func_notify(thread, (void*) data, (void*) worker->data); + + return EINA_TRUE; +#endif +} + +/** + * @brief Get number of active thread jobs + * @return Number of active threads running jobs + * This returns the number of threads currently running jobs through the + * ecore_thread api. + */ +EAPI int +ecore_thread_active_get(void) +{ +#ifdef EFL_HAVE_PTHREAD + return _ecore_thread_count; +#else + return 0; +#endif +} + +/** + * @brief Get number of pending (short) thread jobs + * @return Number of pending threads running "short" jobs + * This returns the number of threads currently running jobs through the + * ecore_thread_run api call. + */ +EAPI int +ecore_thread_pending_get(void) +{ + int ret; +#ifdef EFL_HAVE_PTHREAD + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + ret = eina_list_count(_ecore_pending_job_threads); + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + return ret; +#else + return 0; +#endif +} + +/** + * @brief Get number of pending feedback thread jobs + * @return Number of pending threads running "feedback" jobs + * This returns the number of threads currently running jobs through the + * ecore_thread_feedback_run api call. + */ +EAPI int +ecore_thread_pending_feedback_get(void) +{ + int ret; +#ifdef EFL_HAVE_PTHREAD + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + ret = eina_list_count(_ecore_pending_job_threads_feedback); + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + return ret; +#else + return 0; +#endif +} + +/** + * @brief Get number of pending thread jobs + * @return Number of pending threads running jobs + * This returns the number of threads currently running jobs through the + * ecore_thread_run and ecore_thread_feedback_run api calls combined. + */ +EAPI int +ecore_thread_pending_total_get(void) +{ + int ret; +#ifdef EFL_HAVE_PTHREAD + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + ret = eina_list_count(_ecore_pending_job_threads) + eina_list_count(_ecore_pending_job_threads_feedback); + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + return ret; +#else + return 0; +#endif +} + +/** + * @brief Get the max number of threads that can run simultaneously + * @return Max number of threads ecore will run + * This returns the total number of threads that ecore will attempt to run + * simultaneously. + */ +EAPI int +ecore_thread_max_get(void) +{ + return _ecore_thread_count_max; +} + +/** + * @brief Set the max number of threads that can run simultaneously + * @param num The new maximum + * This sets the maximum number of threads that ecore will try to run + * simultaneously. This number cannot be < 1 or >= 2x the number of active cpus. + */ +EAPI void +ecore_thread_max_set(int num) +{ + if (num < 1) return; + /* avoid doing something hilarious by blocking dumb users */ + if (num >= (2 * eina_cpu_count())) return; + + _ecore_thread_count_max = num; +} + +/** + * @brief Reset the max number of threads that can run simultaneously + * This resets the maximum number of threads that ecore will try to run + * simultaneously to the number of active cpus. + */ +EAPI void +ecore_thread_max_reset(void) +{ + _ecore_thread_count_max = eina_cpu_count(); +} + +/** + * @brief Get the number of threads which are available to be used + * @return The number of available threads + * This returns the number of threads slots that ecore has currently available. + * Assuming that you haven't changed the max number of threads with @ref ecore_thread_max_set + * this should be equal to (num_cpus - (active_running + active_feedback_running)) + */ +EAPI int +ecore_thread_available_get(void) +{ + int ret; +#ifdef EFL_HAVE_PTHREAD + pthread_mutex_lock(&_ecore_pending_job_threads_mutex); + ret = _ecore_thread_count_max - _ecore_thread_count; + pthread_mutex_unlock(&_ecore_pending_job_threads_mutex); + return ret; +#else + return 0; +#endif +} + +/** + * @brief Add data to the thread for subsequent use + * @param thread The thread context to add to + * @param key The name string to add the data with + * @param value The data to add + * @param cb The callback to free the data with + * @param direct If true, this will not copy the key string (like eina_hash_direct_add) + * @return EINA_TRUE on success, EINA_FALSE on failure + * This adds data to the thread context, allowing the thread + * to retrieve and use it without complicated mutexing. This function can only be called by a + * *_run thread INSIDE the thread and will return EINA_FALSE in any case but success. + * All data added to the thread will be freed with its associated callback (if present) + * upon thread termination. If no callback is specified, it is expected that the user will free the + * data, but this is most likely not what you want. + */ +EAPI Eina_Bool +ecore_thread_local_data_add(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread; + Ecore_Thread_Data *d; + Eina_Bool ret; + + if ((!thread) || (!key) || (!value)) + return EINA_FALSE; +#ifdef EFL_HAVE_PTHREAD + if (!pthread_equal(worker->self, pthread_self())) return EINA_FALSE; + + if (!worker->hash) + worker->hash = eina_hash_string_small_new(_ecore_thread_data_free); + + if (!worker->hash) + return EINA_FALSE; + + if (!(d = malloc(sizeof(Ecore_Thread_Data)))) + return EINA_FALSE; + + d->data = value; + d->cb = cb; + + if (direct) + ret = eina_hash_direct_add(worker->hash, key, d); + else + ret = eina_hash_add(worker->hash, key, d); + pthread_cond_broadcast(&worker->cond); + return ret; +#else + return EINA_TRUE; +#endif +} + +/** + * @brief Modify data in the thread, or add if not found + * @param thread The thread context + * @param key The name string to add the data with + * @param value The data to add + * @param cb The callback to free the data with + * @return The old data associated with @p key on success if modified, NULL if added + * This adds/modifies data in the thread context, adding only if modify fails. + * This function can only be called by a *_run thread INSIDE the thread. + * All data added to the thread pool will be freed with its associated callback (if present) + * upon thread termination. If no callback is specified, it is expected that the user will free the + * data, but this is most likely not what you want. + */ +EAPI void * +ecore_thread_local_data_set(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread; + Ecore_Thread_Data *d, *r; + void *ret; + if ((!thread) || (!key) || (!value)) + return NULL; +#ifdef EFL_HAVE_PTHREAD + if (!pthread_equal(worker->self, pthread_self())) return NULL; + + if (!worker->hash) + worker->hash = eina_hash_string_small_new(_ecore_thread_data_free); + + if (!worker->hash) + return NULL; + + if (!(d = malloc(sizeof(Ecore_Thread_Data)))) + return NULL; + + d->data = value; + d->cb = cb; + + r = eina_hash_set(worker->hash, key, d); + pthread_cond_broadcast(&worker->cond); + ret = r->data; + free(r); + return ret; +#else + return NULL; +#endif +} + +/** + * @brief Find data in the thread's data + * @param thread The thread context + * @param key The name string the data is associated with + * @return The value, or NULL on error + * This finds data in the thread context that has been previously added with @ref ecore_thread_local_data_add + * This function can only be called by a *_run thread INSIDE the thread, and will return NULL + * in any case but success. + */ + +EAPI void * +ecore_thread_local_data_find(Ecore_Thread *thread, const char *key) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread; + Ecore_Thread_Data *d; + + if ((!thread) || (!key)) + return NULL; +#ifdef EFL_HAVE_PTHREAD + if (!pthread_equal(worker->self, pthread_self())) return NULL; + + if (!worker->hash) + return NULL; + + d = eina_hash_find(worker->hash, key); + return d->data; +#else + return NULL; +#endif +} + +/** + * @brief Delete data from the thread's data + * @param thread The thread context + * @param key The name string the data is associated with + * @return EINA_TRUE on success, EINA_FALSE on failure + * This deletes the data pointer from the thread context which was previously added with @ref ecore_thread_local_data_add + * This function can only be called by a *_run thread INSIDE the thread, and will return EINA_FALSE + * in any case but success. Note that this WILL free the data if a callback was specified. + */ +EAPI Eina_Bool +ecore_thread_local_data_del(Ecore_Thread *thread, const char *key) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread; + Ecore_Thread_Data *d; + if ((!thread) || (!key)) + return EINA_FALSE; +#ifdef EFL_HAVE_PTHREAD + if (!pthread_equal(worker->self, pthread_self())) return EINA_FALSE; + + if (!worker->hash) + return EINA_FALSE; + if ((d = eina_hash_find(worker->hash, key))) + _ecore_thread_data_free(d); + return eina_hash_del_by_key(worker->hash, key); +#else + return EINA_TRUE; +#endif +} + +/** + * @brief Add data to the global data + * @param key The name string to add the data with + * @param value The data to add + * @param cb The optional callback to free the data with once ecore is shut down + * @param direct If true, this will not copy the key string (like eina_hash_direct_add) + * @return EINA_TRUE on success, EINA_FALSE on failure + * This adds data to the global thread data, and will return EINA_FALSE in any case but success. + * All data added to global can be manually freed, or a callback can be provided with @p cb which will + * be called upon ecore_thread shutting down. Note that if you have manually freed data that a callback + * was specified for, you will most likely encounter a segv later on. + */ +EAPI Eina_Bool +ecore_thread_global_data_add(const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct) +{ + Eina_Bool ret; + Ecore_Thread_Data *d; + + if ((!key) || (!value)) + return EINA_FALSE; +#ifdef EFL_HAVE_PTHREAD + pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock); + if (!_ecore_thread_global_hash) + _ecore_thread_global_hash = eina_hash_string_small_new(_ecore_thread_data_free); + pthread_rwlock_unlock(&_ecore_thread_global_hash_lock); + + if (!(d = malloc(sizeof(Ecore_Thread_Data)))) + return EINA_FALSE; + + d->data = value; + d->cb = cb; + + if (!_ecore_thread_global_hash) + return EINA_FALSE; + pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock); + if (direct) + ret = eina_hash_direct_add(_ecore_thread_global_hash, key, d); + else + ret = eina_hash_add(_ecore_thread_global_hash, key, d); + pthread_rwlock_unlock(&_ecore_thread_global_hash_lock); + pthread_cond_broadcast(&_ecore_thread_global_hash_cond); + return ret; +#else + return EINA_TRUE; +#endif +} + +/** + * @brief Add data to the global data + * @param key The name string to add the data with + * @param value The data to add + * @param cb The optional callback to free the data with once ecore is shut down + * @return An Ecore_Thread_Data on success, NULL on failure + * This adds data to the global thread data and returns NULL, or replaces the previous data + * associated with @p key and returning the previous data if it existed. To see if an error occurred, + * one must use eina_error_get. + * All data added to global can be manually freed, or a callback can be provided with @p cb which will + * be called upon ecore_thread shutting down. Note that if you have manually freed data that a callback + * was specified for, you will most likely encounter a segv later on. + */ +EAPI void * +ecore_thread_global_data_set(const char *key, void *value, Eina_Free_Cb cb) +{ + Ecore_Thread_Data *d, *r; + void *ret; + + if ((!key) || (!value)) + return NULL; +#ifdef EFL_HAVE_PTHREAD + pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock); + if (!_ecore_thread_global_hash) + _ecore_thread_global_hash = eina_hash_string_small_new(_ecore_thread_data_free); + pthread_rwlock_unlock(&_ecore_thread_global_hash_lock); + + if (!_ecore_thread_global_hash) + return NULL; + + if (!(d = malloc(sizeof(Ecore_Thread_Data)))) + return NULL; + + d->data = value; + d->cb = cb; + + pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock); + r = eina_hash_set(_ecore_thread_global_hash, key, d); + pthread_rwlock_unlock(&_ecore_thread_global_hash_lock); + pthread_cond_broadcast(&_ecore_thread_global_hash_cond); + + ret = r->data; + free(r); + return ret; +#else + return NULL; +#endif +} + +/** + * @brief Find data in the global data + * @param key The name string the data is associated with + * @return The value, or NULL on error + * This finds data in the global data that has been previously added with @ref ecore_thread_global_data_add + * This function will return NULL in any case but success. + * All data added to global can be manually freed, or a callback can be provided with @p cb which will + * be called upon ecore_thread shutting down. Note that if you have manually freed data that a callback + * was specified for, you will most likely encounter a segv later on. + * @note Keep in mind that the data returned can be used by multiple threads at a time, so you will most likely want to mutex + * if you will be doing anything with it. + */ + +EAPI void * +ecore_thread_global_data_find(const char *key) +{ + Ecore_Thread_Data *ret; + if (!key) + return NULL; +#ifdef EFL_HAVE_PTHREAD + if (!_ecore_thread_global_hash) return NULL; + + pthread_rwlock_rdlock(&_ecore_thread_global_hash_lock); + ret = eina_hash_find(_ecore_thread_global_hash, key); + pthread_rwlock_unlock(&_ecore_thread_global_hash_lock); + return ret->data; +#else + return NULL; +#endif +} + +/** + * @brief Delete data from the global data + * @param key The name string the data is associated with + * @return EINA_TRUE on success, EINA_FALSE on failure + * This deletes the data pointer from the global data which was previously added with @ref ecore_thread_global_data_add + * This function will return EINA_FALSE in any case but success. + * Note that this WILL free the data if an @c Eina_Free_Cb was specified when the data was added. + */ +EAPI Eina_Bool +ecore_thread_global_data_del(const char *key) +{ + Eina_Bool ret; + Ecore_Thread_Data *d; + + if (!key) + return EINA_FALSE; +#ifdef EFL_HAVE_PTHREAD + if (!_ecore_thread_global_hash) + return EINA_FALSE; + + pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock); + if ((d = eina_hash_find(_ecore_thread_global_hash, key))) + _ecore_thread_data_free(d); + ret = eina_hash_del_by_key(_ecore_thread_global_hash, key); + pthread_rwlock_unlock(&_ecore_thread_global_hash_lock); + return ret; +#else + return EINA_TRUE; +#endif +} + +/** + * @brief Find data in the global data and optionally wait for the data if not found + * @param key The name string the data is associated with + * @param seconds The amount of time in seconds to wait for the data. If 0, the call will be async and not wait for data. + * If < 0 the call will wait indefinitely for the data. + * @return The value, or NULL on failure + * This finds data in the global data that has been previously added with @ref ecore_thread_global_data_add + * This function will return NULL in any case but success. + * Use @p seconds to specify the amount of time to wait. Use > 0 for an actual wait time, 0 to not wait, and < 0 to wait indefinitely. + * @note Keep in mind that the data returned can be used by multiple threads at a time, so you will most likely want to mutex + * if you will be doing anything with it. + */ +EAPI void * +ecore_thread_global_data_wait(const char *key, double seconds) +{ + double time = 0; + Ecore_Thread_Data *ret = NULL; + if (!key) + return NULL; +#ifdef EFL_HAVE_PTHREAD + if (!_ecore_thread_global_hash) + return NULL; + if (seconds > 0) + time = ecore_time_get() + seconds; + + while (1) + { + struct timespec t = { 0, 0 }; + + t.tv_sec = (long int)time; + t.tv_nsec = (long int)((time - (double)t.tv_sec) * 1000000000); + pthread_rwlock_rdlock(&_ecore_thread_global_hash_lock); + ret = eina_hash_find(_ecore_thread_global_hash, key); + pthread_rwlock_unlock(&_ecore_thread_global_hash_lock); + if ((ret) || (!seconds) || ((seconds > 0) && (time <= ecore_time_get()))) + break; + pthread_mutex_lock(&_ecore_thread_global_hash_mutex); + pthread_cond_timedwait(&_ecore_thread_global_hash_cond, &_ecore_thread_global_hash_mutex, &t); + pthread_mutex_unlock(&_ecore_thread_global_hash_mutex); + } + if (ret) return ret->data; + return NULL; +#else + return NULL; +#endif +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/ecore_time.c b/tests/suite/ecore/src/lib/ecore_time.c new file mode 100644 index 0000000000..1fbb4781f4 --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_time.c @@ -0,0 +1,156 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "Ecore.h" +#include "ecore_private.h" + +#include <time.h> + +#ifdef HAVE_CLOCK_GETTIME +static clockid_t _ecore_time_clock_id = -1; +#endif +double _ecore_time_loop_time = -1.0; + +/** + * Retrieves the current system time as a floating point value in seconds. + * + * This uses a monotonic clock and thus never goes back in time while + * machine is live (even if user changes time or timezone changes, + * however it may be reset whenever the machine is restarted). + * + * @see ecore_loop_time_get(). + * @see ecore_time_unix_get(). + * + * @return The number of seconds. Start time is not defined (it may be + * when the machine was booted, unix time, etc), all it is + * defined is that it never goes backwards (unless you got big critical + * messages when the application started). + * @ingroup Ecore_Time_Group + */ +EAPI double +ecore_time_get(void) +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec t; + + if (EINA_UNLIKELY(_ecore_time_clock_id < 0)) + return ecore_time_unix_get(); + + if (EINA_UNLIKELY(clock_gettime(_ecore_time_clock_id, &t))) + { + CRIT("Cannot get current time."); + /* Try to at least return the latest value retrieved*/ + return _ecore_time_loop_time; + } + + return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0); +#else + return ecore_time_unix_get(); +#endif +} + +/** + * Retrieves the current UNIX time as a floating point value in seconds. + * + * @see ecore_time_get(). + * @see ecore_loop_time_get(). + * + * @return The number of seconds since 12.00AM 1st January 1970. + * @ingroup Ecore_Time_Group + */ +EAPI double +ecore_time_unix_get(void) +{ +#ifdef HAVE_EVIL + return evil_time_get(); +#else +# ifdef HAVE_GETTIMEOFDAY + struct timeval timev; + + gettimeofday(&timev, NULL); + return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); +# else +# error "Your platform isn't supported yet" +# endif +#endif +} + +/** + * Retrieves the time at which the last loop stopped waiting for timeouts or + * events. + * + * This gets the time that the main loop ceased waiting for timouts and/or + * events to come in or for signals or any other interrupt source. This should + * be considered a reference point for all time based activity that should + * calculate its timepoint from the return of ecore_loop_time_get(). Use this + * UNLESS you absolutely must get the current actual timepoint - then use + * ecore_time_get(). Note that this time is meant to be used as relative to + * other times obtained on this run. If you need absolute time references, use + * ecore_time_unix_get() instead. + * + * This function can be called before any loop has ever been run, but either + * ecore_init() or ecore_time_get() must have been called once. + * + * @return The number of seconds. Start time is not defined (it may be + * when the machine was booted, unix time, etc), all it is + * defined is that it never goes backwards (unless you got big critical + * messages when the application started). + * @ingroup Ecore_Time_Group + */ +EAPI double +ecore_loop_time_get(void) +{ + return _ecore_time_loop_time; +} + + +/********************** Internal methods ********************************/ + +/* TODO: Documentation says "All implementations support the system-wide + * real-time clock, which is identified by CLOCK_REALTIME. Check if the fallback + * to unix time (without specifying the resolution) might be removed + */ +void +_ecore_time_init(void) +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec t; + + if (_ecore_time_clock_id != -1) return; + + if (!clock_gettime(CLOCK_MONOTONIC, &t)) + { + _ecore_time_clock_id = CLOCK_MONOTONIC; + DBG("using CLOCK_MONOTONIC."); + } + else if (!clock_gettime(CLOCK_REALTIME, &t)) + { + /* may go backwards */ + _ecore_time_clock_id = CLOCK_REALTIME; + WRN("CLOCK_MONOTONIC not available. Fallback to CLOCK_REALTIME."); + } + else + { + _ecore_time_clock_id = -2; + CRIT("Cannot get a valid clock_gettime() clock id! " + "Fallback to unix time."); + } +#else +# warning "Your platform isn't supported yet" + CRIT("Platform does not support clock_gettime. " + "Fallback to unix time."); +#endif + + _ecore_time_loop_time = ecore_time_get(); +} diff --git a/tests/suite/ecore/src/lib/ecore_timer.c b/tests/suite/ecore/src/lib/ecore_timer.c new file mode 100644 index 0000000000..fd6c64e42d --- /dev/null +++ b/tests/suite/ecore/src/lib/ecore_timer.c @@ -0,0 +1,591 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> + +#include "Ecore.h" +#include "ecore_private.h" + + +struct _Ecore_Timer +{ + EINA_INLIST; + ECORE_MAGIC; + double in; + double at; + double pending; + Ecore_Task_Cb func; + void *data; + + int references; + unsigned char delete_me : 1; + unsigned char just_added : 1; + unsigned char frozen : 1; +}; + + +static void _ecore_timer_set(Ecore_Timer *timer, double at, double in, Ecore_Task_Cb func, void *data); + +static int timers_added = 0; +static int timers_delete_me = 0; +static Ecore_Timer *timers = NULL; +static Ecore_Timer *timer_current = NULL; +static Ecore_Timer *suspended = NULL; +static double last_check = 0.0; +static double precision = 10.0 / 1000000.0; + +/** + * @defgroup Ecore_Time_Group Ecore Time Functions + * + * Functions that deal with time. These functions include those that simply + * retrieve it in a given format, and those that create events based on it. + */ + +/** + * Retrieves the current precision used by timer infrastructure. + * + * @see ecore_timer_precision_set() + */ +EAPI double +ecore_timer_precision_get(void) +{ + return precision; +} + +/** + * Sets the precision to be used by timer infrastructure. + * + * When system calculates time to expire the next timer we'll be able + * to delay the timer by the given amount so more timers will fit in + * the same dispatch, waking up the system less often and thus being + * able to save power. + * + * Be aware that kernel may delay delivery even further, these delays + * are always possible due other tasks having higher priorities or + * other scheduler policies. + * + * Example: + * We have 2 timers, one that expires in a 2.0s and another that + * expires in 2.1s, if precision is 0.1s, then the Ecore will request + * for the next expire to happen in 2.1s and not 2.0s and another one + * of 0.1 as it would before. + * + * @note Ecore is smart enough to see if there are timers in the + * precision range, if it does not, in our example if no second timer + * in (T + precision) existed, then it would use the minimum timeout. + * + * @param value allowed introduced timeout delay, in seconds. + */ +EAPI void +ecore_timer_precision_set(double value) +{ + if (value < 0.0) + { + ERR("Precision %f less than zero, ignored", value); + return; + } + precision = value; +} + +/** + * Creates a timer to call the given function in the given period of time. + * @param in The interval in seconds. + * @param func The given function. If @p func returns 1, the timer is + * rescheduled for the next interval @p in. + * @param data Data to pass to @p func when it is called. + * @return A timer object on success. @c NULL on failure. + * @ingroup Ecore_Time_Group + * + * This function adds a timer and returns its handle on success and NULL on + * failure. The function @p func will be called every @p in seconds. The + * function will be passed the @p data pointer as its parameter. + * + * When the timer @p func is called, it must return a value of either 1 + * (or ECORE_CALLBACK_RENEW) or 0 (or ECORE_CALLBACK_CANCEL). + * If it returns 1, it will be called again at the next tick, or if it returns + * 0 it will be deleted automatically making any references/handles for it + * invalid. + */ +EAPI Ecore_Timer * +ecore_timer_add(double in, Ecore_Task_Cb func, const void *data) +{ + double now; + Ecore_Timer *timer; + + if (!func) return NULL; + if (in < 0.0) in = 0.0; + timer = calloc(1, sizeof(Ecore_Timer)); + if (!timer) return NULL; + ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER); + now = ecore_time_get(); + _ecore_timer_set(timer, now + in, in, func, (void *)data); + return timer; +} + +/** + * Creates a timer to call the given function in the given period of time. + * @param in The interval in seconds from current loop time. + * @param func The given function. If @p func returns 1, the timer is + * rescheduled for the next interval @p in. + * @param data Data to pass to @p func when it is called. + * @return A timer object on success. @c NULL on failure. + * @ingroup Ecore_Time_Group + * + * This is the same as ecore_timer_add(), but "now" is the time from + * ecore_loop_time_get() not ecore_time_get() as ecore_timer_add() uses. See + * ecore_timer_add() for more details. + */ +EAPI Ecore_Timer * +ecore_timer_loop_add(double in, Ecore_Task_Cb func, const void *data) +{ + double now; + Ecore_Timer *timer; + + if (!func) return NULL; + if (in < 0.0) in = 0.0; + timer = calloc(1, sizeof(Ecore_Timer)); + if (!timer) return NULL; + ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER); + now = ecore_loop_time_get(); + _ecore_timer_set(timer, now + in, in, func, (void *)data); + return timer; +} + +/** + * Delete the specified timer from the timer list. + * @param timer The timer to delete. + * @return The data pointer set for the timer when @ref ecore_timer_add was + * called. @c NULL is returned if the function is unsuccessful. + * @ingroup Ecore_Time_Group + * + * Note: @p timer must be a valid handle. If the timer function has already + * returned 0, the handle is no longer valid (and does not need to be delete). + */ +EAPI void * +ecore_timer_del(Ecore_Timer *timer) +{ + if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) + { + ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, + "ecore_timer_del"); + return NULL; + } + + if (timer->frozen && !timer->references) + { + void *data = timer->data; + + suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer)); + + if (timer->delete_me) + timers_delete_me--; + + free(timer); + return data; + } + + EINA_SAFETY_ON_TRUE_RETURN_VAL(timer->delete_me, NULL); + timer->delete_me = 1; + timers_delete_me++; + return timer->data; +} + +/** + * Change the interval the timer ticks of. If set during + * a timer call, this will affect the next interval. + * + * @param timer The timer to change. + * @param in The interval in seconds. + * @ingroup Ecore_Time_Group + */ +EAPI void +ecore_timer_interval_set(Ecore_Timer *timer, double in) +{ + if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) + { + ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, + "ecore_timer_interval_set"); + return; + } + timer->in = in; +} + +/** + * Get the interval the timer ticks on. + * + * @param timer The timer to retrieve the interval from + * @return The interval on success. -1 on failure. + * @ingroup Ecore_Time_Group + */ +EAPI double +ecore_timer_interval_get(Ecore_Timer *timer) +{ + if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) + { + ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, + "ecore_timer_interval_get"); + return -1.0; + } + + return timer->in; +} + +/** + * Add some delay for the next occurrence of a timer. + * This doesn't affect the interval of a timer. + * + * @param timer The timer to change. + * @param add The dalay to add to the next iteration. + * @ingroup Ecore_Time_Group + */ +EAPI void +ecore_timer_delay(Ecore_Timer *timer, double add) +{ + if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) + { + ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, + "ecore_timer_delay"); + return; + } + + if (timer->frozen) + { + timer->pending += add; + } + else + { + timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); + _ecore_timer_set(timer, timer->at + add, timer->in, timer->func, timer->data); + } +} + +/** + * Get the pending time regarding a timer. + * + * @param timer The timer to learn from. + * @ingroup Ecore_Time_Group + */ +EAPI double +ecore_timer_pending_get(Ecore_Timer *timer) +{ + double now; + + if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) + { + ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, + "ecore_timer_pending_get"); + return 0; + } + + now = ecore_time_get(); + + if (timer->frozen) + return timer->pending; + return timer->at - now; +} + +/** + * + * + */ +EAPI void +ecore_timer_freeze(Ecore_Timer *timer) +{ + double now; + + if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) + { + ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, + "ecore_timer_freeze"); + return ; + } + + /* Timer already frozen */ + if (timer->frozen) + return ; + + timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); + suspended = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer)); + + now = ecore_time_get(); + + timer->pending = timer->at - now; + timer->at = 0.0; + timer->frozen = 1; +} + +EAPI void +ecore_timer_thaw(Ecore_Timer *timer) +{ + double now; + + if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) + { + ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, + "ecore_timer_thaw"); + return ; + } + + /* Timer not frozen */ + if (!timer->frozen) + return ; + + suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer)); + now = ecore_time_get(); + + _ecore_timer_set(timer, timer->pending + now, timer->in, timer->func, timer->data); +} + +void +_ecore_timer_shutdown(void) +{ + Ecore_Timer *timer; + + while ((timer = timers)) + { + timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timers)); + ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE); + free(timer); + } + + while ((timer = suspended)) + { + suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(suspended)); + ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE); + free(timer); + } + + timer_current = NULL; +} + +void +_ecore_timer_cleanup(void) +{ + Ecore_Timer *l; + int in_use = 0, todo = timers_delete_me, done = 0; + + if (!timers_delete_me) return; + for (l = timers; l;) + { + Ecore_Timer *timer = l; + + l = (Ecore_Timer *) EINA_INLIST_GET(l)->next; + if (timer->delete_me) + { + if (timer->references) + { + in_use++; + continue; + } + timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); + ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE); + free(timer); + timers_delete_me--; + done++; + if (timers_delete_me == 0) return; + } + } + for (l = suspended; l;) + { + Ecore_Timer *timer = l; + + l = (Ecore_Timer *) EINA_INLIST_GET(l)->next; + if (timer->delete_me) + { + if (timer->references) + { + in_use++; + continue; + } + suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer)); + ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE); + free(timer); + timers_delete_me--; + done++; + if (timers_delete_me == 0) return; + } + } + + if ((!in_use) && (timers_delete_me)) + { + ERR("%d timers to delete, but they were not found!" + "Stats: todo=%d, done=%d, pending=%d, in_use=%d. " + "reset counter.", + timers_delete_me, todo, done, todo - done, in_use); + timers_delete_me = 0; + } +} + +void +_ecore_timer_enable_new(void) +{ + Ecore_Timer *timer; + + if (!timers_added) return; + timers_added = 0; + EINA_INLIST_FOREACH(timers, timer) timer->just_added = 0; +} + +int +_ecore_timers_exists(void) +{ + Ecore_Timer *timer = timers; + + while ((timer) && (timer->delete_me)) + timer = (Ecore_Timer *)EINA_INLIST_GET(timer)->next; + + return !!timer; +} + +static inline Ecore_Timer * +_ecore_timer_first_get(void) +{ + Ecore_Timer *timer = timers; + + while ((timer) && ((timer->delete_me) || (timer->just_added))) + timer = (Ecore_Timer *) EINA_INLIST_GET(timer)->next; + + return timer; +} + +static inline Ecore_Timer * +_ecore_timer_after_get(Ecore_Timer *base) +{ + Ecore_Timer *timer = (Ecore_Timer *) EINA_INLIST_GET(base)->next; + double maxtime = base->at + precision; + + while ((timer) && ((timer->delete_me) || (timer->just_added)) && (timer->at <= maxtime)) + timer = (Ecore_Timer *) EINA_INLIST_GET(timer)->next; + + if ((!timer) || (timer->at > maxtime)) + return NULL; + + return timer; +} + +double +_ecore_timer_next_get(void) +{ + double now; + double in; + Ecore_Timer *first, *second; + + first = _ecore_timer_first_get(); + if (!first) return -1; + + second = _ecore_timer_after_get(first); + if (second) + first = second; + + now = ecore_loop_time_get(); + in = first->at - now; + if (in < 0) in = 0; + return in; +} + +static inline void +_ecore_timer_reschedule(Ecore_Timer *timer, double when) +{ + if ((timer->delete_me) || (timer->frozen)) return; + + timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); + + /* if the timer would have gone off more than 15 seconds ago, + * assume that the system hung and set the timer to go off + * timer->in from now. this handles system hangs, suspends + * and more, so ecore will only "replay" the timers while + * the system is suspended if it is suspended for less than + * 15 seconds (basically). this also handles if the process + * is stopped in a debugger or IO and other handling gets + * really slow within the main loop. + */ + if ((timer->at + timer->in) < (when - 15.0)) + _ecore_timer_set(timer, when + timer->in, timer->in, timer->func, timer->data); + else + _ecore_timer_set(timer, timer->at + timer->in, timer->in, timer->func, timer->data); +} + +int +_ecore_timer_call(double when) +{ + if (!timers) return 0; + if (last_check > when) + { + Ecore_Timer *timer; + /* User set time backwards */ + EINA_INLIST_FOREACH(timers, timer) timer->at -= (last_check - when); + } + last_check = when; + + if (!timer_current) + { + /* regular main loop, start from head */ + timer_current = timers; + } + else + { + /* recursive main loop, continue from where we were */ + Ecore_Timer *timer_old = timer_current; + timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next; + _ecore_timer_reschedule(timer_old, when); + } + + while (timer_current) + { + Ecore_Timer *timer = timer_current; + + if (timer->at > when) + { + timer_current = NULL; /* ended walk, next should restart. */ + return 0; + } + + if ((timer->just_added) || (timer->delete_me)) + { + timer_current = (Ecore_Timer*)EINA_INLIST_GET(timer_current)->next; + continue; + } + + timer->references++; + if (!timer->func(timer->data)) + { + if (!timer->delete_me) ecore_timer_del(timer); + } + timer->references--; + + if (timer_current) /* may have changed in recursive main loops */ + timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next; + + _ecore_timer_reschedule(timer, when); + } + return 0; +} + +static void +_ecore_timer_set(Ecore_Timer *timer, double at, double in, Ecore_Task_Cb func, void *data) +{ + Ecore_Timer *t2; + + timers_added = 1; + timer->at = at; + timer->in = in; + timer->func = func; + timer->data = data; + timer->just_added = 1; + timer->frozen = 0; + timer->pending = 0.0; + if (timers) + { + EINA_INLIST_REVERSE_FOREACH(EINA_INLIST_GET(timers), t2) + { + if (timer->at > t2->at) + { + timers = (Ecore_Timer *) eina_inlist_append_relative(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer), EINA_INLIST_GET(t2)); + return; + } + } + } + timers = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); +} diff --git a/tests/suite/ecore/src/lib/eina_accessor.c b/tests/suite/ecore/src/lib/eina_accessor.c new file mode 100644 index 0000000000..cb20cab184 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_accessor.c @@ -0,0 +1,267 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> + +#include "eina_config.h" +#include "eina_private.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_accessor.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static const char EINA_MAGIC_ACCESSOR_STR[] = "Eina Accessor"; + +#define EINA_MAGIC_CHECK_ACCESSOR(d) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ACCESSOR)) { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_ACCESSOR); } \ + } while(0) + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the accessor module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the accessor module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_accessor_init(void) +{ + return eina_magic_string_set(EINA_MAGIC_ACCESSOR, EINA_MAGIC_ACCESSOR_STR); +} + +/** + * @internal + * @brief Shut down the accessor module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the accessor module set up by + * eina_accessor_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_accessor_shutdown(void) +{ + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Accessor_Group Accessor Functions + * + * @brief These functions manage accessor on containers. + * + * These functions allow to access elements of a container in a + * generic way, without knowing which container is used (a bit like + * iterators in the C++ STL). Accessors allows random access (that is, any + * element in the container). For sequential access, see + * @ref Eina_Iterator_Group. + * + * An accessor is created from container data types, so no creation + * function is available here. An accessor is deleted with + * eina_accessor_free(). To get the data of an element at a given + * position, use eina_accessor_data_get(). To call a function on + * chosen elements of a container, use eina_accessor_over(). + * + * @{ + */ + +/** + * @brief Free an accessor. + * + * @param accessor The accessor to free. + * + * This function frees @p accessor if it is not @c NULL; + */ +EAPI void +eina_accessor_free(Eina_Accessor *accessor) +{ + EINA_MAGIC_CHECK_ACCESSOR(accessor); + EINA_SAFETY_ON_NULL_RETURN(accessor); + EINA_SAFETY_ON_NULL_RETURN(accessor->free); + accessor->free(accessor); +} + +/** + * @brief Return the container of an accessor. + * + * @param accessor The accessor. + * @return The container which created the accessor. + * + * This function returns the container which created @p accessor. If + * @p accessor is @c NULL, this function returns @c NULL. + */ +EAPI void * +eina_accessor_container_get(Eina_Accessor *accessor) +{ + EINA_MAGIC_CHECK_ACCESSOR(accessor); + EINA_SAFETY_ON_NULL_RETURN_VAL(accessor, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(accessor->get_container, NULL); + return accessor->get_container(accessor); +} + +/** + * @brief Retrieve the data of an accessor at a given position. + * + * @param accessor The accessor. + * @param position The position of the element. + * @param data The pointer that stores the data to retrieve. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function retrieves the data of the element pointed by + * @p accessor at the porition @p position, and stores it in + * @p data. If @p accessor is @c NULL or if an error occurred, + * #EINA_FALSE is returned, otherwise EINA_TRUE is returned. + */ +EAPI Eina_Bool +eina_accessor_data_get(Eina_Accessor *accessor, + unsigned int position, + void **data) +{ + EINA_MAGIC_CHECK_ACCESSOR(accessor); + EINA_SAFETY_ON_NULL_RETURN_VAL(accessor, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(accessor->get_at, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE); + return accessor->get_at(accessor, position, data); +} + +/** + * @brief Iterate over the container and execute a callback on chosen elements. + * + * @param accessor The accessor. + * @param cb The callback called on the chosen elements. + * @param start The position of the first element. + * @param end The position of the last element. + * @param fdata The data passed to the callback. + * + * This function iterates over the elements pointed by @p accessor, + * starting from the element at position @p start and ending to the + * element at position @p end. For Each element, the callback + * @p cb is called with the data @p fdata. If @p accessor is @c NULL + * or if @p start is greter or equal than @p end, the function returns + * immediately. + */ +EAPI void +eina_accessor_over(Eina_Accessor *accessor, + Eina_Each_Cb cb, + unsigned int start, + unsigned int end, + const void *fdata) +{ + const void *container; + void *data; + unsigned int i; + + EINA_MAGIC_CHECK_ACCESSOR(accessor); + EINA_SAFETY_ON_NULL_RETURN(accessor); + EINA_SAFETY_ON_NULL_RETURN(accessor->get_container); + EINA_SAFETY_ON_NULL_RETURN(accessor->get_at); + EINA_SAFETY_ON_NULL_RETURN(cb); + EINA_SAFETY_ON_FALSE_RETURN(start < end); + + if (!eina_accessor_lock(accessor)) + return ; + + container = accessor->get_container(accessor); + for (i = start; i < end && accessor->get_at(accessor, i, &data) == EINA_TRUE; + ++i) + if (cb(container, data, (void *)fdata) != EINA_TRUE) + goto on_exit; + + on_exit: + (void) eina_accessor_unlock(accessor); +} + +/** + * @brief Lock the container of the accessor. + * + * @param accessor The accessor. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * If the container of the @p accessor permit it, it will be locked. + * If @p accessor is @c NULL or if a problem occurred, #EINA_FALSE is + * returned, otherwise #EINA_TRUE is returned. If the container + * is not lockable, it will return EINA_TRUE. + */ +EAPI Eina_Bool +eina_accessor_lock(Eina_Accessor *accessor) +{ + EINA_MAGIC_CHECK_ACCESSOR(accessor); + EINA_SAFETY_ON_NULL_RETURN_VAL(accessor, EINA_FALSE); + + if (accessor->lock) + return accessor->lock(accessor); + return EINA_TRUE; +} + +/** + * @brief Unlock the container of the accessor. + * + * @param accessor The accessor. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * If the container of the @p accessor permit it and was previously + * locked, it will be unlocked. If @p accessor is @c NULL or if a + * problem occurred, #EINA_FALSE is returned, otherwise #EINA_TRUE + * is returned. If the container is not lockable, it will return + * EINA_TRUE. + */ +EAPI Eina_Bool +eina_accessor_unlock(Eina_Accessor *accessor) +{ + EINA_MAGIC_CHECK_ACCESSOR(accessor); + EINA_SAFETY_ON_NULL_RETURN_VAL(accessor, EINA_FALSE); + + if (accessor->unlock) + return accessor->unlock(accessor); + return EINA_TRUE; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_array.c b/tests/suite/ecore/src/lib/eina_array.c new file mode 100644 index 0000000000..bd71fc9782 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_array.c @@ -0,0 +1,727 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +/** + * @page tutorial_array_page Array Tutorial + * + * The Array data type is allow the storage of data like a C array. + * It is designed such that the access to its element is very fast. + * But the addition or removal can be done only at the end of the + * array. To add or remove an element at any location, the Eina + * @ref Eina_List_Group is the correct container is the correct one. + * + * @section tutorial_error_basic_usage Basic Usage + * + * An array must created with eina_array_new(). That function + * takes an integer as parameter, which is the count of pointers to + * add when increasing the array size. Once the array is not used + * anymore, it must be destroyed with eina_array_free(). + * + * To append data at the end of the array, the function + * eina_array_push() must be used. To remove the data at the end of + * the array, eina_array_pop() must be used. Once the array is filled, + * one can check its elements by iterating over it. A while loop and + * eina_array_data_get() can be used, or else one can use the + * predefined macro EINA_ARRAY_ITER_NEXT(). To free all the elements, + * a while loop can be used with eina_array_count_get(). Here is an + * example of use: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * #include <string.h> + * + * #include <eina_array.h> + * + * int main(void) + * { + * const char *strings[] = { + * "first string", + * "second string", + * "third string", + * "fourth string" + * }; + * Eina_Array *array; + * char *item; + * Eina_Array_Iterator iterator; + * unsigned int i; + * + * if (!eina_init()) + * { + * printf ("Error during the initialization of eina\n"); + * return EXIT_FAILURE; + * } + * + * array = eina_array_new(16); + * if (!array) + * goto shutdown; + * + * for (i = 0; i < 4; i++) + * { + * eina_array_push(array, strdup(strings[i])); + * } + * + * printf("array count: %d\n", eina_array_count_get(array)); + * EINA_ARRAY_ITER_NEXT(array, i, item, iterator) + * { + * printf("item #%d: %s\n", i, item); + * } + * + * while (eina_array_count_get(array)) + * { + * void *data; + * + * data = eina_array_pop(array); + * free(data); + * } + * + * eina_array_free(array); + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * + * shutdown: + * eina_shutdown(); + * + * return EXIT_FAILURE; + * } + * @endcode + * + * To be continued + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_error.h" +#include "eina_log.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_array.h" + +/*============================================================================* + * Local * + *============================================================================*/ + +/** + * @cond LOCAL + */ + +static const char EINA_MAGIC_ARRAY_STR[] = "Eina Array"; +static const char EINA_MAGIC_ARRAY_ITERATOR_STR[] = "Eina Array Iterator"; +static const char EINA_MAGIC_ARRAY_ACCESSOR_STR[] = "Eina Array Accessor"; + +#define EINA_MAGIC_CHECK_ARRAY(d) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ARRAY)) { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_ARRAY); } \ + } while (0) + +#define EINA_MAGIC_CHECK_ARRAY_ITERATOR(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ARRAY_ITERATOR)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_ARRAY_ITERATOR); \ + return __VA_ARGS__; \ + } \ + } while (0) + +#define EINA_MAGIC_CHECK_ARRAY_ACCESSOR(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ARRAY_ACCESSOR)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_ACCESSOR); \ + return __VA_ARGS__; \ + } \ + } while (0) + + +typedef struct _Eina_Iterator_Array Eina_Iterator_Array; +struct _Eina_Iterator_Array +{ + Eina_Iterator iterator; + + const Eina_Array *array; + unsigned int index; + + EINA_MAGIC +}; + +typedef struct _Eina_Accessor_Array Eina_Accessor_Array; +struct _Eina_Accessor_Array +{ + Eina_Accessor accessor; + const Eina_Array *array; + EINA_MAGIC +}; + +static int _eina_array_log_dom = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_array_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_array_log_dom, __VA_ARGS__) + +static void eina_array_iterator_free(Eina_Iterator_Array *it) EINA_ARG_NONNULL(1); +static Eina_Array *eina_array_iterator_get_container(Eina_Iterator_Array *it) EINA_ARG_NONNULL(1); +static Eina_Bool eina_array_iterator_next(Eina_Iterator_Array *it, + void **data) EINA_ARG_NONNULL(1); + +static Eina_Bool eina_array_accessor_get_at(Eina_Accessor_Array *it, + unsigned int idx, + void **data) EINA_ARG_NONNULL(1); +static Eina_Array *eina_array_accessor_get_container(Eina_Accessor_Array *it) EINA_ARG_NONNULL(1); +static void eina_array_accessor_free(Eina_Accessor_Array *it) EINA_ARG_NONNULL(1); + +static Eina_Bool +eina_array_iterator_next(Eina_Iterator_Array *it, void **data) +{ + EINA_MAGIC_CHECK_ARRAY_ITERATOR(it, EINA_FALSE); + + if (!(it->index < eina_array_count_get(it->array))) + return EINA_FALSE; + + if (data) + *data = eina_array_data_get(it->array, it->index); + + it->index++; + return EINA_TRUE; +} + +static Eina_Array * +eina_array_iterator_get_container(Eina_Iterator_Array *it) +{ + EINA_MAGIC_CHECK_ARRAY_ITERATOR(it, NULL); + return (Eina_Array *)it->array; +} + +static void +eina_array_iterator_free(Eina_Iterator_Array *it) +{ + EINA_MAGIC_CHECK_ARRAY_ITERATOR(it); + MAGIC_FREE(it); +} + +static Eina_Bool +eina_array_accessor_get_at(Eina_Accessor_Array *it, + unsigned int idx, + void **data) +{ + EINA_MAGIC_CHECK_ARRAY_ACCESSOR(it, EINA_FALSE); + + if (!(idx < eina_array_count_get(it->array))) + return EINA_FALSE; + + if (data) + *data = eina_array_data_get(it->array, idx); + + return EINA_TRUE; +} + +static Eina_Array * +eina_array_accessor_get_container(Eina_Accessor_Array *it) +{ + EINA_MAGIC_CHECK_ARRAY_ACCESSOR(it, NULL); + return (Eina_Array *)it->array; +} + +static void +eina_array_accessor_free(Eina_Accessor_Array *it) +{ + EINA_MAGIC_CHECK_ARRAY_ACCESSOR(it); + MAGIC_FREE(it); +} + +EAPI Eina_Bool +eina_array_grow(Eina_Array *array) +{ + void **tmp; + unsigned int total; + + EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE); + + EINA_MAGIC_CHECK_ARRAY(array); + + total = array->total + array->step; + eina_error_set(0); + tmp = realloc(array->data, sizeof (void *) * total); + if (EINA_UNLIKELY(!tmp)) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return 0; + } + + array->total = total; + array->data = tmp; + + return 1; +} + +/** + * @endcond + */ + + +/*============================================================================* + * Global * + *============================================================================*/ + +/** + * @internal + * @brief Initialize the array module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the error and magic modules or Eina. It is + * called by eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_array_init(void) +{ + _eina_array_log_dom = eina_log_domain_register("eina_array", + EINA_LOG_COLOR_DEFAULT); + if (_eina_array_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_array"); + return EINA_FALSE; + } + +#define EMS(n) eina_magic_string_static_set(n, n ## _STR) + EMS(EINA_MAGIC_ARRAY); + EMS(EINA_MAGIC_ARRAY_ITERATOR); + EMS(EINA_MAGIC_ARRAY_ACCESSOR); +#undef EMS + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the array module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the array module set up by + * eina_array_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_array_shutdown(void) +{ + eina_log_domain_unregister(_eina_array_log_dom); + _eina_array_log_dom = -1; + return EINA_TRUE; +} + +/*============================================================================* + * API * + *============================================================================*/ + +/** + * @addtogroup Eina_Array_Group Array + * + * @brief These functions provide array management. + * + * The Array data type in Eina is designed to have a very fast access to + * its data (compared to the Eina @ref Eina_List_Group). On the other hand, + * data can be added or removed only at the end of the array. To insert + * data at any place, the Eina @ref Eina_List_Group is the correct container + * to use. + * + * To use the array data type, eina_init() must be called before any + * other array functions. When eina is no more array function is used, + * eina_shutdown() must be called to free all the resources. + * + * An array must be created with eina_array_new(). It allocated all + * the necessary data for an array. When not needed anymore, an array + * is freed with eina_array_free(). This function does not free any + * allocated memory used to store the data of each element. For that, + * just iterate over the array to free them. A convenient way to do + * that is by using #EINA_ARRAY_ITER_NEXT. An example of code is given + * in the description of this macro. + * + * @warning All the other functions do not check if the used array is + * valid or not. It's up to the user to be sure of that. It is + * designed like that for performance reasons. + * + * The usual features of an array are classic ones: to append an + * element, use eina_array_push() and to remove the last element, use + * eina_array_pop(). To retrieve the element at a given positin, use + * eina_array_data_get(). The number of elements can be retrieved with + * eina_array_count_get(). + * + * For more information, you can look at the @ref tutorial_array_page. + * + * @{ + */ + +/** + * @brief Create a new array. + * + * @param step The count of pointers to add when increasing the array size. + * @return @c NULL on failure, non @c NULL otherwise. + * + * This function creates a new array. When adding an element, the array + * allocates @p step elements. When that buffer is full, then adding + * another element will increase the buffer of @p step elements again. + * + * This function return a valid array on success, or @c NULL if memory + * allocation fails. In that case, the error is set to + * #EINA_ERROR_OUT_OF_MEMORY. + */ +EAPI Eina_Array * +eina_array_new(unsigned int step) +{ + Eina_Array *array; + + eina_error_set(0); + array = malloc(sizeof (Eina_Array)); + if (!array) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(array, EINA_MAGIC_ARRAY); + + array->version = EINA_ARRAY_VERSION; + array->data = NULL; + array->total = 0; + array->count = 0; + array->step = step; + + return array; +} + +/** + * @brief Free an array. + * + * @param array The array to free. + * + * This function frees @p array. It calls first eina_array_flush() then + * free the memory of the pointer. It does not free the memory + * allocated for the elements of @p array. To free them, use + * #EINA_ARRAY_ITER_NEXT. For performance reasons, there is no check + * of @p array. + */ +EAPI void +eina_array_free(Eina_Array *array) +{ + eina_array_flush(array); + + EINA_SAFETY_ON_NULL_RETURN(array); + EINA_MAGIC_CHECK_ARRAY(array); + MAGIC_FREE(array); +} + +/** + * @brief Set the step of an array. + * + * @param array The array. + * @param sizeof_eina_array Should be the value returned by sizeof(Eina_Array). + * @param step The count of pointers to add when increasing the array size. + * + * This function sets the step of @p array to @p step. For performance + * reasons, there is no check of @p array. If it is @c NULL or + * invalid, the program may crash. This function should be called when + * the array is not initialized. + */ +EAPI void +eina_array_step_set(Eina_Array *array, + unsigned int sizeof_eina_array, + unsigned int step) +{ + EINA_SAFETY_ON_NULL_RETURN(array); + + if (sizeof (Eina_Array) != sizeof_eina_array) + { + ERR("Unknow Eina_Array size ! Got %i, expected %i !\n", + sizeof_eina_array, + (int) sizeof (Eina_Array)); + /* Force memory to zero to provide a small layer of security */ + memset(array, 0, sizeof_eina_array); + return ; + } + + array->version = EINA_ARRAY_VERSION; + array->data = NULL; + array->total = 0; + array->count = 0; + array->step = step; + EINA_MAGIC_SET(array, EINA_MAGIC_ARRAY); +} + +/** + * @brief Clean an array. + * + * @param array The array to clean. + * + * This function sets the count member of @p array to 0. For + * performance reasons, there is no check of @p array. If it is + * @c NULL or invalid, the program may crash. + */ +EAPI void +eina_array_clean(Eina_Array *array) +{ + EINA_SAFETY_ON_NULL_RETURN(array); + EINA_MAGIC_CHECK_ARRAY(array); + + array->count = 0; +} + +/** + * @brief Flush an array. + * + * @param array The array to flush. + * + * This function sets the count and total members of @p array to 0, + * frees and set to NULL its data member. For performance reasons, + * there is no check of @p array. If it is @c NULL or invalid, the + * program may crash. + */ +EAPI void +eina_array_flush(Eina_Array *array) +{ + EINA_SAFETY_ON_NULL_RETURN(array); + EINA_MAGIC_CHECK_ARRAY(array); + + array->count = 0; + array->total = 0; + + if (!array->data) + return; + + free(array->data); + array->data = NULL; +} + +/** + * @brief Rebuild an array by specifying the data to keep. + * + * @param array The array. + * @param keep The functions which selects the data to keep. + * @param gdata The data to pass to the function keep. + * @return #EINA_TRUE on success, #EINA_FALSE oterwise. + * + * This function rebuilds @p array be specifying the elements to keep + * with the function @p keep. @p gdata is an additional data to pass + * to @p keep. For performance reasons, there is no check of @p + * array. If it is @c NULL or invalid, the program may crash. + * + * This function always return a valid array. If it wasn't able to + * remove items due to an allocation failure, it will return #EINA_FALSE + * and the error is set to #EINA_ERROR_OUT_OF_MEMORY. + */ +EAPI Eina_Bool +eina_array_remove(Eina_Array *array, Eina_Bool (*keep)(void *data, + void *gdata), + void *gdata) +{ + void **tmp; + /* WARNING: + The algorithm does exit before using unitialized data. So compiler is + giving you a false positiv here too. + */ + void *data = NULL; + unsigned int total = 0; + unsigned int limit; + unsigned int i; + + EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(keep, EINA_FALSE); + EINA_MAGIC_CHECK_ARRAY(array); + + if (array->total == 0) + return EINA_TRUE; + + for (i = 0; i < array->count; ++i) + { + data = eina_array_data_get(array, i); + + if (keep(data, gdata) == EINA_FALSE) + break; + } + limit = i; + if (i < array->count) + ++i; + + for (; i < array->count; ++i) + { + data = eina_array_data_get(array, i); + + if (keep(data, gdata) == EINA_TRUE) + break; + } + /* Special case all objects that need to stay are at the beginning of the array. */ + if (i == array->count) + { + array->count = limit; + if (array->count == 0) + { + free(array->data); + array->total = 0; + array->data = NULL; + } + + return EINA_TRUE; + } + + eina_error_set(0); + tmp = malloc(sizeof (void *) * array->total); + if (!tmp) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return EINA_FALSE; + } + + memcpy(tmp, array->data, limit * sizeof(void *)); + total = limit; + + if (i < array->count) + { + tmp[total] = data; + total++; + ++i; + } + + for (; i < array->count; ++i) + { + data = eina_array_data_get(array, i); + + if (keep(data, gdata)) + { + tmp[total] = data; + total++; + } + } + + free(array->data); + + /* If we do not keep any object in the array, we should have exited + earlier in test (i == array->count). */ + assert(total != 0); + + array->data = tmp; + array->count = total; + return EINA_TRUE; +} + +/** + * @brief Returned a new iterator associated to an array. + * + * @param array The array. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to + * @p array. If @p array is @c NULL or the count member of @p array is + * less or equal than 0, this function returns NULL. If the memory can + * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is + * set. Otherwise, a valid iterator is returned. + */ +EAPI Eina_Iterator * +eina_array_iterator_new(const Eina_Array *array) +{ + Eina_Iterator_Array *it; + + EINA_SAFETY_ON_NULL_RETURN_VAL(array, NULL); + EINA_MAGIC_CHECK_ARRAY(array); + + eina_error_set(0); + it = calloc(1, sizeof (Eina_Iterator_Array)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(it, EINA_MAGIC_ARRAY_ITERATOR); + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->array = array; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(eina_array_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + eina_array_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(eina_array_iterator_free); + + return &it->iterator; +} + +/** + * @brief Returned a new accessor associated to an array. + * + * @param array The array. + * @return A new accessor. + * + * This function returns a newly allocated accessor associated to + * @p array. If @p array is @c NULL or the count member of @p array is + * less or equal than 0, this function returns NULL. If the memory can + * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is + * set. Otherwise, a valid accessor is returned. + */ +EAPI Eina_Accessor * +eina_array_accessor_new(const Eina_Array *array) +{ + Eina_Accessor_Array *ac; + + EINA_SAFETY_ON_NULL_RETURN_VAL(array, NULL); + EINA_MAGIC_CHECK_ARRAY(array); + + eina_error_set(0); + ac = calloc(1, sizeof (Eina_Accessor_Array)); + if (!ac) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(ac, EINA_MAGIC_ARRAY_ACCESSOR); + EINA_MAGIC_SET(&ac->accessor, EINA_MAGIC_ACCESSOR); + + ac->array = array; + + ac->accessor.version = EINA_ACCESSOR_VERSION; + ac->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_array_accessor_get_at); + ac->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER( + eina_array_accessor_get_container); + ac->accessor.free = FUNC_ACCESSOR_FREE(eina_array_accessor_free); + + return &ac->accessor; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_benchmark.c b/tests/suite/ecore/src/lib/eina_benchmark.c new file mode 100644 index 0000000000..5cd3fd36e1 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_benchmark.c @@ -0,0 +1,742 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +/** + * @page tutorial_benchmark_page Benchmark Tutorial + * + * The Benchmark module allows you to write easily benchmarks + * framework in a project for timing critical part and detect slow + * parts of code. In addition it automatically creates data files of + * these benchmark, as well as a gnuplot file which can display the + * comparison curves of the benchmarks. + * + * @section tutorial_benchmark_basic_usage Basic Usage + * + * To create a basic benchmark, you have to follow these steps: + * + * @li Create a new bechmark + * @li Write the functions that wraps the the functions you want to + * bechmark. + * @li Register these wrappers functions. + * @li Run the benchmark. + * @li Free the memory. + * + * Here is a basic example of bechmark which creates two functions + * that will be run. These functions just print a message. + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * + * #include <Eina.h> + * + * static + * void work1(int request) + * { + * printf ("work1 in progress... Request: %d\n", request); + * } + * + * static + * void work2(int request) + * { + * printf ("work2 in progress... Request: %d\n", request); + * } + * + * int main() + * { + * Eina_Benchmark *test; + * Eina_Array *ea; + * + * if (!eina_init()) + * return EXIT_FAILURE; + * + * test = eina_benchmark_new("test", "run"); + * if (!test) + * goto shutdown_eina; + * + * eina_benchmark_register(test, "work-1", EINA_BENCHMARK(work1), 200, 300, 10); + * eina_benchmark_register(test, "work-2", EINA_BENCHMARK(work2), 100, 150, 5); + * + * ea = eina_benchmark_run(test); + * + * eina_benchmark_free(test); + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * + * shutdown_eina: + * eina_shutdown(); + * + * return EXIT_FAILURE; + * } + * @endcode + * + * As "test", "run" are passed to eina_benchmark_new() and as the tests + * "work-1" and "work-2" are registered, the data files + * bench_test_run.work-1.data and bench_test_run.work-2.data will be + * created after the eina_benchmark_run() call. They contain four + * columns. The file bench_test_run.work-1.data contains for example: + * + * @code + * # specimen experiment time starting time ending time + * 200 23632 2852446 2876078 + * 210 6924 2883046 2889970 + * 220 6467 2895962 2902429 + * 230 6508 2908271 2914779 + * 240 6278 2920610 2926888 + * 250 6342 2932830 2939172 + * 260 6252 2944954 2951206 + * 270 6463 2956978 2963441 + * 280 6347 2969548 2975895 + * 290 6457 2981702 2988159 + * @endcode + * + * The first column (specimen) is the integer passed to the work1() + * function when the test is run. The second column (experiment time) + * is the time, in nanosecond, that work1() takes. The third and + * fourth columnd are self-explicit. + * + * You can see that the integer passed work1() starts from 200 and + * finishes at 290, with a step of 10. These values are computed withe + * last 3 values passed to eina_benchmark_register(). See the document + * of that function for the detailed behavior. + * + * The gnuplot file will be named bench_test_run.gnuplot. Just run: + * + * @code + * gnuplot bench_test_run.gnuplot + * @endcode + * + * to create the graphic of the comparison curves. The image file is + * named output_test_run.png. + * + * @section tutorial_benchmark_advanced_usage More Advanced Usage + * + * In this section, several test will be created and run. The idea is + * exactly the same than in the previous section, but with some basic + * automatic way to run all the benchmarks. The following code + * benchmarks some Eina converts functions, and some Eina containers + * types: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * #include <time.h> + * + * #include <Eina.h> + * + * static void bench_convert(Eina_Benchmark *bench); + * static void bench_container(Eina_Benchmark *bench); + * + * typedef struct _Benchmark_Case Benchmark_Case; + * + * struct _Benchmark_Case + * { + * const char *bench_case; + * void (*build)(Eina_Benchmark *bench); + * }; + * + * static const Benchmark_Case benchmarks[] = { + * { "Bench 1", bench_convert }, + * { "Bench 2", bench_container }, + * { NULL, NULL } + * }; + * + * static + * void convert1(int request) + * { + * char tmp[128]; + * int i; + * + * srand(time(NULL)); + * + * for (i = 0; i < request; ++i) + * eina_convert_itoa(rand(), tmp); + * } + * + * static + * void convert2(int request) + * { + * char tmp[128]; + * int i; + * + * srand(time(NULL)); + * + * for (i = 0; i < request; ++i) + * eina_convert_xtoa(rand(), tmp); + * } + * + * static void + * bench_convert(Eina_Benchmark *bench) + * { + * eina_benchmark_register(bench, "convert-1", EINA_BENCHMARK(convert1), 200, 400, 10); + * eina_benchmark_register(bench, "convert-2", EINA_BENCHMARK(convert2), 200, 400, 10); + * } + * + * static + * void array(int request) + * { + * Eina_Array *array; + * Eina_Array_Iterator it; + * int *data; + * int i; + * + * srand(time(NULL)); + * + * array = eina_array_new(64); + * + * for (i = 0; i < request; ++i) + * { + * data = (int *)malloc(sizeof(int)); + * if (!data) continue; + * *data = rand(); + * eina_array_push(array, data); + * } + * + * EINA_ARRAY_ITER_NEXT(array, i, data, it) + * free(data); + * + * eina_array_free(array); + * } + * + * static + * void list(int request) + * { + * Eina_List *l = NULL; + * int *data; + * int i; + * + * srand(time(NULL)); + * + * for (i = 0; i < request; ++i) + * { + * data = (int *)malloc(sizeof(int)); + * if (!data) continue; + * *data = rand(); + * l = eina_list_prepend(l, data); + * } + * + * while (l) + * { + * free(eina_list_data_get(l)); + * l = eina_list_remove_list(l, l); + * } + * } + * + * static void + * bench_container(Eina_Benchmark *bench) + * { + * eina_benchmark_register(bench, "array", EINA_BENCHMARK(array), 200, 300, 10); + * eina_benchmark_register(bench, "list", EINA_BENCHMARK(list), 200, 300, 10); + * } + * + * int main() + * { + * Eina_Benchmark *test; + * Eina_Array *ea; + * unsigned int i; + * + * if (!eina_init()) + * return EXIT_FAILURE; + * + * for (i = 0; benchmarks[i].bench_case != NULL; ++i) + * { + * test = eina_benchmark_new(benchmarks[i].bench_case, "Benchmark example"); + * if (!test) + * continue; + * + * benchmarks[i].build(test); + * + * ea = eina_benchmark_run(test); + * + * eina_benchmark_free(test); + * } + * + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * } + * @endcode + * + * gnuplot can be used to see how are performed the convert functions + * together, as well as how are performed the containers. So it is now + * easy to see that the hexadecimal convert function is faster than + * the decimal one, and that arrays are faster than lists. + * + * You can improve all that by executing automatically gnuplot in your + * program, or integrate the Eina benchmark framework in an autotooled + * project. See that + * <a href="http://trac.enlightenment.org/e/wiki/AutotoolsIntegration#Benchmark">page</a> + * for more informations. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_ALLOCA_H +# include <alloca.h> +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include <malloc.h> +# define alloca _alloca +#else +# include <stddef.h> +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_log.h" +#include "eina_benchmark.h" +#include "eina_inlist.h" +#include "eina_list.h" +#include "eina_counter.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +#define EINA_BENCHMARK_FILENAME_MASK "bench_%s_%s.gnuplot" +#define EINA_BENCHMARK_DATA_MASK "bench_%s_%s.%s.data" + +typedef struct _Eina_Run Eina_Run; +struct _Eina_Run +{ + EINA_INLIST; + + Eina_Benchmark_Specimens cb; + const char *name; + int start; + int end; + int step; +}; + +struct _Eina_Benchmark +{ + const char *name; + const char *run; + + Eina_Inlist *runs; + Eina_List *names; +}; + +static int _eina_benchmark_log_dom = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_benchmark_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_benchmark_log_dom, __VA_ARGS__) + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the benchmark module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the benchmark module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_benchmark_init(void) +{ + _eina_benchmark_log_dom = eina_log_domain_register("eina_benchmark", + EINA_LOG_COLOR_DEFAULT); + if (_eina_benchmark_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_benchmark"); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the benchmark module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the benchmark module set up by + * eina_benchmark_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_benchmark_shutdown(void) +{ + eina_log_domain_unregister(_eina_benchmark_log_dom); + _eina_benchmark_log_dom = -1; + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Benchmark_Group Benchmark + * + * These functions allow you to add benchmark framework in a project + * for timing critical part and detect slow parts of code. It is used + * in Eina to compare the time used by eina, glib, evas and ecore data + * types. + * + * To use the benchmark module, Eina must be initialized with + * eina_init() and later shut down with eina_shutdown(). A benchmark + * is created with eina_benchmark_new() and freed with + * eina_benchmark_free(). + * + * eina_benchmark_register() adds a test to a benchmark. That test can + * be run a certain amount of times. Adding more than one test to be + * executed allows the comparison between several parts of a program, + * or different implementations. + * + * eina_benchmark_run() runs all the tests registered with + * eina_benchmark_register(). The amount of time of each test is + * written in a gnuplot file. + * + * For more information, you can look at the @ref tutorial_benchmark_page. + * + * @{ + */ + +/** + * @brief Create a new array. + * + * @param name The name of the benchmark. + * @param run The name of the run. + * @return @c NULL on failure, non @c NULL otherwise. + * + * This function creates a new benchmark. @p name and @p run are used + * to name the gnuplot file that eina_benchmark_run() will create. + * + * This function return a valid benchmark on success, or @c NULL if + * memory allocation fails. In that case, the error is set to + * #EINA_ERROR_OUT_OF_MEMORY. + * + * When the new module is not needed anymore, use + * eina_benchmark_free() to free the allocated memory. + */ +EAPI Eina_Benchmark * +eina_benchmark_new(const char *name, const char *run) +{ + Eina_Benchmark *new; + + eina_error_set(0); + new = calloc(1, sizeof (Eina_Benchmark)); + if (!new) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + new->name = name; + new->run = run; + + return new; +} + +/** + * @brief Free a benchmark object. + * + * @param bench The benchmark to free. + * + * This function removes all the benchmark tests that have been + * registered and frees @p bench. If @p bench is @c NULL, this + * function returns immediately. + */ +EAPI void +eina_benchmark_free(Eina_Benchmark *bench) +{ + Eina_Array *names; + + if (!bench) + return; + + while (bench->runs) + { + Eina_Run *run = (Eina_Run *)bench->runs; + + bench->runs = eina_inlist_remove(bench->runs, bench->runs); + free(run); + } + + EINA_LIST_FREE(bench->names, names) + { + Eina_Array_Iterator it; + char *tmp; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(names, i, tmp, it) + free(tmp); + + eina_array_free(names); + } + + free(bench); +} + +/** + * @brief Add a test to a benchmark. + * + * @param bench The benchmark. + * @param name The name of the test. + * @param bench_cb The test function to be called. + * @param count_start The start data to be passed to @p bench_cb. + * @param count_end The end data to be passed to @p bench_cb. + * @param count_step The step data to be passed to @p bench_cb. + * + * This function adds the test named @p name to @p benchmark. @p + * bench_cb is the function called when the test is executed. That + * test can be executed a certain amount of time. @p start, @p end and + * @p step define a loop with a step increment. The integer that is + * increasing by @p step from @p start to @p end is passed to @p + * bench_cb when eina_benchmark_run() is called. + * + * If @p bench is @c NULL, this function returns imediatly. If the + * allocation of the memory of the test to add fails, the error is set + * to #EINA_ERROR_OUT_OF_MEMORY. + */ +EAPI Eina_Bool +eina_benchmark_register(Eina_Benchmark *bench, + const char *name, + Eina_Benchmark_Specimens bench_cb, + int count_start, + int count_end, + int count_step) +{ + Eina_Run *run; + + if (!bench) + return EINA_FALSE; + + if (count_step == 0) + return EINA_FALSE; + + eina_error_set(0); + run = calloc(1, sizeof (Eina_Run)); + if (!run) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return EINA_FALSE; + } + + run->cb = bench_cb; + run->name = name; + run->start = count_start; + run->end = count_end; + run->step = count_step; + + bench->runs = eina_inlist_append(bench->runs, EINA_INLIST_GET(run)); + + return EINA_TRUE; +} + +/** + * @brief Run the benchmark tests that have been registered. + * + * @param bench The benchmark. + * @return The list of names of the test files. + * + * This function runs all the tests that as been registered with + * eina_benchmark_register() and save the result in a gnuplot + * file. The name of the file has the following format: + * + * @code + * bench_[name]_[run]%s.gnuplot + * @endcode + * + * where [name] and [run] are the values passed to + * eina_benchmark_new(). + * + * Each registered test is executed and timed. The time is written to + * the gnuplot file. The number of times each test is executed is + * controlled by the parameters passed to eina_benchmark_register(). + * + * If @p bench is @c NULL, this functions returns @c NULL + * immediately. Otherwise, it returns the list of the names of each + * test. + */ +EAPI Eina_Array * +eina_benchmark_run(Eina_Benchmark *bench) +{ + FILE *main_script; + FILE *current_data; + Eina_Array *ea; + Eina_Run *run; + char *buffer; + Eina_Bool first = EINA_FALSE; + size_t length; + + if (!bench) + return NULL; + + length = strlen(EINA_BENCHMARK_FILENAME_MASK) + strlen(bench->name) + strlen( + bench->run); + + buffer = alloca(sizeof (char) * length); + if (!buffer) + return NULL; + + snprintf(buffer, + length, + EINA_BENCHMARK_FILENAME_MASK, + bench->name, + bench->run); + + main_script = fopen(buffer, "w"); + if (!main_script) + return NULL; + + ea = eina_array_new(16); + if (!ea) + { + fclose(main_script); + return NULL; + } + + eina_array_push(ea, strdup(buffer)); + + fprintf( + main_script, + "set autoscale # scale axes automatically\n" + "unset log # remove any log-scaling\n" + "unset label # remove any previous labels\n" + "set xtic auto # set xtics automatically\n" + "set ytic auto # set ytics automatically\n" +/* "set logscale y\n" */ + "set terminal png size 1024,768\n" + "set output \"output_%s_%s.png\"\n" + "set title \"%s %s\n" + "set xlabel \"tests\"\n" + "set ylabel \"time\"\n" + "plot ", + bench->name, + bench->run, + bench->name, + bench->run); + + EINA_INLIST_FOREACH(bench->runs, run) + { + Eina_Counter *counter; + char *result; + size_t tmp; + int i; + + tmp = strlen(EINA_BENCHMARK_DATA_MASK) + strlen(bench->name) + strlen( + bench->run) + strlen(run->name); + if (tmp > length) + { + buffer = alloca(sizeof (char) * tmp); + length = tmp; + } + + snprintf(buffer, + length, + EINA_BENCHMARK_DATA_MASK, + bench->name, + bench->run, + run->name); + + current_data = fopen(buffer, "w"); + if (!current_data) + continue; + + eina_array_push(ea, strdup(buffer)); + + counter = eina_counter_new(run->name); + + for (i = run->start; i < run->end; i += run->step) + { + fprintf(stderr, "Run %s: %i\n", run->name, i); + eina_counter_start(counter); + + run->cb(i); + + eina_counter_stop(counter, i); + } + + result = eina_counter_dump(counter); + if (result) + { + fprintf(current_data, "%s", result); + free(result); + } + + eina_counter_free(counter); + + fclose(current_data); + + if (first == EINA_FALSE) + first = EINA_TRUE; + else + fprintf(main_script, ", \\\n"); + + fprintf(main_script, + "\"%s\" using 1:2 title \'%s\' with line", + buffer, run->name); + } + + fprintf(main_script, "\n"); + + fclose(main_script); + + bench->names = eina_list_append(bench->names, ea); + + return ea; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_binshare.c b/tests/suite/ecore/src/lib/eina_binshare.c new file mode 100644 index 0000000000..3973357deb --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_binshare.c @@ -0,0 +1,202 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, + * Jorge Luis Zapata Muga, + * Cedric Bail, + * Gustavo Sverzut Barbieri + * Tom Hacohen + * + * 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 <http://www.gnu.org/licenses/>. + + */ +/** + * @page tutorial_binshare_page Binary Share Tutorial + * + * Should call eina_binshare_init() before usage and eina_binshare_shutdown() after. + * to be written... + * + */ + +#include "eina_share_common.h" +#include "eina_unicode.h" +#include "eina_private.h" +#include "eina_binshare.h" + +/* The actual share */ +static Eina_Share *binshare_share; +static const char EINA_MAGIC_BINSHARE_NODE_STR[] = "Eina Binshare Node"; + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the share_common module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the share_common module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +EAPI Eina_Bool +eina_binshare_init(void) +{ + return eina_share_common_init(&binshare_share, + EINA_MAGIC_BINSHARE_NODE, + EINA_MAGIC_BINSHARE_NODE_STR); +} + +/** + * @internal + * @brief Shut down the share_common module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the share_common module set up by + * eina_share_common_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +EAPI Eina_Bool +eina_binshare_shutdown(void) +{ + Eina_Bool ret; + ret = eina_share_common_shutdown(&binshare_share); + return ret; +} + +/*============================================================================* +* API * +*============================================================================*/ +/** + * @addtogroup Eina_Binshare_Group Binary Share + * + * These functions allow you to store one copy of an object, and use it + * throughout your program. + * + * This is a method to reduce the number of duplicated objects kept in + * memory. + * + * For more information, you can look at the @ref tutorial_binshare_page. + * + * @{ + */ + +/** + * @brief Note that the given object has lost an instance. + * + * @param obj object The given object. + * + * This function decreases the reference counter associated to @p obj + * if it exists. If that counter reaches 0, the memory associated to + * @p obj is freed. If @p obj is NULL, the function returns + * immediately. + * + * Note that if the given pointer is not shared or NULL, bad things + * will happen, likely a segmentation fault. + */ +EAPI void +eina_binshare_del(const void *obj) +{ + if (!obj) + return; + + eina_share_common_del(binshare_share, obj); +} + +/** + * @brief Retrieve an instance of an object for use in a program. + * + * @param obj The binary object to retrieve an instance of. + * @param olen The byte size + * @return A pointer to an instance of the object on success. + * @c NULL on failure. + * + * This function retrieves an instance of @p obj. If @p obj is + * @c NULL, then @c NULL is returned. If @p obj is already stored, it + * is just returned and its reference counter is increased. Otherwise + * it is added to the objects to be searched and a duplicated object + * of @p obj is returned. + * + * This function does not check object size, but uses the + * exact given size. This can be used to share part of a larger + * object or subobject. + * + * @see eina_binshare_add() + */ +EAPI const void * +eina_binshare_add_length(const void *obj, unsigned int olen) +{ + return eina_share_common_add_length(binshare_share, + obj, + (olen) * sizeof(char), + 0); +} + +/** + * Increment references of the given shared object. + * + * @param obj The shared object. + * @return A pointer to an instance of the object on success. + * @c NULL on failure. + * + * This is similar to eina_share_common_add(), but it's faster since it will + * avoid lookups if possible, but on the down side it requires the parameter + * to be shared before, in other words, it must be the return of a previous + * eina_binshare_add(). + * + * There is no unref since this is the work of eina_binshare_del(). + */ +EAPI const void * +eina_binshare_ref(const void *obj) +{ + return eina_share_common_ref(binshare_share, obj); +} + +/** + * @brief Note that the given object @b must be shared. + * + * @param obj the shared object to know the length. It is safe to + * give NULL, in that case -1 is returned. + * + * This function is a cheap way to known the length of a shared + * object. Note that if the given pointer is not shared, bad + * things will happen, likely a segmentation fault. If in doubt, try + * strlen(). + */ +EAPI int +eina_binshare_length(const void *obj) +{ + return eina_share_common_length(binshare_share, obj); +} + +/** + * @brief Dump the contents of the share_common. + * + * This function dumps all objects in the share_common to stdout with a + * DDD: prefix per line and a memory usage summary. + */ +EAPI void +eina_binshare_dump(void) +{ + eina_share_common_dump(binshare_share, NULL, 0); +} + +/** + * @} + */ + diff --git a/tests/suite/ecore/src/lib/eina_chained_mempool.c b/tests/suite/ecore/src/lib/eina_chained_mempool.c new file mode 100644 index 0000000000..f178cac01d --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_chained_mempool.c @@ -0,0 +1,351 @@ +/* EINA - EFL data type library + * Copyright (C) 2008-2010 Cedric BAIL, Vincent Torri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#ifdef EFL_HAVE_POSIX_THREADS +#include <pthread.h> +#endif + +#ifdef EFL_HAVE_WIN32_THREADS +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN +#endif + +#include "eina_inlist.h" +#include "eina_error.h" +#include "eina_module.h" +#include "eina_mempool.h" +#include "eina_trash.h" + +#include "eina_private.h" + +#ifdef DEBUG +#include "eina_log.h" + +static int _eina_mempool_log_dom = -1; + +#ifdef INF +#undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_eina_mempool_log_dom, __VA_ARGS__) +#endif + +typedef struct _Chained_Mempool Chained_Mempool; +struct _Chained_Mempool +{ + Eina_Inlist *first; + const char *name; + int item_alloc; + int pool_size; + int alloc_size; + int group_size; + int usage; +#ifdef EFL_HAVE_THREADS +# ifdef EFL_HAVE_POSIX_THREADS + pthread_mutex_t mutex; +# else + HANDLE mutex; +# endif +#endif +}; + +typedef struct _Chained_Pool Chained_Pool; +struct _Chained_Pool +{ + EINA_INLIST; + Eina_Trash *base; + int usage; +}; + +static inline Chained_Pool * +_eina_chained_mp_pool_new(Chained_Mempool *pool) +{ + Chained_Pool *p; + unsigned char *ptr; + int i; + + eina_error_set(0); + p = malloc(pool->alloc_size); + if (!p) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + ptr = (unsigned char *)p + eina_mempool_alignof(sizeof(Chained_Pool)); + p->usage = 0; + p->base = NULL; + for (i = 0; i < pool->pool_size; ++i, ptr += pool->item_alloc) + eina_trash_push(&p->base, ptr); + return p; +} + +static inline void +_eina_chained_mp_pool_free(Chained_Pool *p) +{ + free(p); +} + +static void * +eina_chained_mempool_malloc(void *data, __UNUSED__ unsigned int size) +{ + Chained_Mempool *pool = data; + Chained_Pool *p = NULL; + void *mem; + +#ifdef EFL_HAVE_THREADS +# ifdef EFL_HAVE_POSIX_THREADS + pthread_mutex_lock(&pool->mutex); +# else + WaitForSingleObject(pool->mutex, INFINITE); +# endif +#endif + + // look 4 pool from 2nd bucket on + EINA_INLIST_FOREACH(pool->first, p) + { + // base is not NULL - has a free slot + if (p->base) + { + pool->first = eina_inlist_demote(pool->first, EINA_INLIST_GET(p)); + break; + } + } + + // we have reached the end of the list - no free pools + if (!p) + { + p = _eina_chained_mp_pool_new(pool); + if (!p) + { +#ifdef EFL_HAVE_PTHREAD +# ifdef EFL_HAVE_POSIX_THREADS + pthread_mutex_unlock(&pool->mutex); +# else + ReleaseMutex(pool->mutex); +# endif +#endif + return NULL; + } + + pool->first = eina_inlist_prepend(pool->first, EINA_INLIST_GET(p)); + } + + // Request a free pointer + mem = eina_trash_pop(&p->base); + // move to end - it just filled up + if (!p->base) + pool->first = eina_inlist_demote(pool->first, EINA_INLIST_GET(p)); + + p->usage++; + pool->usage++; + +#ifdef EFL_HAVE_THREADS +# ifdef EFL_HAVE_POSIX_THREADS + pthread_mutex_unlock(&pool->mutex); +# else + ReleaseMutex(pool->mutex); +# endif +#endif + + return mem; +} + +static void +eina_chained_mempool_free(void *data, void *ptr) +{ + Chained_Mempool *pool = data; + Chained_Pool *p; + void *pmem; + int psize; + + psize = pool->group_size; + // look 4 pool + +#ifdef EFL_HAVE_THREADS +# ifdef EFL_HAVE_POSIX_THREADS + pthread_mutex_lock(&pool->mutex); +# else + WaitForSingleObject(pool->mutex, INFINITE); +# endif +#endif + + EINA_INLIST_FOREACH(pool->first, p) + { + // pool mem base + pmem = (void *)(((unsigned char *)p) + sizeof(Chained_Pool)); + // is it in pool mem? + if ((ptr >= pmem) && + ((unsigned char *)ptr < (((unsigned char *)pmem) + psize))) + { + // freed node points to prev free node + eina_trash_push(&p->base, ptr); + // next free node is now the one we freed + p->usage--; + pool->usage--; + if (p->usage == 0) + { + // free bucket + pool->first = eina_inlist_remove(pool->first, EINA_INLIST_GET(p)); + _eina_chained_mp_pool_free(p); + } + else + // move to front + pool->first = eina_inlist_promote(pool->first, EINA_INLIST_GET(p)); + + break; + } + } + +#ifdef EFL_HAVE_THREADS +# ifdef EFL_HAVE_POSIX_THREADS + pthread_mutex_unlock(&pool->mutex); +# else + ReleaseMutex(pool->mutex); +# endif +#endif +} + +static void * +eina_chained_mempool_realloc(__UNUSED__ void *data, + __UNUSED__ void *element, + __UNUSED__ unsigned int size) +{ + return NULL; +} + +static void * +eina_chained_mempool_init(const char *context, + __UNUSED__ const char *option, + va_list args) +{ + Chained_Mempool *mp; + int item_size; + size_t length; + + length = context ? strlen(context) + 1 : 0; + + mp = calloc(1, sizeof(Chained_Mempool) + length); + if (!mp) + return NULL; + + item_size = va_arg(args, int); + mp->pool_size = va_arg(args, int); + + if (length) + { + mp->name = (const char *)(mp + 1); + memcpy((char *)mp->name, context, length); + } + + mp->item_alloc = eina_mempool_alignof(item_size); + mp->group_size = mp->item_alloc * mp->pool_size; + mp->alloc_size = mp->group_size + eina_mempool_alignof(sizeof(Chained_Pool)); + +#ifdef EFL_HAVE_THREADS +# ifdef EFL_HAVE_POSIX_THREADS + pthread_mutex_init(&mp->mutex, NULL); +# else + mp->mutex = CreateMutex(NULL, FALSE, NULL); +# endif +#endif + + return mp; +} + +static void +eina_chained_mempool_shutdown(void *data) +{ + Chained_Mempool *mp; + + mp = (Chained_Mempool *)data; + + while (mp->first) + { + Chained_Pool *p = (Chained_Pool *)mp->first; + +#ifdef DEBUG + if (p->usage > 0) + INF("Bad news we are destroying not an empty mempool [%s]\n", + mp->name); + +#endif + + mp->first = eina_inlist_remove(mp->first, mp->first); + _eina_chained_mp_pool_free(p); + } + +#ifdef EFL_HAVE_THREADS +# ifdef EFL_HAVE_POSIX_THREADS + pthread_mutex_destroy(&mp->mutex); +# else + CloseHandle(mp->mutex); +# endif +#endif + + free(mp); +} + +static Eina_Mempool_Backend _eina_chained_mp_backend = { + "chained_mempool", + &eina_chained_mempool_init, + &eina_chained_mempool_free, + &eina_chained_mempool_malloc, + &eina_chained_mempool_realloc, + NULL, + NULL, + &eina_chained_mempool_shutdown +}; + +Eina_Bool chained_init(void) +{ +#ifdef DEBUG + _eina_mempool_log_dom = eina_log_domain_register("eina_mempool", + EINA_LOG_COLOR_DEFAULT); + if (_eina_mempool_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_mempool"); + return EINA_FALSE; + } + +#endif + return eina_mempool_register(&_eina_chained_mp_backend); +} + +void chained_shutdown(void) +{ + eina_mempool_unregister(&_eina_chained_mp_backend); +#ifdef DEBUG + eina_log_domain_unregister(_eina_mempool_log_dom); + _eina_mempool_log_dom = -1; +#endif +} + +#ifndef EINA_STATIC_BUILD_CHAINED_POOL + +EINA_MODULE_INIT(chained_init); +EINA_MODULE_SHUTDOWN(chained_shutdown); + +#endif /* ! EINA_STATIC_BUILD_CHAINED_POOL */ diff --git a/tests/suite/ecore/src/lib/eina_convert.c b/tests/suite/ecore/src/lib/eina_convert.c new file mode 100644 index 0000000000..0d75469ca0 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_convert.c @@ -0,0 +1,773 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric BAIL, Vincent Torri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_log.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_convert.h" +#include "eina_fp.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static const char look_up_table[] = {'0', '1', '2', '3', '4', + '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f'}; +static int _eina_convert_log_dom = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_convert_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_convert_log_dom, __VA_ARGS__) + +#define HEXA_TO_INT(Hexa) (Hexa >= 'a') ? Hexa - 'a' + 10 : Hexa - '0' + +static inline void reverse(char s[], int length) +{ + int i, j; + char c; + + for (i = 0, j = length - 1; i < j; i++, j--) + { + c = s[i]; + s[i] = s[j]; + s[j] = c; + } +} + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +EAPI Eina_Error EINA_ERROR_CONVERT_P_NOT_FOUND = 0; +EAPI Eina_Error EINA_ERROR_CONVERT_0X_NOT_FOUND = 0; +EAPI Eina_Error EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH = 0; + +static const char EINA_ERROR_CONVERT_0X_NOT_FOUND_STR[] = + "Error during string conversion to float, First '0x' was not found."; +static const char EINA_ERROR_CONVERT_P_NOT_FOUND_STR[] = + "Error during string conversion to float, First 'p' was not found."; +static const char EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH_STR[] = + "Error outrun string limit during conversion string conversion to float."; + +/** + * @endcond + */ + +/** + * @internal + * @brief Initialize the convert module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the convert module of Eina. It is called by + * eina_init(). + * + * This function sets up the error module of Eina and registers the + * errors #EINA_ERROR_CONVERT_0X_NOT_FOUND, + * #EINA_ERROR_CONVERT_P_NOT_FOUND and + * #EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH. + * + * @see eina_init() + */ +Eina_Bool +eina_convert_init(void) +{ + _eina_convert_log_dom = eina_log_domain_register("eina_convert", + EINA_LOG_COLOR_DEFAULT); + if (_eina_convert_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_convert"); + return EINA_FALSE; + } + +#define EEMR(n) n = eina_error_msg_static_register(n ## _STR) + EEMR(EINA_ERROR_CONVERT_0X_NOT_FOUND); + EEMR(EINA_ERROR_CONVERT_P_NOT_FOUND); + EEMR(EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH); +#undef EEMR + + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the convert module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the convert module set up by + * eina_convert_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_convert_shutdown(void) +{ + eina_log_domain_unregister(_eina_convert_log_dom); + _eina_convert_log_dom = -1; + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Convert_Group Convert + * + * These functions allow you to convert integer or real numbers to + * string or conversely. + * + * To use these functions, you have to call eina_init() + * first, and eina_shutdown() when eina is not used anymore. + * + * @section Eina_Convert_From_Integer_To_Sring Conversion from integer to string + * + * To convert an integer to a string in the decimal base, + * eina_convert_itoa() should be used. If the hexadecimal base is + * wanted, eina_convert_xtoa() should be used. They all need a bufffer + * sufficiently large to store all the cyphers. + * + * Here is an example of use: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * + * #include <Eina.h> + * + * int main(void) + * { + * char tmp[128]; + * + * if (!eina_init()) + * { + * printf ("Error during the initialization of eina.\n"); + * return EXIT_FAILURE; + * } + * + * eina_convert_itoa(45, tmp); + * printf("value: %s\n", tmp); + + * eina_convert_xtoa(0xA1, tmp); + * printf("value: %s\n", tmp); + * + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * } + * @endcode + * + * Compile this code with the following command: + * + * @code + * gcc -Wall -o test_eina_convert test_eina.c `pkg-config --cflags --libs eina` + * @endcode + * + * @note + * The alphabetical cyphers are in lower case. + * + * @section Eina_Convert_Double Conversion double / string + * + * To convert a double to a string, eina_convert_dtoa() should be + * used. Like with the integer functions, a buffer must be used. The + * resulting string ghas the following format (which is the result + * obtained with snprintf() and the @%a modifier): + * + * @code + * [-]0xh.hhhhhp[+-]e + * @endcode + * + * To convert a string to a double, eina_convert_atod() should be + * used. The format of the string must be as above. Then, the double + * has the following mantiss and exponent: + * + * @code + * mantiss : [-]hhhhhh + * exponent : 2^([+-]e - 4 * n) + * @endcode + * + * with n being number of cypers after the point in the string + * format. To obtain the double number from the mantiss and exponent, + * use ldexp(). + * + * Here is an example of use: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * #include <math.h> + * + * #include <Eina.h> + * + * int main(void) + * { + * char tmp[128]; + * long long int m = 0; + * long int e = 0; + * double r; + * + * if (!eina_init()) + * { + * printf ("Error during the initialization of eina.\n"); + * return EXIT_FAILURE; + * } + * + * printf("initial value : 40.56\n"); + * eina_convert_dtoa(40.56, tmp); + * printf("result dtoa : %s\n", tmp); + + * eina_convert_atod(tmp, 128, &m, &e); + * r = ldexp((double)m, e); + * printf("result atod : %f\n", r); + * + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * } + * @endcode + * + * Compile this code with the following command: + * + * @code + * gcc -Wall -o test_eina_convert test_eina.c `pkg-config --cflags --libs eina` -lm + * @endcode + * + * @{ + */ + +/* + * Come from the second edition of The C Programming Language ("K&R2") on page 64 + */ + +/** + * @brief Convert an integer number to a string in decimal base. + * + * @param n The integer to convert. + * @param s The buffer to store the converted integer. + * @return The length of the string, including the nul terminated + * character. + * + * This function converts @p n to a nul terminated string. The + * converted string is in decimal base. As no check is done, @p s must + * be a buffer that is sufficiently large to store the integer. + * + * The returned value is the length of the string, including the nul + * terminated character. + */ +EAPI int +eina_convert_itoa(int n, char *s) +{ + int i = 0; + int r = 0; + + EINA_SAFETY_ON_NULL_RETURN_VAL(s, 0); + + if (n < 0) + { + n = -n; + *s++ = '-'; + r = 1; + } + + do { + s[i++] = n % 10 + '0'; + } while ((n /= 10) > 0); + + s[i] = '\0'; + + reverse(s, i); + + return i + r; +} + +/** + * @brief Convert an integer number to a string in hexadecimal base. + * + * @param n The integer to convert. + * @param s The buffer to store the converted integer. + * @return The length of the string, including the nul terminated + * character. + * + * This function converts @p n to a nul terminated string. The + * converted string is in hexadecimal base and the alphabetical + * cyphers are in lower case. As no check is done, @p s must be a + * buffer that is sufficiently large to store the integer. + * + * The returned value is the length of the string, including the nul + * terminated character. + */ +EAPI int +eina_convert_xtoa(unsigned int n, char *s) +{ + int i; + + EINA_SAFETY_ON_NULL_RETURN_VAL(s, 0); + + i = 0; + do { + s[i++] = look_up_table[n & 0xF]; + } while ((n >>= 4) > 0); + + s[i] = '\0'; + + reverse(s, i); + + return i; +} + +/** + * @brief Convert a string to a double. + * + * @param src The string to convert. + * @param length The length of the string. + * @param m The mantisse. + * @param e The exponent. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function converts the string @p s of length @p length that + * represent a double in hexadecimal base to a double. It is used to + * replace the use of snprintf() with the \%a modifier, which is + * missing on some platform (like Windows (tm) or OpenBSD). + * + * The string must have the following format: + * + * @code + * [-]0xh.hhhhhp[+-]e + * @endcode + * + * where the h are the hexadecimal cyphers of the mantiss and e the + * exponent (a decimal number). If n is the number of cypers after the + * point, the returned mantiss and exponents are: + * + * @code + * mantiss : [-]hhhhhh + * exponent : 2^([+-]e - 4 * n) + * @endcode + * + * The mantiss and exponent are stored in the buffers pointed + * respectively by @p m and @p e. + * + * If the string is invalid, the error is set to: + * + * @li #EINA_ERROR_CONVERT_0X_NOT_FOUND if no 0x is found, + * @li #EINA_ERROR_CONVERT_P_NOT_FOUND if no p is found, + * @li #EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH if @p length is not + * correct. + * + * In those cases, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +EAPI Eina_Bool +eina_convert_atod(const char *src, int length, long long *m, long *e) +{ + const char *str = src; + long long mantisse; + long exponent; + int nbr_decimals = 0; + int sign = 1; + + EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(e, EINA_FALSE); + + if (length <= 0) + goto on_length_error; + + /* Compute the mantisse. */ + if (*str == '-') + { + sign = -1; + str++; + length--; + } + + if (length <= 2) + goto on_length_error; + + if (strncmp(str, "0x", 2)) + { + eina_error_set(EINA_ERROR_CONVERT_0X_NOT_FOUND); + DBG("'0x' not found in '%s'", src); + return EINA_FALSE; + } + + str += 2; + length -= 2; + + mantisse = HEXA_TO_INT(*str); + + str++; + length--; if (length <= 0) + goto on_length_error; + + if (*str == '.') + for (str++, length--; + length > 0 && *str != 'p'; + ++str, --length, ++nbr_decimals) + { + mantisse <<= 4; + mantisse += HEXA_TO_INT(*str); + } + + if (sign < 0) + mantisse = -mantisse; + + /* Compute the exponent. */ + if (*str != 'p') + { + eina_error_set(EINA_ERROR_CONVERT_P_NOT_FOUND); + DBG("'p' not found in '%s'", src); + return EINA_FALSE; + } + + sign = +1; + + str++; + length--; if (length <= 0) + goto on_length_error; + + if (strchr("-+", *str)) + { + sign = (*str == '-') ? -1 : +1; + + str++; length--; + } + + for (exponent = 0; length > 0 && *str != '\0'; ++str, --length) + { + exponent *= 10; + exponent += *str - '0'; + } + + if (length < 0) + goto on_length_error; + + if (sign < 0) + exponent = -exponent; + + *m = mantisse; + *e = exponent - (nbr_decimals << 2); + + return EINA_TRUE; + +on_length_error: + eina_error_set(EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH); + return EINA_FALSE; +} + +/** + * @brief Convert a double to a string. + * + * @param d The double to convert. + * @param des The destination buffer to store the converted double. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function converts the double @p d to a string. The string is + * stored in the buffer pointed by @p des and must be sufficiently + * large to contain the converted double. The returned string is nul + * terminated and has the following format: + * + * @code + * [-]0xh.hhhhhp[+-]e + * @endcode + * + * where the h are the hexadecimal cyphers of the mantiss and e the + * exponent (a decimal number). + * + * The returned value is the length of the string, including the nul + * character. + */ +EAPI int +eina_convert_dtoa(double d, char *des) +{ + int length = 0; + int p; + int i; + + EINA_SAFETY_ON_NULL_RETURN_VAL(des, EINA_FALSE); + + if (d < 0.0) + { + *(des++) = '-'; + d = -d; + length++; + } + + d = frexp(d, &p); + + if (p) + { + d *= 2; + p -= 1; + } + + *(des++) = '0'; + *(des++) = 'x'; + *(des++) = look_up_table[(size_t)d]; + *(des++) = '.'; + length += 4; + + for (i = 0; i < 16; i++, length++) + { + d -= floor(d); + d *= 16; + *(des++) = look_up_table[(size_t)d]; + } + + while (*(des - 1) == '0') + { + des--; + length--; + } + + if (*(des - 1) == '.') + { + des--; + length--; + } + + *(des++) = 'p'; + if (p < 0) + { + *(des++) = '-'; + p = -p; + } + else + *(des++) = '+'; + + length += 2; + + return length + eina_convert_itoa(p, des); +} + +/** + * @brief Convert a 32.32 fixed point number to a string. + * + * @param fp The fixed point number to convert. + * @param des The destination buffer to store the converted fixed point number. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function converts the 32.32 fixed point number @p fp to a + * string. The string is stored in the buffer pointed by @p des and + * must be sufficiently large to contain the converted fixed point + * number. The returned string is terminated and has the following + * format: + * + * @code + * [-]0xh.hhhhhp[+-]e + * @endcode + * + * where the h are the hexadecimal cyphers of the mantiss and e the + * exponent (a decimal number). + * + * The returned value is the length of the string, including the nul + * character. + * + * @note The code is the same than eina_convert_dtoa() except that it + * implements the frexp() function for fixed point numbers and does + * some optimisations. + */ +EAPI int +eina_convert_fptoa(Eina_F32p32 fp, char *des) +{ + int length = 0; + int p = 0; + int i; + + EINA_SAFETY_ON_NULL_RETURN_VAL(des, EINA_FALSE); + + if (fp == 0) + { + memcpy(des, "0x0p+0", 7); + return 7; + } + + if (fp < 0) + { + *(des++) = '-'; + fp = -fp; + length++; + } + + /* fp >= 1 */ + if (fp >= 0x0000000100000000LL) + while (fp >= 0x0000000100000000LL) + { + p++; + /* fp /= 2 */ + fp >>= 1; + } /* fp < 0.5 */ + else if (fp < 0x80000000) + while (fp < 0x80000000) + { + p--; + /* fp *= 2 */ + fp <<= 1; + } + + if (p) + { + p--; + /* fp *= 2 */ + fp <<= 1; + } + + *(des++) = '0'; + *(des++) = 'x'; + *(des++) = look_up_table[fp >> 32]; + *(des++) = '.'; + length += 4; + + for (i = 0; i < 16; i++, length++) + { + fp &= 0x00000000ffffffffLL; + fp <<= 4; /* fp *= 16 */ + *(des++) = look_up_table[fp >> 32]; + } + + while (*(des - 1) == '0') + { + des--; + length--; + } + + if (*(des - 1) == '.') + { + des--; + length--; + } + + *(des++) = 'p'; + if (p < 0) + { + *(des++) = '-'; + p = -p; + } + else + *(des++) = '+'; + + length += 2; + + return length + eina_convert_itoa(p, des); +} + +/** + * @brief Convert a string to a 32.32 fixed point number. + * + * @param src The string to convert. + * @param length The length of the string. + * @param fp The fixed point number. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function converts the string @p src of length @p length that + * represent a double in hexadecimal base to a 32.32 fixed point + * number stored in @p fp. The function always tries to convert the + * string with eina_convert_atod(). + * + * The string must have the following format: + * + * @code + * [-]0xh.hhhhhp[+-]e + * @endcode + * + * where the h are the hexadecimal cyphers of the mantiss and e the + * exponent (a decimal number). If n is the number of cypers after the + * point, the returned mantiss and exponents are: + * + * @code + * mantiss : [-]hhhhhh + * exponent : 2^([+-]e - 4 * n) + * @endcode + * + * The mantiss and exponent are stored in the buffers pointed + * respectively by @p m and @p e. + * + * If the string is invalid, the error is set to: + * + * @li #EINA_ERROR_CONVERT_0X_NOT_FOUND if no 0x is found, + * @li #EINA_ERROR_CONVERT_P_NOT_FOUND if no p is found, + * @li #EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH if @p length is not + * correct. + * + * In those cases, or if @p fp is @c NULL, #EINA_FALSE is returned, + * otherwise @p fp is computed and #EINA_TRUE is returned. + * + * @note The code uses eina_convert_atod() and do the correct bit + * shift to compute the fixed point number. + */ +EAPI Eina_Bool +eina_convert_atofp(const char *src, int length, Eina_F32p32 *fp) +{ + long long m; + long e; + + if (!eina_convert_atod(src, length, &m, &e)) + return EINA_FALSE; + + if (!fp) + return EINA_TRUE; + + e += 32; + + if (e > 0) + *fp = m << e; + else + *fp = m >> -e; + + return EINA_TRUE; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_counter.c b/tests/suite/ecore/src/lib/eina_counter.c new file mode 100644 index 0000000000..8c430dc162 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_counter.c @@ -0,0 +1,505 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail, Vincent Torri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#ifndef _WIN32 +# include <time.h> +#else +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN +#endif /* _WIN2 */ + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_inlist.h" +#include "eina_error.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_counter.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +#ifndef _WIN32 +typedef struct timespec Eina_Nano_Time; +#else +typedef LARGE_INTEGER Eina_Nano_Time; +#endif + +typedef struct _Eina_Clock Eina_Clock; + +struct _Eina_Counter +{ + EINA_INLIST; + + Eina_Inlist *clocks; + const char *name; +}; + +struct _Eina_Clock +{ + EINA_INLIST; + + Eina_Nano_Time start; + Eina_Nano_Time end; + int specimen; + + Eina_Bool valid; +}; + +#ifndef _WIN32 +static inline int +_eina_counter_time_get(Eina_Nano_Time *tp) +{ +# if defined(CLOCK_PROCESS_CPUTIME_ID) + return clock_gettime(CLOCK_PROCESS_CPUTIME_ID, tp); +# elif defined(CLOCK_PROF) + return clock_gettime(CLOCK_PROF, tp); +# elif defined(CLOCK_REALTIME) + return clock_gettime(CLOCK_REALTIME, tp); +# else + return gettimeofday(tp, NULL); +# endif +} +#else +static const char EINA_ERROR_COUNTER_WINDOWS_STR[] = + "Change your OS, you moron !"; +static int EINA_ERROR_COUNTER_WINDOWS = 0; +static LARGE_INTEGER _eina_counter_frequency; + +static inline int +_eina_counter_time_get(Eina_Nano_Time *tp) +{ + return QueryPerformanceCounter(tp); +} +#endif /* _WIN2 */ + +static char * +_eina_counter_asiprintf(char *base, int *position, const char *format, ...) +{ + char *tmp, *result; + int size = 32; + int n; + va_list ap; + + tmp = realloc(base, sizeof (char) * (*position + size)); + if (!tmp) + return base; + + result = tmp; + + while (1) + { + va_start(ap, format); + n = vsnprintf(result + *position, size, format, ap); + va_end(ap); + + if (n > -1 && n < size) + { + /* If we always have glibc > 2.2, we could just return *position += n. */ + *position += strlen(result + *position); + return result; + } + + if (n > -1) + size = n + 1; + else + size <<= 1; + + tmp = realloc(result, sizeof (char) * (*position + size)); + if (!tmp) + return result; + + result = tmp; + } +} + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the eina counter internal structure. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the counter module set up by + * eina_counter_init(). It is called by eina_init(). + * + * This function sets up the error module of Eina and only on Windows, + * it initializes the high precision timer. It also registers, only on + * Windows, the error #EINA_ERROR_COUNTER_WINDOWS. It is also called + * by eina_init(). It returns 0 on failure, otherwise it returns the + * number of times it has already been called. + * + * @see eina_init() + */ +Eina_Bool +eina_counter_init(void) +{ +#ifdef _WIN32 + EINA_ERROR_COUNTER_WINDOWS = eina_error_msg_static_register( + EINA_ERROR_COUNTER_WINDOWS_STR); + if (!QueryPerformanceFrequency(&_eina_counter_frequency)) + { + eina_error_set(EINA_ERROR_COUNTER_WINDOWS); + return EINA_FALSE; + } + +#endif /* _WIN2 */ + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the counter module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the counter module set up by + * eina_counter_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_counter_shutdown(void) +{ + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Counter_Group Counter + * + * @brief These functions allow you to get the time spent in a part of a code. + * + * Before using the counter system, Eina must be initialized with + * eina_init() and later shut down with eina_shutdown(). The create a + * counter, use eina_counter_new(). To free it, use + * eina_counter_free(). + * + * To time a part of a code, call eina_counter_start() just before it, + * and eina_counter_stop() just after it. Each time you start to time + * a code, a clock is added to a list. You can give a number of that + * clock with the second argument of eina_counter_stop(). To send all + * the registered clocks to a stream (like stdout, ofr a file), use + * eina_counter_dump(). + * + * Here is a straightforward example: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * + * #include <eina_counter.h> + * + * void test_malloc(void) + * { + * int i; + * + * for (i = 0; i < 100000; ++i) + * { + * void *buf; + * + * buf = malloc(100); + * free(buf); + * } + * } + * + * int main(void) + * { + * Eina_Counter *counter; + * + * if (!eina_init()) + * { + * printf("Error during the initialization of eina\n"); + * return EXIT_FAILURE; + * } + * + * counter = eina_counter_new("malloc"); + * + * eina_counter_start(counter); + * test_malloc(); + * eina_counter_stop(counter, 1); + * + * char* result = eina_counter_dump(counter); + * printf("%s", result); + * free(result); + * + * eina_counter_free(counter); + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * } + * @endcode + * + * Compile this code with the following commant: + * + * @verbatim + * gcc -Wall -o test_eina_counter test_eina.c `pkg-config --cflags --libs eina` + * @endverbatim + * + * The result should be something like that: + * + * @verbatim + * \# specimen experiment time starting time ending time + * 1 9794125 783816 10577941 + * @endverbatim + * + * Note that the displayed time is in nanosecond. + * + * @{ + */ + +/** + * @brief Return a counter. + * + * @param name The name of the counter. + * + * This function returns a new counter. It is characterized by @p + * name. If @p name is @c NULL, the function returns @c NULL + * immediately. If memory allocation fails, @c NULL is returned and the + * error is set to #EINA_ERROR_OUT_OF_MEMORY. + * + * Whe the new counter is not needed anymore, use eina_counter_free() to + * free the allocated memory. + */ +EAPI Eina_Counter * +eina_counter_new(const char *name) +{ + Eina_Counter *counter; + size_t length; + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL); + + length = strlen(name) + 1; + + eina_error_set(0); + counter = calloc(1, sizeof (Eina_Counter) + length); + if (!counter) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + counter->name = (char *)(counter + 1); + memcpy((char *)counter->name, name, length); + + return counter; +} + +/** + * @brief Delete a counter. + * + * @param counter The counter to delete. + * + * This function remove the clock of @p counter from the used clocks + * (see eina_counter_start()) and frees the memory allocated for + * @p counter. If @p counter is @c NULL, the function returns + * immediately. + */ +EAPI void +eina_counter_free(Eina_Counter *counter) +{ + EINA_SAFETY_ON_NULL_RETURN(counter); + + while (counter->clocks) + { + Eina_Clock *clk = (Eina_Clock *)counter->clocks; + + counter->clocks = eina_inlist_remove(counter->clocks, counter->clocks); + free(clk); + } + + free(counter); +} + +/** + * @brief Start the time count. + * + * @param counter The counter. + * + * This function specifies that the part of the code beginning just + * after its call is being to be timed, using @p counter. If + * @p counter is @c NULL, this function returns immediately. + * + * This function adds the clock associated to @p counter in a list. If + * the memory needed by that clock can not be allocated, the function + * returns and the error is set to #EINA_ERROR_OUT_OF_MEMORY. + * + * To stop the timing, eina_counter_stop() must be called with the + * same counter. + */ +EAPI void +eina_counter_start(Eina_Counter *counter) +{ + Eina_Clock *clk; + Eina_Nano_Time tp; + + EINA_SAFETY_ON_NULL_RETURN(counter); + if (_eina_counter_time_get(&tp) != 0) + return; + + eina_error_set(0); + clk = calloc(1, sizeof (Eina_Clock)); + if (!clk) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return; + } + + counter->clocks = eina_inlist_prepend(counter->clocks, EINA_INLIST_GET(clk)); + + clk->valid = EINA_FALSE; + clk->start = tp; +} + +/** + * @brief Stop the time count. + * + * @param counter The counter. + * @param specimen The number of the test. + * + * This function stop the timing that has been started with + * eina_counter_start(). @p counter must be the same than the one used + * with eina_counter_start(). @p specimen is the number of the + * test. If @p counter or its associated clock are @c NULL, or if the + * time can't be retrieved the function exits. + */ +EAPI void +eina_counter_stop(Eina_Counter *counter, int specimen) +{ + Eina_Clock *clk; + Eina_Nano_Time tp; + + EINA_SAFETY_ON_NULL_RETURN(counter); + if (_eina_counter_time_get(&tp) != 0) + return; + + clk = (Eina_Clock *)counter->clocks; + + if (!clk || clk->valid == EINA_TRUE) + return; + + clk->end = tp; + clk->specimen = specimen; + clk->valid = EINA_TRUE; +} + +/** + * @brief Dump the result of all clocks of a counter to a stream. + * + * @return A string with a summary of the test. + * @param counter The counter. + * + * This function returns an malloc'd string containing the dump of + * all the valid clocks of @p counter. + * If @p counter @c NULL, the functions exits + * immediately. Otherwise, the output is formattted like that: + * + * @verbatim + * \# specimen experiment time starting time ending time + * 1 208 120000 120208 + * @endverbatim + * + * The unit of time is the nanosecond. + */ +EAPI char * +eina_counter_dump(Eina_Counter *counter) +{ + Eina_Clock *clk; + char *result = NULL; + int position = 0; + + EINA_SAFETY_ON_NULL_RETURN_VAL(counter, NULL); + + result = _eina_counter_asiprintf( + result, + &position, + "# specimen\texperiment time\tstarting time\tending time\n"); + if (!result) + return NULL; + + EINA_INLIST_REVERSE_FOREACH(counter->clocks, clk) + { + long int start; + long int end; + long int diff; + + if (clk->valid == EINA_FALSE) + continue; + +#ifndef _WIN32 + start = clk->start.tv_sec * 1000000000 + clk->start.tv_nsec; + end = clk->end.tv_sec * 1000000000 + clk->end.tv_nsec; + diff = + (clk->end.tv_sec - + clk->start.tv_sec) * 1000000000 + clk->end.tv_nsec - + clk->start.tv_nsec; +#else + start = + (long int)(((long long int)clk->start.QuadPart * + 1000000000ll) / + (long long int)_eina_counter_frequency.QuadPart); + end = + (long int)(((long long int)clk->end.QuadPart * + 1000000000LL) / + (long long int)_eina_counter_frequency.QuadPart); + diff = + (long int)(((long long int)(clk->end.QuadPart - + clk->start.QuadPart) * + 1000000000LL) / + (long long int)_eina_counter_frequency.QuadPart); +#endif /* _WIN2 */ + + result = _eina_counter_asiprintf(result, &position, + "%i\t%li\t%li\t%li\n", + clk->specimen, + diff, + start, + end); + } + + return result; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_cpu.c b/tests/suite/ecore/src/lib/eina_cpu.c new file mode 100644 index 0000000000..1ee411fac6 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_cpu.c @@ -0,0 +1,208 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef EFL_HAVE_THREADS +# ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# elif defined (__SUNPRO_C) || defined(__GNU__) +# include <unistd.h> +# elif defined (__FreeBSD__) || defined (__OpenBSD__) || \ + defined (__NetBSD__) || defined (__DragonFly__) || defined (__MacOSX__) || \ + (defined (__MACH__) && defined (__APPLE__)) +# include <unistd.h> +# include <sys/param.h> +# include <sys/sysctl.h> +# elif defined (__linux__) || defined(__GLIBC__) +# define _GNU_SOURCE +# include <sched.h> +# endif +# ifdef EFL_HAVE_POSIX_THREADS +# include <pthread.h> +# endif + +# define TH_MAX 8 +#endif + +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "eina_cpu.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/* FIXME this ifdefs should be replaced */ +#if defined(__i386__) || defined(__x86_64__) +/* We save ebx and restore it to be PIC compatible */ +static inline void _x86_cpuid(int op, int *a, int *b, int *c, int *d) +{ + asm volatile ( +#if defined(__x86_64__) + "pushq %%rbx \n\t" /* save %ebx */ +#else + "pushl %%ebx \n\t" /* save %ebx */ +#endif + "cpuid \n\t" + "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */ +#if defined(__x86_64__) + "popq %%rbx \n\t" /* restore the old %ebx */ +#else + "popl %%ebx \n\t" /* restore the old %ebx */ +#endif + : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d) + : "a" (op) + : "cc"); +} + +static +void _x86_simd(Eina_Cpu_Features *features) +{ + int a, b, c, d; + + _x86_cpuid(1, &a, &b, &c, &d); + /* + * edx + * 18 = PN (Processor Number) + * 19 = CLFlush (Cache Line Flush) + * 23 = MMX + * 25 = SSE + * 26 = SSE2 + * 28 = HTT (Hyper Threading) + * ecx + * 0 = SSE3 + */ + if ((d >> 23) & 1) + *features |= EINA_CPU_MMX; + + if ((d >> 25) & 1) + *features |= EINA_CPU_SSE; + + if ((d >> 26) & 1) + *features |= EINA_CPU_SSE2; + + if (c & 1) + *features |= EINA_CPU_SSE3; +} +#endif + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +/* FIXME the features checks should be called when this function is called? + * or make it static by doing eina_cpu_init() and return a local var + */ +/** + * + * @return + */ +EAPI Eina_Cpu_Features eina_cpu_features_get(void) +{ + Eina_Cpu_Features ecf = 0; +#if defined(__i386__) || defined(__x86_64__) + _x86_simd(&ecf); +#endif + return ecf; +} + +EAPI int eina_cpu_count(void) +{ +#ifdef EFL_HAVE_THREADS + +# if defined (_WIN32) + SYSTEM_INFO sysinfo; + + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; + +# elif defined (__SUNPRO_C) || defined(__GNU__) + /* + * _SC_NPROCESSORS_ONLN: number of processors that are online, that + is available when sysconf is called. The number + of cpu can change by admins. + * _SC_NPROCESSORS_CONF: maximum number of processors that are available + to the current OS instance. That number can be + change after a reboot. + * _SC_NPROCESSORS_MAX : maximum number of processors that are on the + motherboard. + */ + return sysconf(_SC_NPROCESSORS_ONLN); + +# elif defined (__FreeBSD__) || defined (__OpenBSD__) || \ + defined (__NetBSD__) || defined (__DragonFly__) || defined (__MacOSX__) || \ + (defined (__MACH__) && defined (__APPLE__)) + + int mib[4]; + int cpus; + size_t len = sizeof(cpus); + + mib[0] = CTL_HW; +#ifdef HW_AVAILCPU + mib[1] = HW_AVAILCPU; +#else + mib[1] = HW_NCPU; +#endif + sysctl(mib, 2, &cpus, &len, NULL, 0); + if (cpus < 1) + cpus = 1; + + return cpus; + +# elif defined (__linux__) || defined(__GLIBC__) + cpu_set_t cpu; + int i; + static int cpus = 0; + + if (cpus != 0) + return cpus; + + CPU_ZERO(&cpu); + if (sched_getaffinity(0, sizeof(cpu), &cpu) != 0) + { + fprintf(stderr, "[Eina] could not get cpu affinity: %s\n", + strerror(errno)); + return 1; + } + + for (i = 0; i < TH_MAX; i++) + { + if (CPU_ISSET(i, &cpu)) + cpus = i + 1; + else + break; + } + return cpus; + +# else +# error "eina_cpu_count() error: Platform not supported" +# endif +#else + return 1; +#endif +} diff --git a/tests/suite/ecore/src/lib/eina_error.c b/tests/suite/ecore/src/lib/eina_error.c new file mode 100644 index 0000000000..f273ca51c1 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_error.c @@ -0,0 +1,463 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +/** + * @page tutorial_error_page Error Tutorial + * + * @section tutorial_error_introduction Introduction + * + * The Eina error module provides a way to manage errors in a simple + * but powerful way in libraries and modules. It is also used in Eina + * itself. Similar to libC's @c errno and strerror() facilities, this + * is extensible and recommended for other libraries and applications. + * + * @section tutorial_error_registering_msg Registering messages + * + * The error module can provide a system that mimic the errno system + * of the C standard library. It consists in 2 parts: + * + * @li a way of registering new messages with + * eina_error_msg_register() and eina_error_msg_get(), + * @li a way of setting / getting last error message with + * eina_error_set() / eina_error_get(). + * + * So one has to fisrt register all the error messages that a program + * or a lib should manage. Then, when an error can occur, use + * eina_error_set(), and when errors are managed, use + * eina_error_get(). If eina_error_set() is used to set an error, do + * not forget to call before eina_error_set0), to remove previous set + * errors. + * + * Here is an example of use: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * + * #include <eina_error.h> + * + * Eina_Error MY_ERROR_NEGATIVE; + * Eina_Error MY_ERROR_NULL; + * + * voi *data_new() + * { + * eina_error_set(0); + * + * eina_error_set(MY_ERROR_NULL); + * return NULL; + * } + * + * int test(int n) + * { + * eina_error_set(0); + * + * if (n < 0) + * { + * eina_error_set(MY_ERROR_NEGATIVE); + * return 0; + * } + * + * return 1; + * } + * + * int main(void) + * { + * void *data; + * + * if (!eina_init()) + * { + * printf ("Error during the initialization of eina_error module\n"); + * return EXIT_FAILURE; + * } + * + * MY_ERROR_NEGATIVE = eina_error_msg_register("Negative number"); + * MY_ERROR_NULL = eina_error_msg_register("NULL pointer"); + + * data = data_new(); + * if (!data) + * { + * Eina_Error err; + * + * err = eina_error_get(); + * if (err) + * printf("Error during memory allocation: %s\n", + * eina_error_msg_get(err)); + * } + * + * if (!test(0)) + * { + * Eina_Error err; + * + * err = eina_error_get(); + * if (err) + * printf("Error during test function: %s\n", + * eina_error_msg_get(err)); + * } + * + * if (!test(-1)) + * { + * Eina_Error err; + * + * err = eina_error_get(); + * if (err) + * printf("Error during test function: %s\n", + * eina_error_msg_get(err)); + * } + * + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * } + * @endcode + * + * Of course, instead of printf(), eina_log_print() can be used to + * have beautiful error messages. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" + + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_error.h" + +/* TODO + * + add a wrapper for assert? + * + add common error numbers, messages + * + add a calltrace of errors, not only store the last error but a list of them + * and also store the function that set it + */ + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +typedef struct _Eina_Error_Message Eina_Error_Message; +struct _Eina_Error_Message +{ + Eina_Bool string_allocated; + const char *string; +}; + +static Eina_Error_Message *_eina_errors = NULL; +static size_t _eina_errors_count = 0; +static size_t _eina_errors_allocated = 0; +static Eina_Error _eina_last_error; + +static Eina_Error_Message * +_eina_error_msg_alloc(void) +{ + size_t idx; + + if (_eina_errors_count == _eina_errors_allocated) + { + void *tmp; + size_t size; + + if (EINA_UNLIKELY(_eina_errors_allocated == 0)) + size = 24; + else + size = _eina_errors_allocated + 8; + + tmp = realloc(_eina_errors, sizeof(Eina_Error_Message) * size); + if (!tmp) + return NULL; + + _eina_errors = tmp; + _eina_errors_allocated = size; + } + + idx = _eina_errors_count; + _eina_errors_count++; + return _eina_errors + idx; +} + +/** + * @endcond + */ + + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +EAPI Eina_Error EINA_ERROR_OUT_OF_MEMORY = 0; + +static const char EINA_ERROR_OUT_OF_MEMORY_STR[] = "Out of memory"; + +/** + * @endcond + */ + +/** + * @internal + * @brief Initialize the error module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the error module of Eina. It is called by + * eina_init(). + * + * This function registers the error #EINA_ERROR_OUT_OF_MEMORY. + * + * @see eina_init() + */ +Eina_Bool +eina_error_init(void) +{ + /* TODO register the eina's basic errors */ + EINA_ERROR_OUT_OF_MEMORY = eina_error_msg_static_register( + EINA_ERROR_OUT_OF_MEMORY_STR); + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the error module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the error module set up by + * eina_error_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_error_shutdown(void) +{ + Eina_Error_Message *eem, *eem_end; + + eem = _eina_errors; + eem_end = eem + _eina_errors_count; + + for (; eem < eem_end; eem++) + if (eem->string_allocated) + free((char *)eem->string); + + free(_eina_errors); + _eina_errors = NULL; + _eina_errors_count = 0; + _eina_errors_allocated = 0; + + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Error_Group Error + * + * @brief These functions provide error management for projects. + * + * To use the error system Eina must be initialized with eina_init() + * and later shut down with eina_shutdown(). Error codes are + * registered with eina_error_msg_register() and converted from + * identifier to original message string with eina_error_msg_get(). + * + * Logging functions are not in eina_error anymore, see + * eina_log_print() instead. + * + * @{ + */ + +/** + * @brief Register a new error type. + * + * @param msg The description of the error. It will be duplicated using + * strdup(). + * @return The unique number identifier for this error. + * + * This function stores in a list the error message described by + * @p msg. The returned value is a unique identifier greater or equal + * than 1. The description can be retrieve later by passing to + * eina_error_msg_get() the returned value. + * + * @see eina_error_msg_static_register() + */ +EAPI Eina_Error +eina_error_msg_register(const char *msg) +{ + Eina_Error_Message *eem; + + EINA_SAFETY_ON_NULL_RETURN_VAL(msg, 0); + + eem = _eina_error_msg_alloc(); + if (!eem) + return 0; + + eem->string_allocated = EINA_TRUE; + eem->string = strdup(msg); + if (!eem->string) + { + _eina_errors_count--; + return 0; + } + + return _eina_errors_count; /* identifier = index + 1 (== _count). */ +} + +/** + * @brief Register a new error type, statically allocated message. + * + * @param msg The description of the error. This string will not be + * duplicated and thus the given pointer should live during + * usage of eina_error. + * @return The unique number identifier for this error. + * + * This function stores in a list the error message described by + * @p msg. The returned value is a unique identifier greater or equal + * than 1. The description can be retrieve later by passing to + * eina_error_msg_get() the returned value. + * + * @see eina_error_msg_register() + */ +EAPI Eina_Error +eina_error_msg_static_register(const char *msg) +{ + Eina_Error_Message *eem; + + EINA_SAFETY_ON_NULL_RETURN_VAL(msg, 0); + + eem = _eina_error_msg_alloc(); + if (!eem) + return 0; + + eem->string_allocated = EINA_FALSE; + eem->string = msg; + return _eina_errors_count; /* identifier = index + 1 (== _count). */ +} + +/** + * @brief Change the message of an already registered message + * + * @param error The Eina_Error to change the message of + * @param msg The description of the error. This string will be + * duplicated only if the error was registered with @ref eina_error_msg_register + * otherwise it must remain intact for the duration + * @return EINA_TRUE if successful, EINA_FALSE on error + * + * This function modifies the message associated with @p error and changes + * it to @p msg. If the error was previously registered by @ref eina_error_msg_static_register + * then the string will not be duplicated, otherwise the previous message + * will be freed and @p msg copied. + * + * @see eina_error_msg_register() + */ +EAPI Eina_Bool +eina_error_msg_modify(Eina_Error error, const char *msg) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(msg, EINA_FALSE); + if (error < 1) + return EINA_FALSE; + + if ((size_t)error > _eina_errors_count) + return EINA_FALSE; + + if (_eina_errors[error - 1].string_allocated) + { + const char *tmp; + + if (!(tmp = strdup(msg))) + return EINA_FALSE; + + free((void *)_eina_errors[error - 1].string); + _eina_errors[error - 1].string = tmp; + return EINA_TRUE; + } + + _eina_errors[error - 1].string = msg; + return EINA_TRUE; +} + +/** + * @brief Return the description of the given an error number. + * + * @param error The error number. + * @return The description of the error. + * + * This function returns the description of an error that has been + * registered with eina_error_msg_register(). If an incorrect error is + * given, then @c NULL is returned. + */ +EAPI const char * +eina_error_msg_get(Eina_Error error) +{ + if (error < 1) + return NULL; + + if ((size_t)error > _eina_errors_count) + return NULL; + + return _eina_errors[error - 1].string; +} + +/** + * @brief Return the last set error. + * + * @return The last error. + * + * This function returns the last error set by eina_error_set(). The + * description of the message is returned by eina_error_msg_get(). + */ +EAPI Eina_Error +eina_error_get(void) +{ + return _eina_last_error; +} + +/** + * @brief Set the last error. + * + * @param err The error identifier. + * + * This function sets the last error identifier. The last error can be + * retrieved with eina_error_get(). + */ +EAPI void +eina_error_set(Eina_Error err) +{ + _eina_last_error = err; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_file.c b/tests/suite/ecore/src/lib/eina_file.c new file mode 100644 index 0000000000..a5c95720b6 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_file.c @@ -0,0 +1,544 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Vincent Torri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef _WIN32 +# define _GNU_SOURCE +#endif + +#ifdef HAVE_ALLOCA_H +# include <alloca.h> +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include <malloc.h> +# define alloca _alloca +#else +# include <stddef.h> +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include <string.h> +#include <dirent.h> + +#ifndef _WIN32 +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +#else +# include <Evil.h> +#endif /* _WIN2 */ + +#ifndef _WIN32 +# define PATH_DELIM '/' +#else +# define PATH_DELIM '\\' +# define NAME_MAX MAX_PATH +#endif + +#ifdef __sun +# ifndef NAME_MAX +# define NAME_MAX 255 +# endif +#endif + +#include "eina_config.h" +#include "eina_private.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_file.h" +#include "eina_stringshare.h" + +typedef struct _Eina_File_Iterator Eina_File_Iterator; +struct _Eina_File_Iterator +{ + Eina_Iterator iterator; + + DIR *dirp; + int length; + + char dir[1]; +}; + +static Eina_Bool +_eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data) +{ + struct dirent *dp; + char *name; + size_t length; + + do + { + dp = readdir(it->dirp); + if (!dp) + return EINA_FALSE; + } + while ((dp->d_name[0] == '.') && + ((dp->d_name[1] == '\0') || + ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0')))); + + length = strlen(dp->d_name); + name = alloca(length + 2 + it->length); + + memcpy(name, it->dir, it->length); + memcpy(name + it->length, "/", 1); + memcpy(name + it->length + 1, dp->d_name, length + 1); + + *data = (char *)eina_stringshare_add(name); + return EINA_TRUE; +} + +static char * +_eina_file_ls_iterator_container(Eina_File_Iterator *it) +{ + return it->dir; +} + +static void +_eina_file_ls_iterator_free(Eina_File_Iterator *it) +{ + closedir(it->dirp); + + EINA_MAGIC_SET(&it->iterator, 0); + free(it); +} + +typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator; +struct _Eina_File_Direct_Iterator +{ + Eina_Iterator iterator; + + DIR *dirp; + int length; + + Eina_File_Direct_Info info; + + char dir[1]; +}; + +static Eina_Bool +_eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data) +{ + struct dirent *dp; + size_t length; + + do + { + dp = readdir(it->dirp); + if (!dp) + return EINA_FALSE; + + length = strlen(dp->d_name); + if (it->info.name_start + length + 1 >= PATH_MAX) + continue; + } + while ((dp->d_name[0] == '.') && + ((dp->d_name[1] == '\0') || + ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0')))); + + memcpy(it->info.path + it->info.name_start, dp->d_name, length); + it->info.name_length = length; + it->info.path_length = it->info.name_start + length; + it->info.path[it->info.path_length] = '\0'; + it->info.dirent = dp; + + *data = &it->info; + return EINA_TRUE; +} + +static char * +_eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it) +{ + return it->dir; +} + +static void +_eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it) +{ + closedir(it->dirp); + + EINA_MAGIC_SET(&it->iterator, 0); + free(it); +} + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_File_Group File + * + * @brief Functions to traverse directories and split paths. + * + * @li eina_file_dir_list() list the content of a directory, + * recusrsively or not, and can call a callback function for eachfound + * file. + * @li eina_file_split() split a path into all the subdirectories that + * compose it, according to the separator of the file system. + * + * @{ + */ + +/** + * @brief List all files on the directory calling the function for every file found. + * + * @param dir The directory name. + * @param recursive Iterate recursively in the directory. + * @param cb The callback to be called. + * @param data The data to pass to the callback. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function lists all the files in @p dir. To list also all the + * sub directoris recursively, @p recursive must be set to #EINA_TRUE, + * otherwise it must be set to #EINA_FALSE. For each found file, @p cb + * is called and @p data is passed to it. + * + * If @p cb or @p dir are @c NULL, or if @p dir is a string of size 0, + * or if @p dir can not be opened, this function returns #EINA_FALSE + * immediately. otherwise, it returns #EINA_TRUE. + */ +EAPI Eina_Bool +eina_file_dir_list(const char *dir, + Eina_Bool recursive, + Eina_File_Dir_List_Cb cb, + void *data) +{ +#ifndef _WIN32 + struct dirent *de; + DIR *d; + + EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE); + EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE); + + d = opendir(dir); + if (!d) + return EINA_FALSE; + + while ((de = readdir(d))) + { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + cb(de->d_name, dir, data); + /* d_type is only available on linux and bsd (_BSD_SOURCE) */ + + if (recursive == EINA_TRUE) + { + char *path; + + path = alloca(strlen(dir) + strlen(de->d_name) + 2); + strcpy(path, dir); + strcat(path, "/"); + strcat(path, de->d_name); +#ifndef sun + if (de->d_type == DT_UNKNOWN) + { +#endif + struct stat st; + + if (stat(path, &st)) + continue; + + if (!S_ISDIR(st.st_mode)) + continue; + +#ifndef sun + } + else if (de->d_type != DT_DIR) + continue; + +#endif + + eina_file_dir_list(path, recursive, cb, data); + } + } + + closedir(d); +#else + WIN32_FIND_DATA file; + HANDLE hSearch; + char *new_dir; + TCHAR *tdir; + size_t length_dir; + + EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE); + EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE); + + length_dir = strlen(dir); + new_dir = (char *)alloca(length_dir + 5); + if (!new_dir) + return EINA_FALSE; + + memcpy(new_dir, dir, length_dir); + memcpy(new_dir + length_dir, "/*.*", 5); + +#ifdef UNICODE + tdir = evil_char_to_wchar(new_dir); +#else + tdir = new_dir; +#endif /* ! UNICODE */ + hSearch = FindFirstFile(tdir, &file); +#ifdef UNICODE + free(tdir); +#endif /* UNICODE */ + + if (hSearch == INVALID_HANDLE_VALUE) + return EINA_FALSE; + + do + { + char *filename; + +#ifdef UNICODE + filename = evil_wchar_to_char(file.cFileName); +#else + filename = file.cFileName; +#endif /* ! UNICODE */ + if (!strcmp(filename, ".") || !strcmp(filename, "..")) + continue; + + cb(filename, dir, data); + + if (recursive == EINA_TRUE) + { + char *path; + + path = alloca(strlen(dir) + strlen(filename) + 2); + strcpy(path, dir); + strcat(path, "/"); + strcat(path, filename); + + if (!(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + continue; + + eina_file_dir_list(path, recursive, cb, data); + } + +#ifdef UNICODE + free(filename); +#endif /* UNICODE */ + + } while (FindNextFile(hSearch, &file)); + FindClose(hSearch); +#endif /* _WIN32 */ + + return EINA_TRUE; +} + +/** + * @brief Split a path according to the delimiter of the filesystem. + * + * @param path The path to split. + * @return An array of the parts of the path to split. + * + * This function splits @p path according to the delimiter of the used + * filesystem. If @p path is @c NULL or if the array can not be + * created, @c NULL is returned, otherwise, an array with the + * different parts of @p path is returned. + */ +EAPI Eina_Array * +eina_file_split(char *path) +{ + Eina_Array *ea; + char *current; + size_t length; + + EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL); + + ea = eina_array_new(16); + + if (!ea) + return NULL; + + for (current = strchr(path, PATH_DELIM); + current; + path = current + 1, current = strchr(path, PATH_DELIM)) + { + length = current - path; + + if (length <= 0) + continue; + + eina_array_push(ea, path); + *current = '\0'; + } + + if (*path != '\0') + eina_array_push(ea, path); + + return ea; +} + +/** + * Get an iterator to list the content of a directory. + * + * Iterators are cheap to be created and allow interruption at any + * iteration. At each iteration, only the next directory entry is read + * from the filesystem with readdir(). + * + * The iterator will handle the user a stringshared value with the + * full path. One must call eina_stringshare_del() on it after usage + * to not leak! + * + * The eina_file_direct_ls() function will provide a possibly faster + * alternative if you need to filter the results somehow, like + * checking extension. + * + * The iterator will walk over '.' and '..' without returning them. + * + * @param dir The name of the directory to list + * @return Return an Eina_Iterator that will walk over the files and + * directory in the pointed directory. On failure it will + * return NULL. The iterator emits stringshared value with the + * full path and must be freed with eina_stringshare_del(). + * + * @see eina_file_direct_ls() + */ +EAPI Eina_Iterator * +eina_file_ls(const char *dir) +{ + Eina_File_Iterator *it; + size_t length; + + if (!dir) + return NULL; + + length = strlen(dir); + if (length < 1) + return NULL; + + it = calloc(1, sizeof (Eina_File_Iterator) + length); + if (!it) + return NULL; + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->dirp = opendir(dir); + if (!it->dirp) + { + free(it); + return NULL; + } + + memcpy(it->dir, dir, length + 1); + if (dir[length - 1] != '/') + it->length = length; + else + it->length = length - 1; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _eina_file_ls_iterator_container); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free); + + return &it->iterator; +} + +/** + * Get an iterator to list the content of a directory, with direct information. + * + * Iterators are cheap to be created and allow interruption at any + * iteration. At each iteration, only the next directory entry is read + * from the filesystem with readdir(). + * + * The iterator returns the direct pointer to couple of useful information in + * #Eina_File_Direct_Info and that pointer should not be modified anyhow! + * + * The iterator will walk over '.' and '..' without returning them. + * + * @param dir The name of the directory to list + + * @return Return an Eina_Iterator that will walk over the files and + * directory in the pointed directory. On failure it will + * return NULL. The iterator emits #Eina_File_Direct_Info + * pointers that could be used but not modified. The lifetime + * of the returned pointer is until the next iteration and + * while the iterator is live, deleting the iterator + * invalidates the pointer. + * + * @see eina_file_ls() + */ +EAPI Eina_Iterator * +eina_file_direct_ls(const char *dir) +{ + Eina_File_Direct_Iterator *it; + size_t length; + + if (!dir) + return NULL; + + length = strlen(dir); + if (length < 1) + return NULL; + + if (length + NAME_MAX + 2 >= PATH_MAX) + return NULL; + + it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length); + if (!it) + return NULL; + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->dirp = opendir(dir); + if (!it->dirp) + { + free(it); + return NULL; + } + + memcpy(it->dir, dir, length + 1); + it->length = length; + + memcpy(it->info.path, dir, length); + if (dir[length - 1] == '/') + it->info.name_start = length; + else + { + it->info.path[length] = '/'; + it->info.name_start = length + 1; + } + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_direct_ls_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _eina_file_direct_ls_iterator_container); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free); + + return &it->iterator; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_fp.c b/tests/suite/ecore/src/lib/eina_fp.c new file mode 100644 index 0000000000..73a2df0eb7 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_fp.c @@ -0,0 +1,532 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <math.h> + +#include "eina_types.h" +#include "eina_fp.h" + +#define MAX_PREC 1025 +static const Eina_F32p32 eina_trigo[MAX_PREC] = +{ + 0x0000000100000000, 0x00000000ffffec43, 0x00000000ffffb10b, + 0x00000000ffff4e5a, 0x00000000fffec42e, 0x00000000fffe1287, + 0x00000000fffd3967, 0x00000000fffc38cd, 0x00000000fffb10b9, + 0x00000000fff9c12c, + 0x00000000fff84a25, 0x00000000fff6aba5, 0x00000000fff4e5ac, + 0x00000000fff2f83b, 0x00000000fff0e351, 0x00000000ffeea6ef, + 0x00000000ffec4316, 0x00000000ffe9b7c5, 0x00000000ffe704fe, + 0x00000000ffe42ac0, + 0x00000000ffe1290b, 0x00000000ffddffe2, 0x00000000ffdaaf43, + 0x00000000ffd7372f, 0x00000000ffd397a8, 0x00000000ffcfd0ad, + 0x00000000ffcbe23f, 0x00000000ffc7cc5f, 0x00000000ffc38f0d, + 0x00000000ffbf2a4b, + 0x00000000ffba9e17, 0x00000000ffb5ea75, 0x00000000ffb10f63, + 0x00000000ffac0ce3, 0x00000000ffa6e2f6, 0x00000000ffa1919c, + 0x00000000ff9c18d6, 0x00000000ff9678a6, 0x00000000ff90b10b, + 0x00000000ff8ac208, + 0x00000000ff84ab9c, 0x00000000ff7e6dc8, 0x00000000ff78088f, + 0x00000000ff717bf0, 0x00000000ff6ac7ec, 0x00000000ff63ec85, + 0x00000000ff5ce9bc, 0x00000000ff55bf92, 0x00000000ff4e6e08, + 0x00000000ff46f51f, + 0x00000000ff3f54d8, 0x00000000ff378d34, 0x00000000ff2f9e35, + 0x00000000ff2787dc, 0x00000000ff1f4a2a, 0x00000000ff16e520, + 0x00000000ff0e58c0, 0x00000000ff05a50a, 0x00000000fefcca01, + 0x00000000fef3c7a6, + 0x00000000feea9df9, 0x00000000fee14cfe, 0x00000000fed7d4b3, + 0x00000000fece351d, 0x00000000fec46e3b, 0x00000000feba800f, + 0x00000000feb06a9c, 0x00000000fea62de1, 0x00000000fe9bc9e2, + 0x00000000fe913e9f, + 0x00000000fe868c1b, 0x00000000fe7bb256, 0x00000000fe70b153, + 0x00000000fe658913, 0x00000000fe5a3998, 0x00000000fe4ec2e4, + 0x00000000fe4324f9, 0x00000000fe375fd7, 0x00000000fe2b7382, + 0x00000000fe1f5ffa, + 0x00000000fe132543, 0x00000000fe06c35d, 0x00000000fdfa3a4b, + 0x00000000fded8a0e, 0x00000000fde0b2a8, 0x00000000fdd3b41c, + 0x00000000fdc68e6c, 0x00000000fdb94199, 0x00000000fdabcda5, + 0x00000000fd9e3294, + 0x00000000fd907065, 0x00000000fd82871d, 0x00000000fd7476bd, + 0x00000000fd663f46, 0x00000000fd57e0bd, 0x00000000fd495b21, + 0x00000000fd3aae77, 0x00000000fd2bdabf, 0x00000000fd1cdffd, + 0x00000000fd0dbe32, + 0x00000000fcfe7562, 0x00000000fcef058e, 0x00000000fcdf6eb8, + 0x00000000fccfb0e4, 0x00000000fcbfcc13, 0x00000000fcafc048, + 0x00000000fc9f8d86, 0x00000000fc8f33ce, 0x00000000fc7eb325, + 0x00000000fc6e0b8b, + 0x00000000fc5d3d03, 0x00000000fc4c4791, 0x00000000fc3b2b37, + 0x00000000fc29e7f7, 0x00000000fc187dd5, 0x00000000fc06ecd2, + 0x00000000fbf534f2, 0x00000000fbe35637, 0x00000000fbd150a3, + 0x00000000fbbf243b, + 0x00000000fbacd100, 0x00000000fb9a56f6, 0x00000000fb87b61f, + 0x00000000fb74ee7e, 0x00000000fb620016, 0x00000000fb4eeaea, + 0x00000000fb3baefd, 0x00000000fb284c52, 0x00000000fb14c2eb, + 0x00000000fb0112cd, + 0x00000000faed3bf9, 0x00000000fad93e73, 0x00000000fac51a3f, + 0x00000000fab0cf5e, 0x00000000fa9c5dd5, 0x00000000fa87c5a6, + 0x00000000fa7306d5, 0x00000000fa5e2164, 0x00000000fa491558, + 0x00000000fa33e2b3, + 0x00000000fa1e8978, 0x00000000fa0909ab, 0x00000000f9f36350, + 0x00000000f9dd9668, 0x00000000f9c7a2f9, 0x00000000f9b18905, + 0x00000000f99b488f, 0x00000000f984e19c, 0x00000000f96e542e, + 0x00000000f957a049, + 0x00000000f940c5f1, 0x00000000f929c528, 0x00000000f9129df3, + 0x00000000f8fb5056, 0x00000000f8e3dc53, 0x00000000f8cc41ee, + 0x00000000f8b4812b, 0x00000000f89c9a0e, 0x00000000f8848c9b, + 0x00000000f86c58d4, + 0x00000000f853febe, 0x00000000f83b7e5d, 0x00000000f822d7b4, + 0x00000000f80a0ac7, 0x00000000f7f1179a, 0x00000000f7d7fe31, + 0x00000000f7bebe90, 0x00000000f7a558ba, 0x00000000f78bccb3, + 0x00000000f7721a80, + 0x00000000f7584225, 0x00000000f73e43a5, 0x00000000f7241f04, + 0x00000000f709d446, 0x00000000f6ef6370, 0x00000000f6d4cc85, + 0x00000000f6ba0f8a, 0x00000000f69f2c83, 0x00000000f6842374, + 0x00000000f668f461, + 0x00000000f64d9f4e, 0x00000000f632243f, 0x00000000f616833a, + 0x00000000f5fabc41, 0x00000000f5decf59, 0x00000000f5c2bc87, + 0x00000000f5a683cf, 0x00000000f58a2535, 0x00000000f56da0be, + 0x00000000f550f66e, + 0x00000000f5342649, 0x00000000f5173054, 0x00000000f4fa1494, + 0x00000000f4dcd30c, 0x00000000f4bf6bc2, 0x00000000f4a1deb9, + 0x00000000f4842bf7, 0x00000000f4665380, 0x00000000f4485559, + 0x00000000f42a3186, + 0x00000000f40be80c, 0x00000000f3ed78ef, 0x00000000f3cee434, + 0x00000000f3b029e1, 0x00000000f39149f9, 0x00000000f3724482, + 0x00000000f3531980, 0x00000000f333c8f8, 0x00000000f31452ef, + 0x00000000f2f4b76a, + 0x00000000f2d4f66d, 0x00000000f2b50ffe, 0x00000000f2950421, + 0x00000000f274d2dc, 0x00000000f2547c33, 0x00000000f234002b, + 0x00000000f2135eca, 0x00000000f1f29814, 0x00000000f1d1ac0e, + 0x00000000f1b09abe, + 0x00000000f18f6429, 0x00000000f16e0853, 0x00000000f14c8742, + 0x00000000f12ae0fb, 0x00000000f1091583, 0x00000000f0e724e0, + 0x00000000f0c50f17, 0x00000000f0a2d42c, 0x00000000f0807426, + 0x00000000f05def09, + 0x00000000f03b44db, 0x00000000f01875a1, 0x00000000eff58161, + 0x00000000efd2681f, 0x00000000efaf29e2, 0x00000000ef8bc6af, + 0x00000000ef683e8b, 0x00000000ef44917b, 0x00000000ef20bf86, + 0x00000000eefcc8b1, + 0x00000000eed8ad01, 0x00000000eeb46c7b, 0x00000000ee900727, + 0x00000000ee6b7d08, 0x00000000ee46ce25, 0x00000000ee21fa83, + 0x00000000edfd0228, 0x00000000edd7e51a, 0x00000000edb2a35f, + 0x00000000ed8d3cfc, + 0x00000000ed67b1f6, 0x00000000ed420255, 0x00000000ed1c2e1d, + 0x00000000ecf63554, 0x00000000ecd01801, 0x00000000eca9d628, + 0x00000000ec836fd1, 0x00000000ec5ce501, 0x00000000ec3635bd, + 0x00000000ec0f620d, + 0x00000000ebe869f5, 0x00000000ebc14d7d, 0x00000000eb9a0ca9, + 0x00000000eb72a780, 0x00000000eb4b1e08, 0x00000000eb237047, + 0x00000000eafb9e43, 0x00000000ead3a803, 0x00000000eaab8d8d, + 0x00000000ea834ee6, + 0x00000000ea5aec15, 0x00000000ea326520, 0x00000000ea09ba0d, + 0x00000000e9e0eae4, 0x00000000e9b7f7a9, 0x00000000e98ee063, + 0x00000000e965a51a, 0x00000000e93c45d2, 0x00000000e912c292, + 0x00000000e8e91b61, + 0x00000000e8bf5046, 0x00000000e8956146, 0x00000000e86b4e68, + 0x00000000e84117b3, 0x00000000e816bd2d, 0x00000000e7ec3edc, + 0x00000000e7c19cc8, 0x00000000e796d6f6, 0x00000000e76bed6e, + 0x00000000e740e036, + 0x00000000e715af54, 0x00000000e6ea5ad0, 0x00000000e6bee2af, + 0x00000000e69346f9, 0x00000000e66787b5, 0x00000000e63ba4e9, + 0x00000000e60f9e9b, 0x00000000e5e374d4, 0x00000000e5b72798, + 0x00000000e58ab6f1, + 0x00000000e55e22e3, 0x00000000e5316b76, 0x00000000e50490b1, + 0x00000000e4d7929c, 0x00000000e4aa713c, 0x00000000e47d2c98, + 0x00000000e44fc4b9, 0x00000000e42239a4, 0x00000000e3f48b61, + 0x00000000e3c6b9f7, + 0x00000000e398c56c, 0x00000000e36aadc9, 0x00000000e33c7314, + 0x00000000e30e1554, 0x00000000e2df9490, 0x00000000e2b0f0d0, + 0x00000000e2822a1a, 0x00000000e2534077, 0x00000000e22433ec, + 0x00000000e1f50482, + 0x00000000e1c5b240, 0x00000000e1963d2d, 0x00000000e166a550, + 0x00000000e136eab0, 0x00000000e1070d56, 0x00000000e0d70d48, + 0x00000000e0a6ea8e, 0x00000000e076a52f, 0x00000000e0463d33, + 0x00000000e015b2a1, + 0x00000000dfe50580, 0x00000000dfb435d9, 0x00000000df8343b2, + 0x00000000df522f13, 0x00000000df20f804, 0x00000000deef9e8d, + 0x00000000debe22b5, 0x00000000de8c8483, 0x00000000de5ac3ff, + 0x00000000de28e131, + 0x00000000ddf6dc21, 0x00000000ddc4b4d6, 0x00000000dd926b59, + 0x00000000dd5fffb0, 0x00000000dd2d71e3, 0x00000000dcfac1fb, + 0x00000000dcc7f000, 0x00000000dc94fbf8, 0x00000000dc61e5ec, + 0x00000000dc2eade4, + 0x00000000dbfb53e8, 0x00000000dbc7d7ff, 0x00000000db943a31, + 0x00000000db607a88, 0x00000000db2c9909, 0x00000000daf895bf, + 0x00000000dac470af, 0x00000000da9029e3, 0x00000000da5bc163, + 0x00000000da273737, + 0x00000000d9f28b66, 0x00000000d9bdbdf9, 0x00000000d988cef8, + 0x00000000d953be6b, 0x00000000d91e8c5b, 0x00000000d8e938d0, + 0x00000000d8b3c3d1, 0x00000000d87e2d67, 0x00000000d848759b, + 0x00000000d8129c74, + 0x00000000d7dca1fb, 0x00000000d7a68638, 0x00000000d7704934, + 0x00000000d739eaf7, 0x00000000d7036b89, 0x00000000d6cccaf3, + 0x00000000d696093d, 0x00000000d65f266f, 0x00000000d6282293, + 0x00000000d5f0fdb0, + 0x00000000d5b9b7d0, 0x00000000d58250fa, 0x00000000d54ac937, + 0x00000000d513208f, 0x00000000d4db570c, 0x00000000d4a36cb6, + 0x00000000d46b6195, 0x00000000d43335b3, 0x00000000d3fae917, + 0x00000000d3c27bcb, + 0x00000000d389edd7, 0x00000000d3513f43, 0x00000000d318701a, + 0x00000000d2df8063, 0x00000000d2a67027, 0x00000000d26d3f6f, + 0x00000000d233ee43, 0x00000000d1fa7cae, 0x00000000d1c0eab7, + 0x00000000d1873867, + 0x00000000d14d65c8, 0x00000000d11372e1, 0x00000000d0d95fbd, + 0x00000000d09f2c64, 0x00000000d064d8df, 0x00000000d02a6537, + 0x00000000cfefd176, 0x00000000cfb51da3, 0x00000000cf7a49c8, + 0x00000000cf3f55ef, + 0x00000000cf044220, 0x00000000cec90e64, 0x00000000ce8dbac5, + 0x00000000ce52474c, 0x00000000ce16b401, 0x00000000cddb00ef, + 0x00000000cd9f2e1e, 0x00000000cd633b97, 0x00000000cd272964, + 0x00000000cceaf78e, + 0x00000000ccaea61e, 0x00000000cc72351e, 0x00000000cc35a497, + 0x00000000cbf8f492, 0x00000000cbbc2519, 0x00000000cb7f3634, + 0x00000000cb4227ee, 0x00000000cb04fa50, 0x00000000cac7ad63, + 0x00000000ca8a4131, + 0x00000000ca4cb5c3, 0x00000000ca0f0b22, 0x00000000c9d14159, + 0x00000000c9935870, 0x00000000c9555072, 0x00000000c9172967, + 0x00000000c8d8e35a, 0x00000000c89a7e53, 0x00000000c85bfa5e, + 0x00000000c81d5782, + 0x00000000c7de95cb, 0x00000000c79fb541, 0x00000000c760b5ee, + 0x00000000c72197dc, 0x00000000c6e25b15, 0x00000000c6a2ffa3, + 0x00000000c663858f, 0x00000000c623ece2, 0x00000000c5e435a8, + 0x00000000c5a45fe9, + 0x00000000c5646bb0, 0x00000000c5245906, 0x00000000c4e427f6, + 0x00000000c4a3d888, 0x00000000c4636ac8, 0x00000000c422debf, + 0x00000000c3e23476, 0x00000000c3a16bf9, 0x00000000c3608550, + 0x00000000c31f8087, + 0x00000000c2de5da6, 0x00000000c29d1cb8, 0x00000000c25bbdc8, + 0x00000000c21a40de, 0x00000000c1d8a606, 0x00000000c196ed49, + 0x00000000c15516b2, 0x00000000c113224a, 0x00000000c0d1101d, + 0x00000000c08ee033, + 0x00000000c04c9297, 0x00000000c00a2754, 0x00000000bfc79e73, + 0x00000000bf84f800, 0x00000000bf423404, 0x00000000beff5289, + 0x00000000bebc539a, 0x00000000be793741, 0x00000000be35fd89, + 0x00000000bdf2a67b, + 0x00000000bdaf3223, 0x00000000bd6ba08b, 0x00000000bd27f1bc, + 0x00000000bce425c2, 0x00000000bca03ca7, 0x00000000bc5c3676, + 0x00000000bc181338, 0x00000000bbd3d2f9, 0x00000000bb8f75c3, + 0x00000000bb4afba1, + 0x00000000bb06649c, 0x00000000bac1b0c0, 0x00000000ba7ce018, + 0x00000000ba37f2ad, 0x00000000b9f2e88b, 0x00000000b9adc1bc, + 0x00000000b9687e4a, 0x00000000b9231e41, 0x00000000b8dda1ac, + 0x00000000b8980894, + 0x00000000b8525305, 0x00000000b80c8109, 0x00000000b7c692ac, + 0x00000000b78087f7, 0x00000000b73a60f6, 0x00000000b6f41db4, + 0x00000000b6adbe3a, 0x00000000b6674296, 0x00000000b620aad0, + 0x00000000b5d9f6f4, + 0x00000000b593270e, 0x00000000b54c3b27, 0x00000000b505334a, + 0x00000000b4be0f84, 0x00000000b476cfde, 0x00000000b42f7464, + 0x00000000b3e7fd20, 0x00000000b3a06a1e, 0x00000000b358bb69, + 0x00000000b310f10c, + 0x00000000b2c90b11, 0x00000000b2810985, 0x00000000b238ec71, + 0x00000000b1f0b3e2, 0x00000000b1a85fe2, 0x00000000b15ff07c, + 0x00000000b11765bc, 0x00000000b0cebfad, 0x00000000b085fe5a, + 0x00000000b03d21ce, + 0x00000000aff42a15, 0x00000000afab1739, 0x00000000af61e946, + 0x00000000af18a048, 0x00000000aecf3c49, 0x00000000ae85bd55, + 0x00000000ae3c2377, 0x00000000adf26ebb, 0x00000000ada89f2c, + 0x00000000ad5eb4d5, + 0x00000000ad14afc2, 0x00000000acca8ffd, 0x00000000ac805594, + 0x00000000ac360090, 0x00000000abeb90fe, 0x00000000aba106e9, + 0x00000000ab56625d, 0x00000000ab0ba364, 0x00000000aac0ca0b, + 0x00000000aa75d65d, + 0x00000000aa2ac865, 0x00000000a9dfa030, 0x00000000a9945dc9, + 0x00000000a949013a, 0x00000000a8fd8a91, 0x00000000a8b1f9d8, + 0x00000000a8664f1c, 0x00000000a81a8a68, 0x00000000a7ceabc7, + 0x00000000a782b345, + 0x00000000a736a0ef, 0x00000000a6ea74cf, 0x00000000a69e2ef2, + 0x00000000a651cf63, 0x00000000a605562f, 0x00000000a5b8c360, + 0x00000000a56c1702, 0x00000000a51f5123, 0x00000000a4d271cc, + 0x00000000a485790b, + 0x00000000a43866eb, 0x00000000a3eb3b77, 0x00000000a39df6bd, + 0x00000000a35098c7, 0x00000000a30321a2, 0x00000000a2b5915a, + 0x00000000a267e7fa, 0x00000000a21a258e, 0x00000000a1cc4a24, + 0x00000000a17e55c5, + 0x00000000a1304880, 0x00000000a0e2225f, 0x00000000a093e36f, + 0x00000000a0458bbb, 0x000000009ff71b50, 0x000000009fa8923a, + 0x000000009f59f086, 0x000000009f0b363e, 0x000000009ebc6370, + 0x000000009e6d7827, + 0x000000009e1e746f, 0x000000009dcf5856, 0x000000009d8023e6, + 0x000000009d30d72d, 0x000000009ce17236, 0x000000009c91f50e, + 0x000000009c425fc1, 0x000000009bf2b25b, 0x000000009ba2ece8, + 0x000000009b530f76, + 0x000000009b031a0f, 0x000000009ab30cc1, 0x000000009a62e797, + 0x000000009a12aa9f, 0x0000000099c255e5, 0x000000009971e974, + 0x000000009921655a, 0x0000000098d0c9a2, 0x0000000098801659, + 0x00000000982f4b8d, + 0x0000000097de6948, 0x00000000978d6f97, 0x00000000973c5e88, + 0x0000000096eb3626, 0x000000009699f67f, 0x0000000096489f9e, + 0x0000000095f73190, 0x0000000095a5ac61, 0x000000009554101f, + 0x0000000095025cd6, + 0x0000000094b09292, 0x00000000945eb161, 0x00000000940cb94e, + 0x0000000093baaa66, 0x00000000936884b6, 0x000000009316484b, + 0x0000000092c3f531, 0x0000000092718b75, 0x00000000921f0b24, + 0x0000000091cc744b, + 0x000000009179c6f5, 0x0000000091270331, 0x0000000090d4290a, + 0x000000009081388e, 0x00000000902e31c8, 0x000000008fdb14c7, + 0x000000008f87e197, 0x000000008f349845, 0x000000008ee138dd, + 0x000000008e8dc36c, + 0x000000008e3a3800, 0x000000008de696a5, 0x000000008d92df68, + 0x000000008d3f1256, 0x000000008ceb2f7c, 0x000000008c9736e7, + 0x000000008c4328a3, 0x000000008bef04bf, 0x000000008b9acb46, + 0x000000008b467c45, + 0x000000008af217cb, 0x000000008a9d9de3, 0x000000008a490e9b, + 0x0000000089f469ff, 0x00000000899fb01e, 0x00000000894ae103, + 0x0000000088f5fcbc, 0x0000000088a10357, 0x00000000884bf4df, + 0x0000000087f6d163, + 0x0000000087a198f0, 0x00000000874c4b92, 0x0000000086f6e956, + 0x0000000086a1724b, 0x00000000864be67c, 0x0000000085f645f8, + 0x0000000085a090cc, 0x00000000854ac704, 0x0000000084f4e8ad, + 0x00000000849ef5d7, + 0x000000008448ee8c, 0x0000000083f2d2db, 0x00000000839ca2d1, + 0x0000000083465e7c, 0x0000000082f005e8, 0x0000000082999922, + 0x0000000082431839, 0x0000000081ec833a, 0x000000008195da31, + 0x00000000813f1d2d, + 0x0000000080e84c3a, 0x0000000080916766, 0x00000000803a6ebf, + 0x000000007fe36251, 0x000000007f8c422b, 0x000000007f350e59, + 0x000000007eddc6ea, 0x000000007e866bea, 0x000000007e2efd67, + 0x000000007dd77b6f, + 0x000000007d7fe60f, 0x000000007d283d54, 0x000000007cd0814c, + 0x000000007c78b205, 0x000000007c20cf8c, 0x000000007bc8d9ef, + 0x000000007b70d13b, 0x000000007b18b57e, 0x000000007ac086c5, + 0x000000007a68451f, + 0x000000007a0ff098, 0x0000000079b7893e, 0x00000000795f0f1f, + 0x0000000079068248, 0x0000000078ade2c8, 0x00000000785530ab, + 0x0000000077fc6c01, 0x0000000077a394d5, 0x00000000774aab36, + 0x0000000076f1af32, + 0x000000007698a0d6, 0x00000000763f8030, 0x0000000075e64d4e, + 0x00000000758d083e, 0x000000007533b10d, 0x0000000074da47c9, + 0x000000007480cc80, 0x0000000074273f3f, 0x0000000073cda016, + 0x000000007373ef10, + 0x00000000731a2c3d, 0x0000000072c057aa, 0x0000000072667164, + 0x00000000720c797a, 0x0000000071b26ffa, 0x00000000715854f2, + 0x0000000070fe286e, 0x0000000070a3ea7e, 0x0000000070499b30, + 0x000000006fef3a90, + 0x000000006f94c8ae, 0x000000006f3a4596, 0x000000006edfb157, + 0x000000006e850c00, 0x000000006e2a559d, 0x000000006dcf8e3d, + 0x000000006d74b5ee, 0x000000006d19ccbe, 0x000000006cbed2bb, + 0x000000006c63c7f3, + 0x000000006c08ac74, 0x000000006bad804c, 0x000000006b524389, + 0x000000006af6f639, 0x000000006a9b986b, 0x000000006a402a2c, + 0x0000000069e4ab8a, 0x0000000069891c94, 0x00000000692d7d57, + 0x0000000068d1cde3, + 0x0000000068760e44, 0x00000000681a3e89, 0x0000000067be5ec1, + 0x0000000067626ef9, 0x0000000067066f40, 0x0000000066aa5fa3, + 0x00000000664e4032, 0x0000000065f210f9, 0x000000006595d209, + 0x000000006539836d, + 0x0000000064dd2536, 0x000000006480b770, 0x0000000064243a2b, + 0x0000000063c7ad75, 0x00000000636b115c, 0x00000000630e65ed, + 0x0000000062b1ab39, 0x000000006254e14c, 0x0000000061f80835, + 0x00000000619b2002, + 0x00000000613e28c2, 0x0000000060e12283, 0x0000000060840d54, + 0x000000006026e943, 0x000000005fc9b65d, 0x000000005f6c74b2, + 0x000000005f0f2450, 0x000000005eb1c545, 0x000000005e5457a0, + 0x000000005df6db6f, + 0x000000005d9950c0, 0x000000005d3bb7a3, 0x000000005cde1024, + 0x000000005c805a54, 0x000000005c22963f, 0x000000005bc4c3f6, + 0x000000005b66e385, 0x000000005b08f4fd, 0x000000005aaaf86a, + 0x000000005a4ceddc, + 0x0000000059eed561, 0x000000005990af08, 0x0000000059327adf, + 0x0000000058d438f4, 0x000000005875e957, 0x0000000058178c16, + 0x0000000057b9213f, 0x00000000575aa8e0, 0x0000000056fc230a, + 0x00000000569d8fc9, + 0x00000000563eef2d, 0x0000000055e04144, 0x000000005581861d, + 0x000000005522bdc6, 0x0000000054c3e84e, 0x00000000546505c4, + 0x0000000054061636, 0x0000000053a719b3, 0x000000005348104a, + 0x0000000052e8fa09, + 0x000000005289d6ff, 0x00000000522aa73a, 0x0000000051cb6aca, + 0x00000000516c21bc, 0x00000000510ccc20, 0x0000000050ad6a05, + 0x00000000504dfb78, 0x000000004fee808a, 0x000000004f8ef947, + 0x000000004f2f65c0, + 0x000000004ecfc603, 0x000000004e701a1f, 0x000000004e106222, + 0x000000004db09e1b, 0x000000004d50ce19, 0x000000004cf0f22b, + 0x000000004c910a5f, 0x000000004c3116c5, 0x000000004bd1176b, + 0x000000004b710c5f, + 0x000000004b10f5b2, 0x000000004ab0d371, 0x000000004a50a5ab, + 0x0000000049f06c70, 0x00000000499027cd, 0x00000000492fd7d3, + 0x0000000048cf7c8f, 0x00000000486f1611, 0x00000000480ea467, + 0x0000000047ae27a1, + 0x00000000474d9fcd, 0x0000000046ed0cfa, 0x00000000468c6f37, + 0x00000000462bc693, 0x0000000045cb131c, 0x00000000456a54e3, + 0x0000000045098bf5, 0x0000000044a8b861, 0x000000004447da37, + 0x0000000043e6f186, + 0x000000004385fe5c, 0x00000000432500c8, 0x0000000042c3f8d9, + 0x000000004262e69f, 0x000000004201ca28, 0x0000000041a0a383, + 0x00000000413f72bf, 0x0000000040de37eb, 0x00000000407cf317, + 0x00000000401ba450, + 0x000000003fba4ba7, 0x000000003f58e92a, 0x000000003ef77ce8, + 0x000000003e9606f1, 0x000000003e348752, 0x000000003dd2fe1c, + 0x000000003d716b5e, 0x000000003d0fcf25, 0x000000003cae2982, + 0x000000003c4c7a83, + 0x000000003beac238, 0x000000003b8900b0, 0x000000003b2735f9, + 0x000000003ac56223, 0x000000003a63853d, 0x000000003a019f56, + 0x00000000399fb07d, 0x00000000393db8c1, 0x0000000038dbb831, + 0x000000003879aedd, + 0x0000000038179cd3, 0x0000000037b58222, 0x0000000037535edb, + 0x0000000036f1330b, 0x00000000368efec2, 0x00000000362cc20f, + 0x0000000035ca7d02, 0x0000000035682fa9, 0x000000003505da14, + 0x0000000034a37c51, + 0x0000000034411671, 0x0000000033dea881, 0x00000000337c3292, + 0x000000003319b4b3, 0x0000000032b72ef2, 0x000000003254a15e, + 0x0000000031f20c08, 0x00000000318f6efe, 0x00000000312cca50, + 0x0000000030ca1e0c, + 0x0000000030676a43, 0x000000003004af02, 0x000000002fa1ec5a, + 0x000000002f3f2259, 0x000000002edc510f, 0x000000002e79788b, + 0x000000002e1698dc, 0x000000002db3b212, 0x000000002d50c43c, + 0x000000002cedcf68, + 0x000000002c8ad3a7, 0x000000002c27d108, 0x000000002bc4c799, + 0x000000002b61b76b, 0x000000002afea08c, 0x000000002a9b830b, + 0x000000002a385ef9, 0x0000000029d53464, 0x000000002972035b, + 0x00000000290ecbee, + 0x0000000028ab8e2c, 0x0000000028484a25, 0x0000000027e4ffe7, + 0x000000002781af83, 0x00000000271e5906, 0x0000000026bafc82, + 0x0000000026579a04, 0x0000000025f4319d, 0x000000002590c35c, + 0x00000000252d4f4f, + 0x0000000024c9d587, 0x0000000024665613, 0x000000002402d101, + 0x00000000239f4662, 0x00000000233bb644, 0x0000000022d820b8, + 0x00000000227485cc, 0x000000002210e590, 0x0000000021ad4013, + 0x0000000021499565, + 0x0000000020e5e594, 0x00000000208230b1, 0x00000000201e76ca, + 0x000000001fbab7ef, 0x000000001f56f430, 0x000000001ef32b9b, + 0x000000001e8f5e41, 0x000000001e2b8c30, 0x000000001dc7b578, + 0x000000001d63da29, + 0x000000001cfffa51, 0x000000001c9c1600, 0x000000001c382d46, + 0x000000001bd44032, 0x000000001b704ed3, 0x000000001b0c5939, + 0x000000001aa85f74, 0x000000001a446191, 0x0000000019e05fa2, + 0x00000000197c59b5, + 0x0000000019184fdb, 0x0000000018b44221, 0x0000000018503098, + 0x0000000017ec1b50, 0x0000000017880257, 0x000000001723e5bd, + 0x0000000016bfc591, 0x00000000165ba1e4, 0x0000000015f77ac3, + 0x0000000015935040, + 0x00000000152f2269, 0x0000000014caf14d, 0x000000001466bcfd, + 0x0000000014028587, 0x00000000139e4afb, 0x00000000133a0d69, + 0x0000000012d5cce0, 0x000000001271896f, 0x00000000120d4326, + 0x0000000011a8fa15, + 0x000000001144ae4a, 0x0000000010e05fd6, 0x00000000107c0ec7, + 0x000000001017bb2d, 0x000000000fb36519, 0x000000000f4f0c98, + 0x000000000eeab1bb, 0x000000000e865491, 0x000000000e21f52a, + 0x000000000dbd9395, + 0x000000000d592fe1, 0x000000000cf4ca1f, 0x000000000c90625c, + 0x000000000c2bf8aa, 0x000000000bc78d18, 0x000000000b631fb4, + 0x000000000afeb08f, 0x000000000a9a3fb8, 0x000000000a35cd3e, + 0x0000000009d15931, + 0x00000000096ce3a1, 0x0000000009086c9c, 0x0000000008a3f433, + 0x00000000083f7a75, 0x0000000007daff71, 0x0000000007768337, + 0x00000000071205d6, 0x0000000006ad875f, 0x00000000064907df, + 0x0000000005e48768, + 0x0000000005800608, 0x00000000051b83cf, 0x0000000004b700cc, + 0x0000000004527d0f, 0x0000000003edf8a7, 0x00000000038973a4, + 0x000000000324ee16, 0x0000000002c0680b, 0x00000000025be194, + 0x0000000001f75ac0, + 0x000000000192d39e, 0x00000000012e4c3e, 0x0000000000c9c4af, + 0x0000000000653d02, 0x0000000000000000 +}; + +EAPI Eina_F32p32 +eina_f32p32_cos(Eina_F32p32 a) +{ + Eina_F32p32 F32P32_2PI; + Eina_F32p32 F32P32_PI2; + Eina_F32p32 F32P32_3PI2; + Eina_F32p32 remainder_2PI; + Eina_F32p32 remainder_PI; + Eina_F32p32 interpol; + Eina_F32p32 result; + int idx; + int index2; + + F32P32_2PI = EINA_F32P32_PI << 1; + F32P32_PI2 = EINA_F32P32_PI >> 1; + F32P32_3PI2 = EINA_F32P32_PI + F32P32_PI2; + + /* Take advantage of cosinus symetrie. */ + a = eina_fp32p32_llabs(a); + + /* Find table entry in 0 to PI / 2 */ + remainder_PI = a - (a / EINA_F32P32_PI) * EINA_F32P32_PI; + + /* Find which case from 0 to 2 * PI */ + remainder_2PI = a - (a / F32P32_2PI) * F32P32_2PI; + + interpol = eina_f32p32_div(eina_f32p32_scale(remainder_PI, MAX_PREC * 2), + EINA_F32P32_PI); + idx = eina_f32p32_int_to(interpol); + if (idx >= MAX_PREC) + idx = 2 * MAX_PREC - (idx - 1); + + index2 = idx + 1; + if (index2 == MAX_PREC) + index2 = idx - 1; + + result = eina_f32p32_add(eina_trigo[idx], + eina_f32p32_mul(eina_f32p32_sub(eina_trigo[idx], + eina_trigo[index2]), + (Eina_F32p32)eina_f32p32_fracc_get( + interpol))); + + if (0 <= remainder_2PI && remainder_2PI < F32P32_PI2) + return result; + else if (F32P32_PI2 <= remainder_2PI && remainder_2PI < EINA_F32P32_PI) + return -result; + else if (EINA_F32P32_PI <= remainder_2PI && remainder_2PI < F32P32_3PI2) + return -result; + else /* if (F32P32_3PI2 <= remainder_2PI) */ + return result; +} + +EAPI Eina_F32p32 +eina_f32p32_sin(Eina_F32p32 a) +{ + Eina_F32p32 F32P32_2PI; + Eina_F32p32 F32P32_PI2; + Eina_F32p32 F32P32_3PI2; + Eina_F32p32 remainder_2PI; + Eina_F32p32 remainder_PI; + Eina_F32p32 interpol; + Eina_F32p32 result; + int idx; + int index2; + + F32P32_2PI = EINA_F32P32_PI << 1; + F32P32_PI2 = EINA_F32P32_PI >> 1; + F32P32_3PI2 = EINA_F32P32_PI + F32P32_PI2; + + /* We only have a table for cosinus, but sin(a) = cos(pi / 2 - a) */ + a = eina_f32p32_sub(F32P32_PI2, a); + + /* Take advantage of cosinus symetrie. */ + a = eina_fp32p32_llabs(a); + + /* Find table entry in 0 to PI / 2 */ + remainder_PI = a - (a / EINA_F32P32_PI) * EINA_F32P32_PI; + + /* Find which case from 0 to 2 * PI */ + remainder_2PI = a - (a / F32P32_2PI) * F32P32_2PI; + + interpol = eina_f32p32_div(eina_f32p32_scale(remainder_PI, MAX_PREC * 2), + EINA_F32P32_PI); + idx = eina_f32p32_int_to(interpol); + if (idx >= MAX_PREC) + idx = 2 * MAX_PREC - (idx + 1); + + index2 = idx + 1; + if (index2 == MAX_PREC) + index2 = idx - 1; + + result = eina_f32p32_add(eina_trigo[idx], + eina_f32p32_mul(eina_f32p32_sub(eina_trigo[idx], + eina_trigo[index2]), + (Eina_F32p32)eina_f32p32_fracc_get( + interpol))); + + if (0 <= remainder_2PI && remainder_2PI < F32P32_PI2) + return result; + else if (F32P32_PI2 <= remainder_2PI && remainder_2PI < EINA_F32P32_PI) + return -result; + else if (EINA_F32P32_PI <= remainder_2PI && remainder_2PI < F32P32_3PI2) + return -result; + else /* if (F32P32_3PI2 <= remainder_2PI) */ + return result; +} + diff --git a/tests/suite/ecore/src/lib/eina_hamster.c b/tests/suite/ecore/src/lib/eina_hamster.c new file mode 100644 index 0000000000..2f68777cc6 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_hamster.c @@ -0,0 +1,128 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> + +#include "eina_config.h" +#include "eina_types.h" +#include "eina_hamster.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +const char *_eina_hamster_time = __TIME__; +const char *_eina_hamster_date = __DATE__; +static int _eina_hamsters = -1; + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Hamster_Group Hamster + * + * @brief These functions provide hamster calls. + * + * @{ + */ + +/** + * @brief Get the hamster count. + * + * @return The number of available hamsters. + * + * This function returns how many hamsters you have. + */ +EAPI int +eina_hamster_count(void) +{ + if (_eina_hamsters < 0) + { + int hrs = 0, min = 0, sec = 0; + char mon[8] = ""; + int monnum = 0, day = 0, year = 0; + int fields; + + fields = sscanf(_eina_hamster_time, "%02i:%02i:%02i", &hrs, &min, &sec); + if (fields == 3) + { + _eina_hamsters = (hrs * 60) + min; + fields = sscanf(_eina_hamster_date, "%s %i %i", mon, &day, &year); + if (fields == 3) + { + int i; + const char *mons[] = + { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + }; + + for (i = 0; i < 12; i++) + { + if (!strcmp(mon, mons[i])) + { + monnum = i + 1; + break; + } + } + // alloc 60 for mins, 24 for hrs + // alloc 1-31 (32) for days, 1-12 (13) for months + // use year as-is, for 31 bits (signed) this gives us up to + // 3584 years, which is good enough imho. - 1500 years from + // now or so. :) + _eina_hamsters += + (day + (monnum * 32) + (13 * 32 * year)) * (24 * 60); + } + } + } + + // format: [rest - year][0-12 - month][0-31 - day][0-23 - hrs][0-59 - sec] + return _eina_hamsters; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_hash.c b/tests/suite/ecore/src/lib/eina_hash.c new file mode 100644 index 0000000000..4c5f533b55 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_hash.c @@ -0,0 +1,1933 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Gustavo Sverzut Barbieri, + * Vincent Torri, Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _MSC_VER +# include <Evil.h> +#else +# include <stdint.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_rbtree.h" +#include "eina_error.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_hash.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +#define EINA_MAGIC_CHECK_HASH(d) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_HASH)) { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_HASH); } \ + } while(0) + +#define EINA_MAGIC_CHECK_HASH_ITERATOR(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_HASH_ITERATOR)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_HASH_ITERATOR); \ + return __VA_ARGS__; \ + } \ + } while(0) + +#define EINA_HASH_BUCKET_SIZE 8 +#define EINA_HASH_SMALL_BUCKET_SIZE 5 + +#define EINA_HASH_RBTREE_MASK 0xFFF + +typedef struct _Eina_Hash_Head Eina_Hash_Head; +typedef struct _Eina_Hash_Element Eina_Hash_Element; +typedef struct _Eina_Hash_Foreach_Data Eina_Hash_Foreach_Data; +typedef struct _Eina_Iterator_Hash Eina_Iterator_Hash; +typedef struct _Eina_Hash_Each Eina_Hash_Each; + +struct _Eina_Hash +{ + Eina_Key_Length key_length_cb; + Eina_Key_Cmp key_cmp_cb; + Eina_Key_Hash key_hash_cb; + Eina_Free_Cb data_free_cb; + + Eina_Rbtree **buckets; + int size; + int mask; + + int population; + + EINA_MAGIC +}; + +struct _Eina_Hash_Head +{ + EINA_RBTREE; + int hash; + + Eina_Rbtree *head; +}; + +struct _Eina_Hash_Element +{ + EINA_RBTREE; + Eina_Hash_Tuple tuple; + Eina_Bool begin : 1; +}; + +struct _Eina_Hash_Foreach_Data +{ + Eina_Hash_Foreach cb; + const void *fdata; +}; + +typedef void *(*Eina_Iterator_Get_Content_Callback)(Eina_Iterator_Hash *it); +#define FUNC_ITERATOR_GET_CONTENT(Function) ((Eina_Iterator_Get_Content_Callback)Function) + +struct _Eina_Iterator_Hash +{ + Eina_Iterator iterator; + + Eina_Iterator_Get_Content_Callback get_content; + const Eina_Hash *hash; + + Eina_Iterator *current; + Eina_Iterator *list; + Eina_Hash_Head *hash_head; + Eina_Hash_Element *hash_element; + int bucket; + + int index; + + EINA_MAGIC +}; + +struct _Eina_Hash_Each +{ + Eina_Hash_Head *hash_head; + const Eina_Hash_Element *hash_element; + const void *data; +}; + +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +# define get16bits(d) (*((const uint16_t *)(d))) +#endif + +#if !defined (get16bits) +# define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + + (uint32_t)(((const uint8_t *)(d))[0])) +#endif + +static inline int +_eina_hash_hash_rbtree_cmp_hash(const Eina_Hash_Head *hash_head, + const int *hash, + __UNUSED__ int key_length, + __UNUSED__ void *data) +{ + return hash_head->hash - *hash; +} + +static Eina_Rbtree_Direction +_eina_hash_hash_rbtree_cmp_node(const Eina_Hash_Head *left, + const Eina_Hash_Head *right, + __UNUSED__ void *data) +{ + if (left->hash - right->hash < 0) + return EINA_RBTREE_LEFT; + + return EINA_RBTREE_RIGHT; +} + +static inline int +_eina_hash_key_rbtree_cmp_key_data(const Eina_Hash_Element *hash_element, + const Eina_Hash_Tuple *tuple, + __UNUSED__ unsigned int key_length, + Eina_Key_Cmp cmp) +{ + int result; + + result = cmp(hash_element->tuple.key, + hash_element->tuple.key_length, + tuple->key, + tuple->key_length); + + if (result == 0 && tuple->data && tuple->data != hash_element->tuple.data) + return 1; + + return result; +} + +static Eina_Rbtree_Direction +_eina_hash_key_rbtree_cmp_node(const Eina_Hash_Element *left, + const Eina_Hash_Element *right, + Eina_Key_Cmp cmp) +{ + int result; + + result = cmp(left->tuple.key, left->tuple.key_length, + right->tuple.key, right->tuple.key_length); + + if (result < 0) + return EINA_RBTREE_LEFT; + + return EINA_RBTREE_RIGHT; +} + +static inline Eina_Bool +eina_hash_add_alloc_by_hash(Eina_Hash *hash, + const void *key, int key_length, int alloc_length, + int key_hash, + const void *data) +{ + Eina_Hash_Element *new_hash_element = NULL; + Eina_Hash_Head *hash_head; + Eina_Error error = 0; + int hash_num; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE); + EINA_MAGIC_CHECK_HASH(hash); + + error = EINA_ERROR_OUT_OF_MEMORY; + + /* Apply eina mask to hash. */ + hash_num = key_hash & hash->mask; + key_hash &= EINA_HASH_RBTREE_MASK; + + if (!hash->buckets) + { + hash->buckets = calloc(sizeof (Eina_Rbtree *), hash->size); + if (!hash->buckets) goto on_error; + + hash_head = NULL; + } + else + /* Look up for head node. */ + hash_head = (Eina_Hash_Head *)eina_rbtree_inline_lookup(hash->buckets[hash_num], + &key_hash, 0, + EINA_RBTREE_CMP_KEY_CB(_eina_hash_hash_rbtree_cmp_hash), + NULL); + + if (!hash_head) + { + /* If not found allocate it and an element. */ + hash_head = malloc(sizeof(Eina_Hash_Head) + sizeof(Eina_Hash_Element) + alloc_length); + if (!hash_head) + goto on_error; + + hash_head->hash = key_hash; + hash_head->head = NULL; + + hash->buckets[hash_num] = + eina_rbtree_inline_insert(hash->buckets[hash_num], EINA_RBTREE_GET(hash_head), + EINA_RBTREE_CMP_NODE_CB( + _eina_hash_hash_rbtree_cmp_node), NULL); + + new_hash_element = (Eina_Hash_Element *)(hash_head + 1); + new_hash_element->begin = EINA_TRUE; + } + + if (!new_hash_element) + { + /* + Alloc a new element + (No more lookup as we expect to support more than one item for one key). + */ + new_hash_element = malloc(sizeof (Eina_Hash_Element) + alloc_length); + if (!new_hash_element) + goto on_error; + + new_hash_element->begin = EINA_FALSE; + } + + /* Setup the element */ + new_hash_element->tuple.key_length = key_length; + new_hash_element->tuple.data = (void *)data; + if (alloc_length > 0) + { + new_hash_element->tuple.key = (char *)(new_hash_element + 1); + memcpy((char *)new_hash_element->tuple.key, key, alloc_length); + } + else + new_hash_element->tuple.key = key; + + /* add the new element to the hash. */ + hash_head->head = eina_rbtree_inline_insert(hash_head->head, EINA_RBTREE_GET(new_hash_element), + EINA_RBTREE_CMP_NODE_CB( + _eina_hash_key_rbtree_cmp_node), + (const void *)hash->key_cmp_cb); + hash->population++; + return EINA_TRUE; + +on_error: + eina_error_set(error); + return EINA_FALSE; +} + +static Eina_Bool +_eina_hash_rbtree_each(__UNUSED__ const Eina_Rbtree *container, + const Eina_Hash_Head *hash_head, + Eina_Hash_Each *data) +{ + Eina_Iterator *it; + Eina_Hash_Element *hash_element; + Eina_Bool found = EINA_TRUE; + + it = eina_rbtree_iterator_prefix(hash_head->head); + EINA_ITERATOR_FOREACH(it, hash_element) + { + if (hash_element->tuple.data == data->data) + { + data->hash_element = hash_element; + data->hash_head = (Eina_Hash_Head *)hash_head; + found = EINA_FALSE; + break; + } + } + + eina_iterator_free(it); + return found; +} + +static inline Eina_Hash_Element * +_eina_hash_find_by_hash(const Eina_Hash *hash, + Eina_Hash_Tuple *tuple, + int key_hash, + Eina_Hash_Head **hash_head) +{ + Eina_Hash_Element *hash_element; + int rb_hash = key_hash & EINA_HASH_RBTREE_MASK; + + key_hash &= hash->mask; + + if (!hash->buckets) + return NULL; + + *hash_head = (Eina_Hash_Head *)eina_rbtree_inline_lookup(hash->buckets[key_hash], + &rb_hash, 0, + EINA_RBTREE_CMP_KEY_CB( + _eina_hash_hash_rbtree_cmp_hash), + NULL); + if (!*hash_head) + return NULL; + + hash_element = (Eina_Hash_Element *)eina_rbtree_inline_lookup((*hash_head)->head, + tuple, 0, + EINA_RBTREE_CMP_KEY_CB( + _eina_hash_key_rbtree_cmp_key_data), + (const void *)hash-> + key_cmp_cb); + + return hash_element; +} + +static inline Eina_Hash_Element * +_eina_hash_find_by_data(const Eina_Hash *hash, + const void *data, + int *key_hash, + Eina_Hash_Head **hash_head) +{ + Eina_Hash_Each each; + Eina_Iterator *it; + int hash_num; + + if (!hash->buckets) + return NULL; + + each.hash_element = NULL; + each.data = data; + + for (hash_num = 0; hash_num < hash->size; hash_num++) + { + if (!hash->buckets[hash_num]) + continue; + + it = eina_rbtree_iterator_prefix(hash->buckets[hash_num]); + eina_iterator_foreach(it, EINA_EACH_CB(_eina_hash_rbtree_each), &each); + eina_iterator_free(it); + + if (each.hash_element) + { + *key_hash = hash_num; + *hash_head = each.hash_head; + return (Eina_Hash_Element *)each.hash_element; + } + } + + return NULL; +} + +static void +_eina_hash_el_free(Eina_Hash_Element *hash_element, Eina_Hash *hash) +{ + if (hash->data_free_cb) + hash->data_free_cb(hash_element->tuple.data); + + if (hash_element->begin == EINA_FALSE) + free(hash_element); +} + +static void +_eina_hash_head_free(Eina_Hash_Head *hash_head, Eina_Hash *hash) +{ + eina_rbtree_delete(hash_head->head, EINA_RBTREE_FREE_CB(_eina_hash_el_free), hash); + free(hash_head); +} + +static Eina_Bool +_eina_hash_del_by_hash_el(Eina_Hash *hash, + Eina_Hash_Element *hash_element, + Eina_Hash_Head *hash_head, + int key_hash) +{ + hash_head->head = eina_rbtree_inline_remove(hash_head->head, EINA_RBTREE_GET( + hash_element), EINA_RBTREE_CMP_NODE_CB( + _eina_hash_key_rbtree_cmp_node), + (const void *)hash->key_cmp_cb); + _eina_hash_el_free(hash_element, hash); + + if (!hash_head->head) + { + key_hash &= hash->mask; + + hash->buckets[key_hash] = + eina_rbtree_inline_remove(hash->buckets[key_hash], EINA_RBTREE_GET( + hash_head), + EINA_RBTREE_CMP_NODE_CB( + _eina_hash_hash_rbtree_cmp_node), NULL); + free(hash_head); + } + + hash->population--; + if (hash->population == 0) + { + free(hash->buckets); + hash->buckets = NULL; + } + + return EINA_TRUE; +} + +static Eina_Bool +_eina_hash_del_by_key_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) +{ + Eina_Hash_Element *hash_element; + Eina_Hash_Head *hash_head; + Eina_Hash_Tuple tuple; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + EINA_MAGIC_CHECK_HASH(hash); + + if (!hash->buckets) + return EINA_FALSE; + + tuple.key = (void *)key; + tuple.key_length = key_length; + tuple.data = (void *)data; + + hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head); + if (!hash_element) + return EINA_FALSE; + + return _eina_hash_del_by_hash_el(hash, hash_element, hash_head, key_hash); +} + +static Eina_Bool +_eina_hash_del_by_key(Eina_Hash *hash, const void *key, const void *data) +{ + int key_length, key_hash; + + EINA_MAGIC_CHECK_HASH(hash); + if (!hash) + return EINA_FALSE; + + if (!key) + return EINA_FALSE; + + if (!hash->buckets) + return EINA_FALSE; + + key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0; + key_hash = hash->key_hash_cb(key, key_length); + return _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, data); +} + +static unsigned int +_eina_string_key_length(const char *key) +{ + if (!key) + return 0; + + return (int)strlen(key) + 1; +} + +static int +_eina_string_key_cmp(const char *key1, __UNUSED__ int key1_length, + const char *key2, __UNUSED__ int key2_length) +{ + return strcmp(key1, key2); +} + +static int +_eina_stringshared_key_cmp(const char *key1, __UNUSED__ int key1_length, + const char *key2, __UNUSED__ int key2_length) +{ + return key1 - key2; +} + +static unsigned int +_eina_int32_key_length(__UNUSED__ const uint32_t *key) +{ + return 4; +} + +static int +_eina_int32_key_cmp(const uint32_t *key1, __UNUSED__ int key1_length, + const uint32_t *key2, __UNUSED__ int key2_length) +{ + return *key1 - *key2; +} + +static unsigned int +_eina_int64_key_length(__UNUSED__ const uint32_t *key) +{ + return 8; +} + +static int +_eina_int64_key_cmp(const uint64_t *key1, __UNUSED__ int key1_length, + const uint64_t *key2, __UNUSED__ int key2_length) +{ + return *key1 - *key2; +} + +static Eina_Bool +_eina_foreach_cb(const Eina_Hash *hash, + Eina_Hash_Tuple *data, + Eina_Hash_Foreach_Data *fdata) +{ + return fdata->cb((Eina_Hash *)hash, + data->key, + data->data, + (void *)fdata->fdata); +} + +static void * +_eina_hash_iterator_data_get_content(Eina_Iterator_Hash *it) +{ + Eina_Hash_Element *stuff; + + EINA_MAGIC_CHECK_HASH_ITERATOR(it, NULL); + + stuff = it->hash_element; + + if (!stuff) + return NULL; + + return stuff->tuple.data; +} + +static void * +_eina_hash_iterator_key_get_content(Eina_Iterator_Hash *it) +{ + Eina_Hash_Element *stuff; + + EINA_MAGIC_CHECK_HASH_ITERATOR(it, NULL); + + stuff = it->hash_element; + + if (!stuff) + return NULL; + + return (void *)stuff->tuple.key; +} + +static Eina_Hash_Tuple * +_eina_hash_iterator_tuple_get_content(Eina_Iterator_Hash *it) +{ + Eina_Hash_Element *stuff; + + EINA_MAGIC_CHECK_HASH_ITERATOR(it, NULL); + + stuff = it->hash_element; + + if (!stuff) + return NULL; + + return &stuff->tuple; +} + +static Eina_Bool +_eina_hash_iterator_next(Eina_Iterator_Hash *it, void **data) +{ + Eina_Bool ok; + int bucket; + + if (!(it->index < it->hash->population)) + return EINA_FALSE; + + if (!it->current) + { + ok = EINA_FALSE; + bucket = 0; + it->index = -1; + } + else + { + ok = eina_iterator_next(it->list, (void **)&it->hash_element); + if (!ok) + { + eina_iterator_free(it->list); + it->list = NULL; + + ok = eina_iterator_next(it->current, (void **)&it->hash_head); + if (!ok) + { + eina_iterator_free(it->current); + it->current = NULL; + it->bucket++; + } + else + { + it->list = eina_rbtree_iterator_prefix(it->hash_head->head); + ok = eina_iterator_next(it->list, (void **)&it->hash_element); + } + } + + bucket = it->bucket; + } + + if (ok == EINA_FALSE) + { + while (bucket < it->hash->size) + { + if (it->hash->buckets[bucket]) + { + it->current = + eina_rbtree_iterator_prefix(it->hash->buckets[bucket]); + ok = eina_iterator_next(it->current, (void **)&it->hash_head); + if (ok) + break; + + eina_iterator_free(it->current); + it->current = NULL; + } + + ++bucket; + } + if (it->list) + eina_iterator_free(it->list); + + it->list = eina_rbtree_iterator_prefix(it->hash_head->head); + ok = eina_iterator_next(it->list, (void **)&it->hash_element); + if (bucket == it->hash->size) + ok = EINA_FALSE; + } + + it->index++; + it->bucket = bucket; + + if (ok) + *data = it->get_content(it); + + return ok; +} + +static void * +_eina_hash_iterator_get_container(Eina_Iterator_Hash *it) +{ + EINA_MAGIC_CHECK_HASH_ITERATOR(it, NULL); + return (void *)it->hash; +} + +static void +_eina_hash_iterator_free(Eina_Iterator_Hash *it) +{ + EINA_MAGIC_CHECK_HASH_ITERATOR(it); + if (it->current) + eina_iterator_free(it->current); + + if (it->list) + eina_iterator_free(it->list); + + free(it); +} + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Hash_Group Hash Table + * + * @brief give a small description here : what it is for, what it does + * , etc... + * + * Hash API. Give some hints about the use (functions that must be + * used like init / shutdown), general use, etc... Give also a link to + * tutorial below. + * + * @section hashtable_algo Algorithm + * + * Give here the algorithm used in the implementation + * + * @section hashtable_perf Performance + * + * Give some hints about performance if it is possible, and an image ! + * + * @section hashtable_tutorial Tutorial + * + * Here is a fantastic tutorial about our hash table + * + * @{ + */ + +/** + * @brief Create a new hash table. + * + * @param key_length_cb The function called when getting the size of the key. + * @param key_cmp_cb The function called when comparing the keys. + * @param key_hash_cb The function called when getting the values. + * @param data_free_cb The function called when the hash table is freed. + * @param buckets_power_size The size of the buckets. + * @return The new hash table. + * + * This function create a new hash table using user-defined callbacks + * to manage the hash table. On failure, @c NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. If @p key_cmp_cb or @pkey_hash_cb + * are @c NULL, @c NULL is returned. If @p buckets_power_size is + * smaller or equal than 2, or if it is greater or equal than 17, + * @c NULL is returned. + * + * Pre-defined functions are available to create a hash table. See + * eina_hash_string_djb2_new(), eina_hash_string_superfast_new(), + * eina_hash_string_small_new(), eina_hash_int32_new(), + * eina_hash_int64_new(), eina_hash_pointer_new() and + * eina_hash_stringshared_new(). + */ +EAPI Eina_Hash * +eina_hash_new(Eina_Key_Length key_length_cb, + Eina_Key_Cmp key_cmp_cb, + Eina_Key_Hash key_hash_cb, + Eina_Free_Cb data_free_cb, + int buckets_power_size) +{ + /* FIXME: Use mempool. */ + Eina_Hash *new; + + eina_error_set(0); + EINA_SAFETY_ON_NULL_RETURN_VAL(key_cmp_cb, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(key_hash_cb, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(buckets_power_size < 3, NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(buckets_power_size > 16, NULL); + + new = malloc(sizeof (Eina_Hash)); + if (!new) + goto on_error; + + EINA_MAGIC_SET(new, EINA_MAGIC_HASH); + + new->key_length_cb = key_length_cb; + new->key_cmp_cb = key_cmp_cb; + new->key_hash_cb = key_hash_cb; + new->data_free_cb = data_free_cb; + new->buckets = NULL; + new->population = 0; + + new->size = 1 << buckets_power_size; + new->mask = new->size - 1; + + return new; + +on_error: + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; +} + +/** + * @brief Create a new hash table using the djb2 algorithm. + * + * @param data_free_cb The function called when the hash table is freed. + * @return The new hash table. + * + * This function create a new hash table using the djb2 algorithm for + * table management and strcmp() to compare the keys. Values can then + * be looked up with pointers other than the original key pointer that + * was used to add values. On failure, this function returns @c NULL. + * @p data_free_cb is a callback called when the hash table is + * freed. @c NULL can be passed as callback. + */ +EAPI Eina_Hash * +eina_hash_string_djb2_new(Eina_Free_Cb data_free_cb) +{ + return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length), + EINA_KEY_CMP(_eina_string_key_cmp), + EINA_KEY_HASH(eina_hash_djb2), + data_free_cb, + EINA_HASH_BUCKET_SIZE); +} + +/** + * @brief Create a new hash table for use with strings. + * + * @param data_free_cb The function called when the hash table is freed. + * @return The new hash table. + * + * This function create a new hash table using the superfast algorithm + * for table management and strcmp() to compare the keys. Values can + * then be looked up with pointers other than the original key pointer + * that was used to add values. On failure, this function returns + * @c NULL. @p data_free_cb is a callback called when the hash table is + * freed. @c NULL can be passed as callback. + */ +EAPI Eina_Hash * +eina_hash_string_superfast_new(Eina_Free_Cb data_free_cb) +{ + return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length), + EINA_KEY_CMP(_eina_string_key_cmp), + EINA_KEY_HASH(eina_hash_superfast), + data_free_cb, + EINA_HASH_BUCKET_SIZE); +} + +/** + * @brief Create a new hash table for use with strings with small bucket size. + * + * @param data_free_cb The function called when the hash table is freed. + * @return The new hash table. + * + * This function create a new hash table using the superfast algorithm + * for table management and strcmp() to compare the keys, but with a + * smaller bucket size (compared to eina_hash_string_superfast_new()) + * which will minimize the memory used by the returned hash + * table. Values can then be looked up with pointers other than the + * original key pointer that was used to add values. On failure, this + * function returns @c NULL. @p data_free_cb is a callback called when + * the hash table is freed. @c NULL can be passed as callback. + */ +EAPI Eina_Hash * +eina_hash_string_small_new(Eina_Free_Cb data_free_cb) +{ + return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length), + EINA_KEY_CMP(_eina_string_key_cmp), + EINA_KEY_HASH(eina_hash_superfast), + data_free_cb, + EINA_HASH_SMALL_BUCKET_SIZE); +} + +/** + * @brief Create a new hash table for use with 32bit integers. + * + * @param data_free_cb The function called when the hash table is freed. + * @return The new hash table. + * + * This function create a new hash table using the int32 algorithm for + * table management and dereferenced pointers to compare the + * keys. Values can then be looked up with pointers other than the + * original key pointer that was used to add values. This method may + * appear to be able to match string keys, actually it only matches + * the first character. On failure, this function returns @c NULL. + * @p data_free_cb is a callback called when the hash table is freed. + * @c NULL can be passed as callback. + */ +EAPI Eina_Hash * +eina_hash_int32_new(Eina_Free_Cb data_free_cb) +{ + return eina_hash_new(EINA_KEY_LENGTH(_eina_int32_key_length), + EINA_KEY_CMP(_eina_int32_key_cmp), + EINA_KEY_HASH(eina_hash_int32), + data_free_cb, + EINA_HASH_BUCKET_SIZE); +} + +/** + * @brief Create a new hash table for use with 64bit integers. + * + * @param data_free_cb The function called when the hash table is freed. + * @return The new hash table. + * + * This function create a new hash table using the int64 algorithm for + * table management and dereferenced pointers to compare the + * keys. Values can then be looked up with pointers other than the + * original key pointer that was used to add values. This method may + * appear to be able to match string keys, actually it only matches + * the first character. On failure, this function returns @c NULL. + * @p data_free_cb is a callback called when the hash table is freed. + * @c NULL can be passed as callback. + */ +EAPI Eina_Hash * +eina_hash_int64_new(Eina_Free_Cb data_free_cb) +{ + return eina_hash_new(EINA_KEY_LENGTH(_eina_int64_key_length), + EINA_KEY_CMP(_eina_int64_key_cmp), + EINA_KEY_HASH(eina_hash_int64), + data_free_cb, + EINA_HASH_BUCKET_SIZE); +} + +/** + * @brief Create a new hash table for use with pointers. + * + * @param data_free_cb The function called when the hash table is freed. + * @return The new hash table. + * + * This function create a new hash table using the int64 algorithm for + * table management and dereferenced pointers to compare the + * keys. Values can then be looked up with pointers other than the + * original key pointer that was used to add values. This method may + * appear to be able to match string keys, actually it only matches + * the first character. On failure, this function returns @c NULL. + * @p data_free_cb is a callback called when the hash table is freed. + * @c NULL can be passed as callback. + */ +EAPI Eina_Hash * +eina_hash_pointer_new(Eina_Free_Cb data_free_cb) +{ +#ifdef __LP64__ + return eina_hash_new(EINA_KEY_LENGTH(_eina_int64_key_length), + EINA_KEY_CMP(_eina_int64_key_cmp), + EINA_KEY_HASH(eina_hash_int64), + data_free_cb, + EINA_HASH_BUCKET_SIZE); +#else + return eina_hash_new(EINA_KEY_LENGTH(_eina_int32_key_length), + EINA_KEY_CMP(_eina_int32_key_cmp), + EINA_KEY_HASH(eina_hash_int32), + data_free_cb, + EINA_HASH_BUCKET_SIZE); +#endif +} + +/** + * @brief Create a new hash table optimized for stringshared values. + * + * @param data_free_cb The function called when the hash table is freed. + * @return The new hash table. + * + * This function create a new hash table optimized for stringshared + * values. Values CAN NOT be looked up with pointers not + * equal to the original key pointer that was used to add a value. On failure, this function returns @c NULL. + * @p data_free_cb is a callback called when the hash table is freed. + * @c NULL can be passed as callback. + * + * Excerpt of code that will NOT work with this type of hash: + * + * @code + * extern Eina_Hash *hash; + * extern const char *value; + * const char *a = eina_stringshare_add("key"); + * + * eina_hash_add(hash, a, value); + * eina_hash_find(hash, "key") + * @endcode + */ +EAPI Eina_Hash * +eina_hash_stringshared_new(Eina_Free_Cb data_free_cb) +{ + return eina_hash_new(NULL, + EINA_KEY_CMP(_eina_stringshared_key_cmp), + EINA_KEY_HASH(eina_hash_superfast), + data_free_cb, + EINA_HASH_BUCKET_SIZE); +} + +/** + * @brief Returns the number of entries in the given hash table. + * + * @param hash The given hash table. + * @return The number of entries in the hash table. + * + * This function returns the number of entries in @p hash, or 0 on + * error. If @p hash is @c NULL, 0 is returned. + */ +EAPI int +eina_hash_population(const Eina_Hash *hash) +{ + if (!hash) + return 0; + + EINA_MAGIC_CHECK_HASH(hash); + return hash->population; +} + +/** + * Free the given hash table resources. + * + * @param hash The hash table to be freed. + * + * This function frees up all the memory allocated to storing @p hash, + * and call the free callback if it has been passed to the hash table + * at creation time. If no free callback has been passed, any entries + * in the table that the program has no more pointers for elsewhere + * may now be lost, so this should only be called if the program has + * already freed any allocated data in the hash table or has the + * pointers for data in the table stored elsewhere as well. If @p hash + * is @c NULL, the function returns immediately. + * + * Example: + * @code + * extern Eina_Hash *hash; + * + * eina_hash_free(hash); + * hash = NULL; + * @endcode + */ +EAPI void +eina_hash_free(Eina_Hash *hash) +{ + int i; + + EINA_MAGIC_CHECK_HASH(hash); + EINA_SAFETY_ON_NULL_RETURN(hash); + + if (hash->buckets) + { + for (i = 0; i < hash->size; i++) + eina_rbtree_delete(hash->buckets[i], EINA_RBTREE_FREE_CB(_eina_hash_head_free), hash); + free(hash->buckets); + } + free(hash); +} + +/** + * Free the given hash table buckets resources. + * + * @param hash The hash table whose buckets have to be freed. + * + * This function frees up all the memory allocated to storing the + * buckets of @p hash, and call the free callback on all hash table + * buckets if it has been passed to the hash table at creation time, + * then frees the buckets. If no free callback has been passed, no + * buckets value will be freed. If @p hash is @c NULL, the function + * returns immediately. + */ +EAPI void +eina_hash_free_buckets(Eina_Hash *hash) +{ + int i; + + EINA_MAGIC_CHECK_HASH(hash); + EINA_SAFETY_ON_NULL_RETURN(hash); + + if (hash->buckets) + { + for (i = 0; i < hash->size; i++) + eina_rbtree_delete(hash->buckets[i], + EINA_RBTREE_FREE_CB(_eina_hash_head_free), hash); + free(hash->buckets); + hash->buckets = NULL; + hash->population = 0; + } +} + +/** + * @brief Add an entry to the given hash table. + * + * @param hash The given hash table. + * @param key A unique key. + * @param key_length The length of the key. + * @param key_hash The hash that will always match key. + * @param data The data to associate with the string given by the key. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * This function adds @p key to @p hash. @p hash, @p key and @p data + * can be @c NULL, in that case #EINA_FALSE is returned. @p key is + * expected to be a unique string within the hash table. Otherwise, + * one cannot be sure which inserted data pointer will be accessed + * with @ref eina_hash_find, and removed with @ref eina_hash_del. Do + * not forget to count '\\0' for string when setting the value of + * @p key_length. @p key_hash is expected to always match + * @p key. Otherwise, one cannot be sure to find it again with @ref + * eina_hash_find_by_hash. Key strings are case sensitive. If an error + * occurs, eina_error_get() should be used to determine if an + * allocation error occurred during this function. This function + * returns #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + */ +EAPI Eina_Bool +eina_hash_add_by_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) +{ + return eina_hash_add_alloc_by_hash(hash, + key, + key_length, + key_length, + key_hash, + data); +} + +/** + * @brief Add an entry to the given hash table and do not duplicate the string key. + * + * @param hash The given hash table. Can be @c NULL. + * @param key A unique key. Can be @c NULL. + * @param key_length Should be the length of @p key (don't forget to count '\\0' for string). + * @param key_hash The hash that will always match key. + * @param data Data to associate with the string given by @p key. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * This function adds @p key to @p hash. @p hash, @p key and @p data + * can be @c NULL, in that case #EINA_FALSE is returned. @p key is + * expected to be a unique string within the hash table. Otherwise, + * one cannot be sure which inserted data pointer will be accessed + * with @ref eina_hash_find, and removed with @ref eina_hash_del. This + * function does not make a copy of @p key so it must be a string + * constant or stored elsewhere (in the object being added). Do + * not forget to count '\\0' for string when setting the value of + * @p key_length. @p key_hash is expected to always match + * @p key. Otherwise, one cannot be sure to find it again with @ref + * eina_hash_find_by_hash. Key strings are case sensitive. If an error + * occurs, eina_error_get() should be used to determine if an + * allocation error occurred during this function. This function + * returns #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + */ +EAPI Eina_Bool +eina_hash_direct_add_by_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) +{ + return eina_hash_add_alloc_by_hash(hash, key, key_length, 0, key_hash, data); +} + +/** + * @brief Add an entry to the given hash table. + * + * @param hash The given hash table. + * @param key A unique key. + * @param data Data to associate with the string given by @p key. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * This function adds @p key to @p hash. @p hash, @p key and @p data + * can be @c NULL, in that case #EINA_FALSE is returned. @p key is + * expected to be unique within the hash table. Key uniqueness varies + * depending on the type of @p hash: a stringshared @ref Eina_Hash + * need only have unique pointers for keys, but the strings in the + * pointers may be identical. All other hash types require the strings + * themselves to be unique. Failure to use sufficient uniqueness will + * result in unexpected results when inserting data pointers accessed + * with eina_hash_find(), and removed with eina_hash_del(). Key + * strings are case sensitive. If an error occurs, eina_error_get() + * should be used to determine if an allocation error occurred during + * this function. This function returns #EINA_FALSE if an error + * occurred, #EINA_TRUE otherwise. + */ +EAPI Eina_Bool +eina_hash_add(Eina_Hash *hash, const void *key, const void *data) +{ + unsigned int key_length; + int key_hash; + + EINA_MAGIC_CHECK_HASH(hash); + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE); + + key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0; + key_hash = hash->key_hash_cb(key, key_length); + + return eina_hash_add_alloc_by_hash(hash, key, key_length, key_length, key_hash, data); +} + +/** + * @brief Add an entry to the given hash table without duplicating the string key. + * + * @param hash The given hash table. Can be @c NULL. + * @param key A unique key. Can be @c NULL. + * @param data Data to associate with the string given by @p key. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * This function adds @p key to @p hash. @p hash, @p key and @p data + * can be @c NULL, in that case #EINA_FALSE is returned. @p key is + * expected to be unique within the hash table. Key uniqueness varies + * depending on the type of @p hash: a stringshared @ref Eina_Hash + * need only have unique pointers for keys, but the strings in the + * pointers may be identical. All other hash types require the strings + * themselves to be unique. Failure to use sufficient uniqueness will + * result in unexpected results when inserting data pointers accessed + * with eina_hash_find(), and removed with eina_hash_del(). This + * function does not make a copy of @p key, so it must be a string + * constant or stored elsewhere ( in the object being added). Key + * strings are case sensitive. If an error occurs, eina_error_get() + * should be used to determine if an allocation error occurred during + * this function. This function returns #EINA_FALSE if an error + * occurred, #EINA_TRUE otherwise. + */ +EAPI Eina_Bool +eina_hash_direct_add(Eina_Hash *hash, const void *key, const void *data) +{ + int key_length; + int key_hash; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE); + EINA_MAGIC_CHECK_HASH(hash); + + key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0; + key_hash = hash->key_hash_cb(key, key_length); + + return eina_hash_add_alloc_by_hash(hash, key, key_length, 0, key_hash, data); +} + +/** + * @brief Remove the entry identified by a key and a key hash from the given hash table. + * + * @param hash The given hash table. + * @param key The key. + * @param key_length The length of the key. + * @param key_hash The hash that always match the key. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * This function removes the entry identified by @p key and + * @p key_hash from @p hash. If a free function was given to the + * callback on creation, it will be called for the data being + * deleted. Do not forget to count '\\0' for string when setting the + * value of @p key_length. If @p hash or @p key are @c NULL, the + * functions returns immediately #EINA_FALSE. This function returns + * #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * @note if you don't have the key_hash, use eina_hash_del_by_key() instead. + * @note if you don't have the key, use eina_hash_del_by_data() instead. + */ +EAPI Eina_Bool +eina_hash_del_by_key_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + + return _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, NULL); +} + +/** + * @brief Remove the entry identified by a key from the given hash table. + * + * This version will calculate key length and hash by using functions + * provided to hash creation function. + * + * @param hash The given hash table. + * @param key The key. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * This function removes the entry identified by @p key from @p + * hash. The key length and hash will be calculated automatically by + * using functiond provided to has creation function. If a free + * function was given to the callback on creation, it will be called + * for the data being deleted. If @p hash or @p key are @c NULL, the + * functions returns immediately #EINA_FALSE. This function returns + * #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * @note if you already have the key_hash, use eina_hash_del_by_key_hash() instead. + * @note if you don't have the key, use eina_hash_del_by_data() instead. + */ +EAPI Eina_Bool +eina_hash_del_by_key(Eina_Hash *hash, const void *key) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); + + return _eina_hash_del_by_key(hash, key, NULL); +} + +/** + * @brief Remove the entry identified by a data from the given hash table. + * + * This version is slow since there is no quick access to nodes based on data. + * + * @param hash The given hash table. + * @param data The data value to search and remove. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * thing goes fine. + * + * This function removes the entry identified by @p data from @p + * hash. If a free function was given to the callback on creation, it + * will be called for the data being deleted. If @p hash or @p data + * are @c NULL, the functions returns immediately #EINA_FALSE. This + * function returns #EINA_FALSE if an error occurred, #EINA_TRUE + * otherwise. + * + * @note if you already have the key, use eina_hash_del_by_key() or eina_hash_del_by_key_hash() instead. + */ +EAPI Eina_Bool +eina_hash_del_by_data(Eina_Hash *hash, const void *data) +{ + Eina_Hash_Element *hash_element; + Eina_Hash_Head *hash_head; + int key_hash; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE); + EINA_MAGIC_CHECK_HASH(hash); + + hash_element = _eina_hash_find_by_data(hash, data, &key_hash, &hash_head); + if (!hash_element) + goto error; + + if (hash_element->tuple.data != data) + goto error; + + return _eina_hash_del_by_hash_el(hash, hash_element, hash_head, key_hash); + +error: + return EINA_FALSE; +} + +/** + * @brief Remove the entry identified by a key and a key hash or a + * data from the given hash table. + * + * If @p key is @c NULL, then @p data is used to find a match to + * remove. + * + * @param hash The given hash table. + * @param key The key. + * @param key_length The length of the key. + * @param key_hash The hash that always match the key. + * @param data The data pointer to remove if the key is @c NULL. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * This function removes the entry identified by @p key and + * @p key_hash, or @p data, from @p hash. If a free function was given to + * the callback on creation, it will be called for the data being + * deleted. If @p hash is @c NULL, the functions returns immediately + * #EINA_FALSE. If @p key is @c NULL, then @p key_hash and @p key_hash + * are ignored and @p data is used to find a match to remove, + * otherwise @p key and @p key_hash are used and @p data is not + * required and can be @c NULL. Do not forget to count '\\0' for + * string when setting the value of @p key_length. This function + * returns #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * @note if you know you already have the key, use eina_hash_del_by_key_hash(), + * if you know you don't have the key, use eina_hash_del_by_data() + * directly. + */ +EAPI Eina_Bool +eina_hash_del_by_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) +{ + Eina_Bool ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_MAGIC_CHECK_HASH(hash); + + if (key) + ret = _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, data); + else + ret = eina_hash_del_by_data(hash, data); + + return ret; +} + +/** + * @brief Remove the entry identified by a key or a data from the given + * hash table. + * + * @param hash The given hash table. + * @param key The key. + * @param data The data pointer to remove if the key is @c NULL. + * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise. + * + * This function removes the entry identified by @p key or @p data + * from @p hash. If a free function was given to the + * callback on creation, it will be called for the data being + * deleted. If @p hash is @c NULL, the functions returns immediately + * #EINA_FALSE. If @p key is @c NULL, then @p data is used to find the a + * match to remove, otherwise @p key is used and @p data is not + * required and can be @c NULL. This function returns #EINA_FALSE if + * an error occurred, #EINA_TRUE otherwise. + * + * @note if you know you already have the key, use + * eina_hash_del_by_key() or eina_hash_del_by_key_hash(). If you + * know you don't have the key, use eina_hash_del_by_data() + * directly. + */ +EAPI Eina_Bool +eina_hash_del(Eina_Hash *hash, const void *key, const void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_MAGIC_CHECK_HASH(hash); + + if (!key) + return eina_hash_del_by_data(hash, data); + + return _eina_hash_del_by_key(hash, key, data); +} + +/** + * @brief Retrieve a specific entry in the given hash table. + * + * @param hash The given hash table. + * @param key The key of the entry to find. + * @param key_length The length of the key. + * @param key_hash The hash that always match the key + * @return The data pointer for the stored entry on success, @c NULL + * otherwise. + * + * This function retrieves the entry associated to @p key of length + * @p key_length in @p hash. @p key_hash is the hash that always match + * @p key. It is ignored if @p key is @c NULL. Do not forget to count + * '\\0' for string when setting the value of @p key_length. If + * @p hash is @c NULL, this function returns immediately @c NULL. This + * function returns the data pointer on success, @c NULL otherwise. + */ +EAPI void * +eina_hash_find_by_hash(const Eina_Hash *hash, + const void *key, + int key_length, + int key_hash) +{ + Eina_Hash_Head *hash_head; + Eina_Hash_Element *hash_element; + Eina_Hash_Tuple tuple; + + if (!hash) + return NULL; + + EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); + EINA_MAGIC_CHECK_HASH(hash); + + tuple.key = key; + tuple.key_length = key_length; + tuple.data = NULL; + + hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head); + if (hash_element) + return hash_element->tuple.data; + + return NULL; +} + +/** + * @brief Retrieve a specific entry in the given hash table. + * + * @param hash The given hash table. + * @param key The key of the entry to find. + * @return The data pointer for the stored entry on success, @c NULL + * otherwise. + * + * This function retrieves the entry associated to @p key in + * @p hash. If @p hash is @c NULL, this function returns immediately + * @c NULL. This function returns the data pointer on success, @c NULL + * otherwise. + */ +EAPI void * +eina_hash_find(const Eina_Hash *hash, const void *key) +{ + int key_length; + int hash_num; + + if (!hash) + return NULL; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); + EINA_MAGIC_CHECK_HASH(hash); + + key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0; + hash_num = hash->key_hash_cb(key, key_length); + + return eina_hash_find_by_hash(hash, key, key_length, hash_num); +} + +/** + * @brief Modify the entry pointer at the specified key and returns + * the old entry. + * + * @param hash The given hash table. + * @param key The key of the entry to modify. + * @param key_length Should be the length of @p key (don't forget to count '\\0' for string). + * @param key_hash The hash that always match the key. Ignored if @p key is @c NULL. + * @param data The data to replace the old entry, if it exists. + * @return The data pointer for the old stored entry, or @c NULL if not + * found. If an existing entry is not found, nothing is added to the + * hash. + */ +EAPI void * +eina_hash_modify_by_hash(Eina_Hash *hash, + const void *key, + int key_length, + int key_hash, + const void *data) +{ + Eina_Hash_Head *hash_head; + Eina_Hash_Element *hash_element; + void *old_data = NULL; + Eina_Hash_Tuple tuple; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL); + EINA_MAGIC_CHECK_HASH(hash); + + tuple.key = key; + tuple.key_length = key_length; + tuple.data = NULL; + + hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head); + if (hash_element) + { + old_data = hash_element->tuple.data; + hash_element->tuple.data = (void *)data; + } + + return old_data; +} + +/** + * @brief Modify the entry pointer at the specified key and return the + * old entry or add the entry if not found. + * + * @param hash The given hash table. + * @param key The key of the entry to modify. + * @param data The data to replace the old entry + * @return The data pointer for the old stored entry, or @c NULL + * otherwise. + * + * This function modifies the data of @p key with @p data in @p + * hash. If no entry is found, @p data is added to @p hash with the + * key @p key. On success this function returns the old entry, + * otherwise it returns @c NULL. To check for errors, use + * eina_error_get(). + */ +EAPI void * +eina_hash_set(Eina_Hash *hash, const void *key, const void *data) +{ + Eina_Hash_Tuple tuple; + Eina_Hash_Head *hash_head; + Eina_Hash_Element *hash_element; + int key_length; + int key_hash; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL); + EINA_MAGIC_CHECK_HASH(hash); + + key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0; + key_hash = hash->key_hash_cb(key, key_length); + + tuple.key = key; + tuple.key_length = key_length; + tuple.data = NULL; + + hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head); + if (hash_element) + { + void *old_data = NULL; + + old_data = hash_element->tuple.data; + hash_element->tuple.data = (void *)data; + return old_data; + } + + eina_hash_add_alloc_by_hash(hash, + key, + key_length, + key_length, + key_hash, + data); + return NULL; +} +/** + * @brief Modify the entry pointer at the specified key and return the old entry. + * @param hash The given hash table. + * @param key The key of the entry to modify. + * @param data The data to replace the old entry. + * @return The data pointer for the old stored entry on success, or + * @c NULL otherwise. + * + * This function modifies the data of @p key with @p data in @p + * hash. If no entry is found, nothing is added to @p hash. On success + * this function returns the old entry, otherwise it returns @c NULL. + */ +EAPI void * +eina_hash_modify(Eina_Hash *hash, const void *key, const void *data) +{ + int key_length; + int hash_num; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL); + EINA_MAGIC_CHECK_HASH(hash); + + key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0; + hash_num = hash->key_hash_cb(key, key_length); + + return eina_hash_modify_by_hash(hash, key, key_length, hash_num, data); +} + +/** + * @brief Change the key associated with a data without triggering the + * free callback. + * + * @param hash The given hash table. + * @param old_key The current key associated with the data + * @param new_key The new key to associate data with + * @return EINA_FALSE in any case but success, EINA_TRUE on success. + * + * This function allows for the move of data from one key to another, + * but does not call the Eina_Free_Cb associated with the hash table + * when destroying the old key. + */ +EAPI Eina_Bool +eina_hash_move(Eina_Hash *hash, const void *old_key, const void *new_key) +{ + Eina_Free_Cb hash_free_cb; + const void *data; + Eina_Bool result = EINA_FALSE; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(old_key, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(new_key, EINA_FALSE); + EINA_MAGIC_CHECK_HASH(hash); + + data = eina_hash_find(hash, old_key); + if (!data) goto error; + + hash_free_cb = hash->data_free_cb; + hash->data_free_cb = NULL; + + eina_hash_del(hash, old_key, data); + result = eina_hash_add(hash, new_key, data); + + hash->data_free_cb = hash_free_cb; + +error: + return result; +} + +/*============================================================================* +* Iterator * +*============================================================================*/ + +/** + * @brief Call a function on every member stored in the hash table + * + * @param hash The hash table whose members will be walked + * @param func The function to call on each parameter + * @param fdata The data pointer to pass to the function being called + * + * This function goes through every entry in the hash table @p hash and calls + * the function @p func on each member. The function should @b not modify the + * hash table contents if it returns 1. @b If the hash table contents are + * modified by this function or the function wishes to stop processing it must + * return 0, otherwise return 1 to keep processing. + * + * Example: + * @code + * extern Eina_Hash *hash; + * + * Eina_Bool hash_fn(const Eina_Hash *hash, const void *key, void *data, void *fdata) + * { + * printf("Func data: %s, Hash entry: %s / %p\n", fdata, (const char *)key, data); + * return 1; + * } + * + * int main(int argc, char **argv) + * { + * char *hash_fn_data; + * + * hash_fn_data = strdup("Hello World"); + * eina_hash_foreach(hash, hash_fn, hash_fn_data); + * free(hash_fn_data); + * } + * @endcode + */ +EAPI void +eina_hash_foreach(const Eina_Hash *hash, + Eina_Hash_Foreach func, + const void *fdata) +{ + Eina_Iterator *it; + Eina_Hash_Foreach_Data foreach; + + EINA_MAGIC_CHECK_HASH(hash); + EINA_SAFETY_ON_NULL_RETURN(hash); + EINA_SAFETY_ON_NULL_RETURN(func); + + foreach.cb = func; + foreach.fdata = fdata; + + it = eina_hash_iterator_tuple_new(hash); + if (!it) + return; + eina_iterator_foreach(it, EINA_EACH_CB(_eina_foreach_cb), &foreach); + + eina_iterator_free(it); +} + +/** + * @brief Returned a new iterator associated to hash data. + * + * @param hash The hash. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to + * @p hash. If @p hash is not populated, this function still returns a + * valid iterator that will always return false on + * eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, @c NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the hash structure changes then the iterator becomes + * invalid. That is, if you add or remove items this iterator behavior + * is undefined and your program may crash. + */ +EAPI Eina_Iterator * +eina_hash_iterator_data_new(const Eina_Hash *hash) +{ + Eina_Iterator_Hash *it; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + EINA_MAGIC_CHECK_HASH(hash); + + eina_error_set(0); + it = calloc(1, sizeof (Eina_Iterator_Hash)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + it->hash = hash; + it->get_content = FUNC_ITERATOR_GET_CONTENT(_eina_hash_iterator_data_get_content); + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _eina_hash_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_hash_iterator_free); + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + EINA_MAGIC_SET(it, EINA_MAGIC_HASH_ITERATOR); + + return &it->iterator; +} + +/** + * @brief Returned a new iterator associated to hash keys. + * + * @param hash The hash. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * hash. If @p hash is not populated, this function still returns a + * valid iterator that will always return false on + * eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the hash structure changes then the iterator becomes + * invalid! That is, if you add or remove items this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_hash_iterator_key_new(const Eina_Hash *hash) +{ + Eina_Iterator_Hash *it; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + EINA_MAGIC_CHECK_HASH(hash); + + eina_error_set(0); + it = calloc(1, sizeof (Eina_Iterator_Hash)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + it->hash = hash; + it->get_content = FUNC_ITERATOR_GET_CONTENT( + _eina_hash_iterator_key_get_content); + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _eina_hash_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_hash_iterator_free); + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + EINA_MAGIC_SET(it, EINA_MAGIC_HASH_ITERATOR); + + return &it->iterator; +} + +/** + * @brief Returned a new iterator associated to hash keys and data. + * + * @param hash The hash. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * hash. If @p hash is not populated, this function still returns a + * valid iterator that will always return false on + * eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @note iterator data will provide values as Eina_Hash_Tuple that should not + * be modified! + * + * @warning if the hash structure changes then the iterator becomes + * invalid! That is, if you add or remove items this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_hash_iterator_tuple_new(const Eina_Hash *hash) +{ + Eina_Iterator_Hash *it; + + EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); + EINA_MAGIC_CHECK_HASH(hash); + + eina_error_set(0); + it = calloc(1, sizeof (Eina_Iterator_Hash)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + it->hash = hash; + it->get_content = FUNC_ITERATOR_GET_CONTENT( + _eina_hash_iterator_tuple_get_content); + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _eina_hash_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_hash_iterator_free); + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + EINA_MAGIC_SET(it, EINA_MAGIC_HASH_ITERATOR); + + return &it->iterator; +} + +/* Common hash functions */ + +/* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html) + used by WebCore (http://webkit.org/blog/8/hashtables-part-2/) */ +EAPI int +eina_hash_superfast(const char *key, int len) +{ + int hash = len, tmp; + int rem; + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (; len > 0; len--) + { + hash += get16bits(key); + tmp = (get16bits(key + 2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + key += 2 * sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) + { + case 3: + hash += get16bits(key); + hash ^= hash << 16; + hash ^= key[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + + case 2: + hash += get16bits(key); + hash ^= hash << 11; + hash += hash >> 17; + break; + + case 1: + hash += *key; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + +/** + * @} + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_inlist.c b/tests/suite/ecore/src/lib/eina_inlist.c new file mode 100644 index 0000000000..9ebc6233c4 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_inlist.c @@ -0,0 +1,693 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <assert.h> + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_error.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_inlist.h" + +/* FIXME: TODO please, refactor this :) */ + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +typedef struct _Eina_Iterator_Inlist Eina_Iterator_Inlist; +typedef struct _Eina_Accessor_Inlist Eina_Accessor_Inlist; + +struct _Eina_Iterator_Inlist +{ + Eina_Iterator iterator; + const Eina_Inlist *head; + const Eina_Inlist *current; +}; + +struct _Eina_Accessor_Inlist +{ + Eina_Accessor accessor; + + const Eina_Inlist *head; + const Eina_Inlist *current; + + unsigned int index; +}; + +static Eina_Bool +eina_inlist_iterator_next(Eina_Iterator_Inlist *it, void **data) { + if (!it->current) + return EINA_FALSE; + + if (data) + *data = (void *)it->current; + + it->current = it->current->next; + + return EINA_TRUE; +} + +static Eina_Inlist * +eina_inlist_iterator_get_container(Eina_Iterator_Inlist *it) { + return (Eina_Inlist *)it->head; +} + +static void +eina_inlist_iterator_free(Eina_Iterator_Inlist *it) { + free(it); +} + +static Eina_Bool +eina_inlist_accessor_get_at(Eina_Accessor_Inlist *it, + unsigned int idx, + void **data) { + const Eina_Inlist *over; + unsigned int middle; + unsigned int i; + + if (it->index == idx) + over = it->current; + else if (idx > it->index) + /* Looking after current. */ + for (i = it->index, over = it->current; + i < idx && over; + ++i, over = over->next) + ; + else + { + middle = it->index >> 1; + + if (idx > middle) + /* Looking backward from current. */ + for (i = it->index, over = it->current; + i > idx && over; + --i, over = over->prev) + ; + else + /* Looking from the start. */ + for (i = 0, over = it->head; + i < idx && over; + ++i, over = over->next) + ; + } + + if (!over) + return EINA_FALSE; + + it->current = over; + it->index = idx; + + if (data) + *data = (void *)over; + + return EINA_TRUE; +} + +static Eina_Inlist * +eina_inlist_accessor_get_container(Eina_Accessor_Inlist *it) { + return (Eina_Inlist *)it->head; +} + +static void +eina_inlist_accessor_free(Eina_Accessor_Inlist *it) { + free(it); +} + +/** + * @endcond + */ + + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Inline_List_Group Inline List + * + * @brief These functions provide inline list management. + * + * Inline lists mean its nodes pointers are part of same memory as + * data. This has the benefit of framenting memory less and avoiding + * @c node->data indirection, but has the drawback of elements only + * being able to be part of one single inlist at same time. But it is + * possible to have inlist nodes to be part of regular lists created + * with eina_list_append() or eina_list_prepend(). + * + * Inline lists have its purposes, but if you don't know them go with + * regular lists instead. + * + * @code + * #include <Eina.h> + * #include <stdio.h> + * + * int + * main(void) + * { + * struct my_struct { + * EINA_INLIST; + * int a, b; + * } *d, *cur; + * Eina_Inlist *list, *itr; + * + * eina_init(); + * + * d = malloc(sizeof(*d)); + * d->a = 1; + * d->b = 10; + * list = eina_inlist_append(NULL, EINA_INLIST_GET(d)); + * + * d = malloc(sizeof(*d)); + * d->a = 2; + * d->b = 20; + * list = eina_inlist_append(list, EINA_INLIST_GET(d)); + * + * d = malloc(sizeof(*d)); + * d->a = 3; + * d->b = 30; + * list = eina_inlist_prepend(list, EINA_INLIST_GET(d)); + * + * printf("list=%p\n", list); + * EINA_INLIST_FOREACH(list, cur) + * printf("\ta=%d, b=%d\n", cur->a, cur->b); + * + * list = eina_inlist_remove(list, EINA_INLIST_GET(d)); + * free(d); + * printf("list=%p\n", list); + * for (itr = list; itr != NULL; itr = itr->next) + * { + * cur = EINA_INLIST_CONTAINER_GET(itr, struct my_struct); + * printf("\ta=%d, b=%d\n", cur->a, cur->b); + * } + * + * while (list) + * { + * Eina_Inlist *aux = list; + * list = eina_inlist_remove(list, list); + * free(aux); + * } + * + * eina_shutdown(); + * + * return 0; + * } + * @endcode + * + * @{ + */ + +/** + * Add a new node to end of list. + * + * @note this code is meant to be fast, appends are O(1) and do not + * walk @a list anyhow. + * + * @note @a new_l is considered to be in no list. If it was in another + * list before, please eina_inlist_remove() it before adding. No + * check of @a new_l prev and next pointers is done, so it' safe + * to have them uninitialized. + * + * @param list existing list head or NULL to create a new list. + * @param new_l new list node, must not be NULL. + * + * @return the new list head. Use it and not given @a list anymore. + */ +EAPI Eina_Inlist * +eina_inlist_append(Eina_Inlist *list, Eina_Inlist *new_l) +{ + Eina_Inlist *l; + + EINA_SAFETY_ON_NULL_RETURN_VAL(new_l, list); + + new_l->next = NULL; + if (!list) + { + new_l->prev = NULL; + new_l->last = new_l; + return new_l; + } + + if (list->last) + l = list->last; + else + for (l = list; (l) && (l->next); l = l->next) + ; + + l->next = new_l; + new_l->prev = l; + list->last = new_l; + return list; +} + +/** + * Add a new node to beginning of list. + * + * @note this code is meant to be fast, prepends are O(1) and do not + * walk @a list anyhow. + * + * @note @a new_l is considered to be in no list. If it was in another + * list before, please eina_inlist_remove() it before adding. No + * check of @a new_l prev and next pointers is done, so it' safe + * to have them uninitialized. + * + * @param list existing list head or NULL to create a new list. + * @param new_l new list node, must not be NULL. + * + * @return the new list head. Use it and not given @a list anymore. + */ +EAPI Eina_Inlist * +eina_inlist_prepend(Eina_Inlist *list, Eina_Inlist *new_l) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(new_l, list); + + new_l->prev = NULL; + if (!list) + { + new_l->next = NULL; + new_l->last = new_l; + return new_l; + } + + new_l->next = list; + list->prev = new_l; + new_l->last = list->last; + list->last = NULL; + return new_l; +} + +/** + * Add a new node after the given relative item in list. + * + * @note this code is meant to be fast, appends are O(1) and do not + * walk @a list anyhow. + * + * @note @a new_l is considered to be in no list. If it was in another + * list before, please eina_inlist_remove() it before adding. No + * check of @a new_l prev and next pointers is done, so it' safe + * to have them uninitialized. + * + * @note @a relative is considered to be inside @a list, no checks are + * done to confirm that and giving nodes from different lists + * will lead to problems. Giving NULL @a relative is the same as + * eina_list_append(). + * + * @param list existing list head or NULL to create a new list. + * @param new_l new list node, must not be NULL. + * @param relative reference node, @a new_l will be added after it. + * + * @return the new list head. Use it and not given @a list anymore. + */ +EAPI Eina_Inlist * +eina_inlist_append_relative(Eina_Inlist *list, + Eina_Inlist *new_l, + Eina_Inlist *relative) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(new_l, list); + + if (relative) + { + if (relative->next) + { + new_l->next = relative->next; + relative->next->prev = new_l; + } + else + new_l->next = NULL; + + relative->next = new_l; + new_l->prev = relative; + if (!new_l->next) + list->last = new_l; + + return list; + } + + return eina_inlist_append(list, new_l); +} + +/** + * Add a new node before the given relative item in list. + * + * @note this code is meant to be fast, prepends are O(1) and do not + * walk @a list anyhow. + * + * @note @a new_l is considered to be in no list. If it was in another + * list before, please eina_inlist_remove() it before adding. No + * check of @a new_l prev and next pointers is done, so it' safe + * to have them uninitialized. + * + * @note @a relative is considered to be inside @a list, no checks are + * done to confirm that and giving nodes from different lists + * will lead to problems. Giving NULL @a relative is the same as + * eina_list_prepend(). + * + * @param list existing list head or NULL to create a new list. + * @param new_l new list node, must not be NULL. + * @param relative reference node, @a new_l will be added before it. + * + * @return the new list head. Use it and not given @a list anymore. + */ +EAPI Eina_Inlist * +eina_inlist_prepend_relative(Eina_Inlist *list, + Eina_Inlist *new_l, + Eina_Inlist *relative) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(new_l, list); + + if (relative) + { + new_l->prev = relative->prev; + new_l->next = relative; + relative->prev = new_l; + if (new_l->prev) + { + new_l->prev->next = new_l; + /* new_l->next could not be NULL, as it was set to 'relative' */ + assert(new_l->next); + return list; + } + else + { + /* new_l->next could not be NULL, as it was set to 'relative' */ + assert(new_l->next); + + new_l->last = list->last; + list->last = NULL; + return new_l; + } + } + + return eina_inlist_prepend(list, new_l); +} + +/** + * Remove node from list. + * + * @note this code is meant to be fast, removals are O(1) and do not + * walk @a list anyhow. + * + * @note @a item is considered to be inside @a list, no checks are + * done to confirm that and giving nodes from different lists + * will lead to problems, specially if @a item is the head since + * it will be different from @a list and the wrong new head will + * be returned. + * + * @param list existing list head, must not be NULL. + * @param item existing list node, must not be NULL. + * + * @return the new list head. Use it and not given @a list anymore. + */ +EAPI Eina_Inlist * +eina_inlist_remove(Eina_Inlist *list, Eina_Inlist *item) +{ + Eina_Inlist *return_l; + + /* checkme */ + EINA_SAFETY_ON_NULL_RETURN_VAL(list, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(item, list); + EINA_SAFETY_ON_TRUE_RETURN_VAL + ((item != list) && (!item->prev) && (!item->next), list); + + if (item->next) + item->next->prev = item->prev; + + if (item->prev) + { + item->prev->next = item->next; + return_l = list; + } + else + { + return_l = item->next; + if (return_l) + return_l->last = list->last; + } + + if (item == list->last) + list->last = item->prev; + + item->next = NULL; + item->prev = NULL; + return return_l; +} + +/** + * Move existing node to beginning of list. + * + * @note this code is meant to be fast, promotion is O(1) and do not + * walk @a list anyhow. + * + * @note @a item is considered to be inside @a list, no checks are + * done to confirm that and giving nodes from different lists + * will lead to problems. + * + * @param list existing list head or NULL to create a new list. + * @param item list node to move to beginning (head), must not be NULL. + * + * @return the new list head. Use it and not given @a list anymore. + */ +EAPI Eina_Inlist * +eina_inlist_promote(Eina_Inlist *list, Eina_Inlist *item) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(list, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(item, list); + + if (item == list) + return list; + + if (item->next) + item->next->prev = item->prev; + + item->prev->next = item->next; + + if (list->last == item) + list->last = item->prev; + + item->next = list; + item->prev = NULL; + item->last = list->last; + + list->prev = item; + list->last = NULL; + + return item; +} + +/** + * Move existing node to end of list. + * + * @note this code is meant to be fast, demoting is O(1) and do not + * walk @a list anyhow. + * + * @note @a item is considered to be inside @a list, no checks are + * done to confirm that and giving nodes from different lists + * will lead to problems. + * + * @param list existing list head or NULL to create a new list. + * @param item list node to move to end (tail), must not be NULL. + * + * @return the new list head. Use it and not given @a list anymore. + */ +EAPI Eina_Inlist * +eina_inlist_demote(Eina_Inlist *list, Eina_Inlist *item) +{ + Eina_Inlist *l; + + EINA_SAFETY_ON_NULL_RETURN_VAL(list, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(item, list); + + if (list->last == item) + return list; + + if (!list->last) + { + for (l = list; l->next; l = l->next) + ; + list->last = l; + } + + l = list; + if (item->prev) + item->prev->next = item->next; + else + l = item->next; + + item->next->prev = item->prev; + + list->last->next = item; + item->prev = list->last; + item->next = NULL; + + l->last = item; + return l; +} + +/** + * Find given node in list, returns itself if found, NULL if not. + * + * @warning this is an expensive call and have O(n) cost, possibly + * walking the whole list. + * + * @param list existing list to search @a item in, must not be NULL. + * @param item what to search for, must not be NULL. + * + * @return @a item if found, NULL if not. + */ +EAPI Eina_Inlist * +eina_inlist_find(Eina_Inlist *list, Eina_Inlist *item) +{ + Eina_Inlist *l; + + for (l = list; l; l = l->next) { + if (l == item) + return item; + } + return NULL; +} + +/** + * @brief Get the count of the number of items in a list. + * + * @param list The list whose count to return. + * @return The number of members in the list. + * + * This function returns how many members @p list contains. If the + * list is @c NULL, 0 is returned. + * + * @warning This is an order-N operation and so the time will depend + * on the number of elements on the list, that is, it might become + * slow for big lists! + */ +EAPI unsigned int +eina_inlist_count(const Eina_Inlist *list) +{ + const Eina_Inlist *l; + unsigned int i = 0; + + for (l = list; l; l = l->next) + i++; + + return i; +} + +/** + * @brief Returned a new iterator associated to a list. + * + * @param list The list. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * list. If @p list is @c NULL or the count member of @p list is less + * or equal than 0, this function still returns a valid iterator that + * will always return false on eina_iterator_next(), thus keeping API + * sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the list structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_inlist_iterator_new(const Eina_Inlist *list) +{ + Eina_Iterator_Inlist *it; + + eina_error_set(0); + it = calloc(1, sizeof (Eina_Iterator_Inlist)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + it->head = list; + it->current = list; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(eina_inlist_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + eina_inlist_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(eina_inlist_iterator_free); + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + return &it->iterator; +} + +/** + * @brief Returned a new accessor associated to a list. + * + * @param list The list. + * @return A new accessor. + * + * This function returns a newly allocated accessor associated to + * @p list. If @p list is @c NULL or the count member of @p list is + * less or equal than 0, this function returns NULL. If the memory can + * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is + * set. Otherwise, a valid accessor is returned. + */ +EAPI Eina_Accessor * +eina_inlist_accessor_new(const Eina_Inlist *list) +{ + Eina_Accessor_Inlist *ac; + + eina_error_set(0); + ac = calloc(1, sizeof (Eina_Accessor_Inlist)); + if (!ac) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + ac->head = list; + ac->current = list; + ac->index = 0; + + ac->accessor.version = EINA_ACCESSOR_VERSION; + ac->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_inlist_accessor_get_at); + ac->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER( + eina_inlist_accessor_get_container); + ac->accessor.free = FUNC_ACCESSOR_FREE(eina_inlist_accessor_free); + + EINA_MAGIC_SET(&ac->accessor, EINA_MAGIC_ACCESSOR); + + return &ac->accessor; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_iterator.c b/tests/suite/ecore/src/lib/eina_iterator.c new file mode 100644 index 0000000000..66dbbf4ee7 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_iterator.c @@ -0,0 +1,260 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> + +#include "eina_config.h" +#include "eina_private.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_iterator.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static const char EINA_MAGIC_ITERATOR_STR[] = "Eina Iterator"; + +#define EINA_MAGIC_CHECK_ITERATOR(d) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ITERATOR)) { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_ITERATOR); } \ + } while(0) + +/** + * @endcond + */ + + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the iterator module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the iterator module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_iterator_init(void) +{ + return eina_magic_string_set(EINA_MAGIC_ITERATOR, EINA_MAGIC_ITERATOR_STR); +} + +/** + * @internal + * @brief Shut down the iterator module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the iterator module set up by + * eina_iterator_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_iterator_shutdown(void) +{ + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Iterator_Group Iterator Functions + * + * @brief These functions manage iterators on containers. + * + * These functions allow to access elements of a container in a + * generic way, without knowing which container is used (a bit like + * iterators in the C++ STL). Iterators only allows sequential access + * (that is, from an element to the next one). For random access, see + * @ref Eina_Accessor_Group. + * + * An iterator is created from container data types, so no creation + * function is available here. An iterator is deleted with + * eina_iterator_free(). To get the data and iterate, use + * eina_iterator_next(). To call a function on all the elements of a + * container, use eina_iterator_foreach(). + * + * @{ + */ + +/** + * @brief Free an iterator. + * + * @param iterator The iterator to free. + * + * This function frees @p iterator if it is not @c NULL; + */ +EAPI void +eina_iterator_free(Eina_Iterator *iterator) +{ + EINA_MAGIC_CHECK_ITERATOR(iterator); + EINA_SAFETY_ON_NULL_RETURN(iterator); + EINA_SAFETY_ON_NULL_RETURN(iterator->free); + iterator->free(iterator); +} + +/** + * @brief Return the container of an iterator. + * + * @param iterator The iterator. + * @return The container which created the iterator. + * + * This function returns the container which created @p iterator. If + * @p iterator is @c NULL, this function returns @c NULL. + */ +EAPI void * +eina_iterator_container_get(Eina_Iterator *iterator) +{ + EINA_MAGIC_CHECK_ITERATOR(iterator); + EINA_SAFETY_ON_NULL_RETURN_VAL(iterator, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(iterator->get_container, NULL); + return iterator->get_container(iterator); +} + +/** + * @brief Return the value of the current element and go to the next one. + * + * @param iterator The iterator. + * @param data The data of the element. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function returns the value of the current element pointed by + * @p iterator in @p data, then goes to the next element. If @p + * iterator is @c NULL or if a problem occurred, #EINA_FALSE is + * returned, otherwise #EINA_TRUE is returned. + */ +EAPI Eina_Bool +eina_iterator_next(Eina_Iterator *iterator, void **data) +{ + if (!iterator) + return EINA_FALSE; + + EINA_MAGIC_CHECK_ITERATOR(iterator); + EINA_SAFETY_ON_NULL_RETURN_VAL(iterator, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(iterator->next, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE); + return iterator->next(iterator, data); +} + +/** + * @brief Iterate over the container and execute a callback on each element. + * + * @param iterator The iterator. + * @param cb The callback called on each iteration. + * @param fdata The data passed to the callback. + * + * This function iterates over the elements pointed by @p iterator, + * beginning from the current element. For Each element, the callback + * @p cb is called with the data @p fdata. If @p iterator is @c NULL, + * the function returns immediately. Also, if @p cb returns @c + * EINA_FALSE, the iteration stops at that point. + */ +EAPI void +eina_iterator_foreach(Eina_Iterator *iterator, + Eina_Each_Cb cb, + const void *fdata) +{ + const void *container; + void *data; + + EINA_MAGIC_CHECK_ITERATOR(iterator); + EINA_SAFETY_ON_NULL_RETURN(iterator); + EINA_SAFETY_ON_NULL_RETURN(iterator->get_container); + EINA_SAFETY_ON_NULL_RETURN(iterator->next); + EINA_SAFETY_ON_NULL_RETURN(cb); + + if (!eina_iterator_lock(iterator)) return ; + + container = iterator->get_container(iterator); + while (iterator->next(iterator, &data) == EINA_TRUE) { + if (cb(container, data, (void *)fdata) != EINA_TRUE) + goto on_exit; + } + + on_exit: + (void) eina_iterator_unlock(iterator); +} + +/** + * @brief Lock the container of the iterator. + * + * @param iterator The iterator. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * If the container of the @p iterator permit it, it will be locked. + * If @p iterator is @c NULL or if a problem occurred, #EINA_FALSE is + * returned, otherwise #EINA_TRUE is returned. If the container + * is not lockable, it will return EINA_TRUE. + */ +EAPI Eina_Bool +eina_iterator_lock(Eina_Iterator *iterator) +{ + EINA_MAGIC_CHECK_ITERATOR(iterator); + EINA_SAFETY_ON_NULL_RETURN_VAL(iterator, EINA_FALSE); + + if (iterator->lock) + return iterator->lock(iterator); + return EINA_TRUE; +} + +/** + * @brief Unlock the container of the iterator. + * + * @param iterator The iterator. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * If the container of the @p iterator permit it and was previously + * locked, it will be unlocked. If @p iterator is @c NULL or if a + * problem occurred, #EINA_FALSE is returned, otherwise #EINA_TRUE + * is returned. If the container is not lockable, it will return + * EINA_TRUE. + */ +EAPI Eina_Bool +eina_iterator_unlock(Eina_Iterator *iterator) +{ + EINA_MAGIC_CHECK_ITERATOR(iterator); + EINA_SAFETY_ON_NULL_RETURN_VAL(iterator, EINA_FALSE); + + if (iterator->unlock) + return iterator->unlock(iterator); + return EINA_TRUE; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_lalloc.c b/tests/suite/ecore/src/lib/eina_lalloc.c new file mode 100644 index 0000000000..b1e62b7420 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_lalloc.c @@ -0,0 +1,158 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> + +#include "eina_config.h" +#include "eina_private.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_lalloc.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +struct _Eina_Lalloc +{ + void *data; + int num_allocated; + int num_elements; + int acc; + Eina_Lalloc_Alloc alloc_cb; + Eina_Lalloc_Free free_cb; +}; + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Lalloc_Group Lazy allocator + * + * @{ + */ + +EAPI Eina_Lalloc *eina_lalloc_new(void *data, + Eina_Lalloc_Alloc alloc_cb, + Eina_Lalloc_Free free_cb, + int num_init) +{ + Eina_Lalloc *a; + + EINA_SAFETY_ON_NULL_RETURN_VAL(alloc_cb, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(free_cb, NULL); + + a = calloc(1, sizeof(Eina_Lalloc)); + a->data = data; + a->alloc_cb = alloc_cb; + a->free_cb = free_cb; + if (num_init > 0) + { + a->num_allocated = num_init; + a->alloc_cb(a->data, a->num_allocated); + } + + return a; +} + +EAPI void eina_lalloc_free(Eina_Lalloc *a) +{ + EINA_SAFETY_ON_NULL_RETURN(a); + EINA_SAFETY_ON_NULL_RETURN(a->free_cb); + a->free_cb(a->data); + free(a); +} + +EAPI Eina_Bool eina_lalloc_element_add(Eina_Lalloc *a) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(a, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(a->alloc_cb, EINA_FALSE); + + if (a->num_elements == a->num_allocated) + { + if (a->alloc_cb(a->data, (1 << a->acc)) == EINA_TRUE) + { + a->num_allocated = (1 << a->acc); + a->acc++; + } + else + return EINA_FALSE; + } + + a->num_elements++; + + return EINA_TRUE; +} + +EAPI Eina_Bool eina_lalloc_elements_add(Eina_Lalloc *a, int num) +{ + int tmp; + + EINA_SAFETY_ON_NULL_RETURN_VAL(a, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(a->alloc_cb, EINA_FALSE); + + tmp = a->num_elements + num; + if (tmp > a->num_allocated) + { + int allocated; + int acc; + + allocated = a->num_allocated; + acc = a->acc; + + while (tmp > allocated) + { + allocated = (1 << acc); + acc++; + } + + if (a->alloc_cb(a->data, allocated) == EINA_TRUE) + { + a->num_allocated = allocated; + a->acc = acc; + } + else + return EINA_FALSE; + } + + a->num_elements += num; + + return EINA_TRUE; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_list.c b/tests/suite/ecore/src/lib/eina_list.c new file mode 100644 index 0000000000..e301476f27 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_list.c @@ -0,0 +1,2184 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Gustavo Sverzut Barbieri, Tilman Sauerbeck, + * Vincent Torri, Cedric Bail, Jorge Luis Zapata Muga, + * Corey Donohoe, Arnaud de Turckheim, Alexandre Becoulet + * + * 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 <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (C) 2004 ncn + * Copyright (C) 2006 Sebastian Dransfeld + * Copyright (C) 2007 Christopher Michael + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies of the Software and its Copyright notices. In addition publicly + * documented acknowledgment must be given that this software has been used if no + * source code of this software is made available publicly. This includes + * acknowledgments in either Copyright notices, Manuals, Publicity and Marketing + * documents or any documentation provided with any product containing this + * software. This License does not apply to any software that links to the + * libraries provided by this software (statically or dynamically), but only to + * the software provided. + + * Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice + * and it's intent. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** + * @page tutorial_list_page List Tutorial + * + * to be written... + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_error.h" +#include "eina_log.h" +#include "eina_mempool.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_list.h" + + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static const char EINA_MAGIC_LIST_STR[] = "Eina List"; +static const char EINA_MAGIC_LIST_ITERATOR_STR[] = "Eina List Iterator"; +static const char EINA_MAGIC_LIST_ACCESSOR_STR[] = "Eina List Accessor"; +static const char EINA_MAGIC_LIST_ACCOUNTING_STR[] = "Eina List Accounting"; + + +#define EINA_MAGIC_CHECK_LIST(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_LIST)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_LIST); \ + return __VA_ARGS__; \ + } \ + } while(0) + +#define EINA_MAGIC_CHECK_LIST_ITERATOR(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_LIST_ITERATOR)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_LIST_ITERATOR); \ + return __VA_ARGS__; \ + } \ + } while(0) + +#define EINA_MAGIC_CHECK_LIST_ACCESSOR(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_LIST_ACCESSOR)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_LIST_ACCESSOR); \ + return __VA_ARGS__; \ + } \ + } while(0) + +#define EINA_MAGIC_CHECK_LIST_ACCOUNTING(d) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_LIST_ACCOUNTING)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_LIST_ACCOUNTING); \ + return; \ + } \ + } while(0) + +#define EINA_LIST_SORT_STACK_SIZE 32 + +typedef struct _Eina_Iterator_List Eina_Iterator_List; +typedef struct _Eina_Accessor_List Eina_Accessor_List; + +struct _Eina_Iterator_List +{ + Eina_Iterator iterator; + + const Eina_List *head; + const Eina_List *current; + + EINA_MAGIC +}; + +struct _Eina_Accessor_List +{ + Eina_Accessor accessor; + + const Eina_List *head; + const Eina_List *current; + + unsigned int index; + + EINA_MAGIC +}; + +static Eina_Mempool *_eina_list_mp = NULL; +static Eina_Mempool *_eina_list_accounting_mp = NULL; +static int _eina_list_log_dom = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_list_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_list_log_dom, __VA_ARGS__) + +static inline Eina_List_Accounting * +_eina_list_mempool_accounting_new(__UNUSED__ Eina_List *list) +{ + Eina_List_Accounting *tmp; + + tmp = + eina_mempool_malloc(_eina_list_accounting_mp, + sizeof (Eina_List_Accounting)); + if (!tmp) + return NULL; + + EINA_MAGIC_SET(tmp, EINA_MAGIC_LIST_ACCOUNTING); + + return tmp; +} +static inline void +_eina_list_mempool_accounting_free(Eina_List_Accounting *accounting) +{ + EINA_MAGIC_CHECK_LIST_ACCOUNTING(accounting); + + EINA_MAGIC_SET(accounting, EINA_MAGIC_NONE); + eina_mempool_free(_eina_list_accounting_mp, accounting); +} + +static inline Eina_List * +_eina_list_mempool_list_new(__UNUSED__ Eina_List *list) +{ + Eina_List *tmp; + + tmp = eina_mempool_malloc(_eina_list_mp, sizeof (Eina_List)); + if (!tmp) + return NULL; + + EINA_MAGIC_SET(tmp, EINA_MAGIC_LIST); + + return tmp; +} +static inline void +_eina_list_mempool_list_free(Eina_List *list) +{ + EINA_MAGIC_CHECK_LIST(list); + + list->accounting->count--; + if (list->accounting->count == 0) + _eina_list_mempool_accounting_free(list->accounting); + + EINA_MAGIC_SET(list, EINA_MAGIC_NONE); + eina_mempool_free(_eina_list_mp, list); +} + +static Eina_List * +_eina_list_setup_accounting(Eina_List *list) +{ + EINA_MAGIC_CHECK_LIST(list, NULL); + + list->accounting = _eina_list_mempool_accounting_new(list); + if (!list->accounting) + goto on_error; + + list->accounting->last = list; + list->accounting->count = 1; + + return list; + +on_error: + _eina_list_mempool_list_free(list); + return NULL; +} + +static inline void +_eina_list_update_accounting(Eina_List *list, Eina_List *new_list) +{ + EINA_MAGIC_CHECK_LIST(list); + EINA_MAGIC_CHECK_LIST(new_list); + + list->accounting->count++; + new_list->accounting = list->accounting; +} + +#if 0 +static Eina_Mempool2 _eina_list_mempool = +{ + sizeof(Eina_List), + 320, + 0, NULL, NULL +}; +static Eina_Mempool2 _eina_list_accounting_mempool = +{ + sizeof(Eina_List_Accounting), + 80, + 0, NULL, NULL +}; +#endif + +static Eina_Bool +eina_list_iterator_next(Eina_Iterator_List *it, void **data) +{ + EINA_MAGIC_CHECK_LIST_ITERATOR(it, EINA_FALSE); + + if (!it->current) + return EINA_FALSE; + + *data = eina_list_data_get(it->current); + + it->current = eina_list_next(it->current); + + return EINA_TRUE; +} + +static Eina_Bool +eina_list_iterator_prev(Eina_Iterator_List *it, void **data) +{ + EINA_MAGIC_CHECK_LIST_ITERATOR(it, EINA_FALSE); + + if (!it->current) + return EINA_FALSE; + + *data = eina_list_data_get(it->current); + + it->current = eina_list_prev(it->current); + + return EINA_TRUE; +} + +static Eina_List * +eina_list_iterator_get_container(Eina_Iterator_List *it) +{ + EINA_MAGIC_CHECK_LIST_ITERATOR(it, NULL); + + return (Eina_List *)it->head; +} + +static void +eina_list_iterator_free(Eina_Iterator_List *it) +{ + EINA_MAGIC_CHECK_LIST_ITERATOR(it); + + MAGIC_FREE(it); +} + +static Eina_Bool +eina_list_accessor_get_at(Eina_Accessor_List *it, unsigned int idx, void **data) +{ + const Eina_List *over; + unsigned int middle; + unsigned int i; + + EINA_MAGIC_CHECK_LIST_ACCESSOR(it, EINA_FALSE); + + if (idx >= eina_list_count(it->head)) + return EINA_FALSE; + + if (it->index == idx) + over = it->current; + else if (idx > it->index) + { + /* After current position. */ + middle = ((eina_list_count(it->head) - it->index) >> 1) + it->index; + + if (idx > middle) + /* Go backward from the end. */ + for (i = eina_list_count(it->head) - 1, + over = eina_list_last(it->head); + i > idx && over; + --i, over = eina_list_prev(over)) + ; + else + /* Go forward from current. */ + for (i = it->index, over = it->current; + i < idx && over; + ++i, over = eina_list_next(over)) + ; + } + else + { + /* Before current position. */ + middle = it->index >> 1; + + if (idx > middle) + /* Go backward from current. */ + for (i = it->index, over = it->current; + i > idx && over; + --i, over = eina_list_prev(over)) + ; + else + /* Go forward from start. */ + for (i = 0, over = it->head; + i < idx && over; + ++i, over = eina_list_next(over)) + ; + } + + if (!over) + return EINA_FALSE; + + it->current = over; + it->index = idx; + + *data = eina_list_data_get(it->current); + return EINA_TRUE; +} + +static Eina_List * +eina_list_accessor_get_container(Eina_Accessor_List *it) +{ + EINA_MAGIC_CHECK_LIST_ACCESSOR(it, NULL); + + return (Eina_List *)it->head; +} + +static void +eina_list_accessor_free(Eina_Accessor_List *it) +{ + EINA_MAGIC_CHECK_LIST_ACCESSOR(it); + + MAGIC_FREE(it); +} + +static Eina_List * +eina_list_sort_rebuild_prev(Eina_List *list) +{ + Eina_List *prev = NULL; + + EINA_MAGIC_CHECK_LIST(list, NULL); + + for (; list; list = list->next) + { + list->prev = prev; + prev = list; + } + + return prev; +} + +static Eina_List * +eina_list_sort_merge(Eina_List *a, Eina_List *b, Eina_Compare_Cb func) +{ + Eina_List *first, *last; + + if (func(a->data, b->data) < 0) + a = (last = first = a)->next; + else + b = (last = first = b)->next; + + while (a && b) + if (func(a->data, b->data) < 0) + a = (last = last->next = a)->next; + else + b = (last = last->next = b)->next; + + last->next = a ? a : b; + + return first; +} + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the list module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the list module of Eina. It is called by + * eina_init(). + * + * This function creates mempool to speed up list node and accounting + * management, using EINA_MEMPOOL environment variable if it is set to + * choose the memory pool type to use. + * + * @see eina_init() + */ +Eina_Bool +eina_list_init(void) +{ + const char *choice, *tmp; + + _eina_list_log_dom = eina_log_domain_register("eina_list", + EINA_LOG_COLOR_DEFAULT); + if (_eina_list_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_list"); + 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_list_mp = eina_mempool_add + (choice, "list", NULL, sizeof(Eina_List), 320); + if (!_eina_list_mp) + { + ERR("ERROR: Mempool for list cannot be allocated in list init."); + goto on_init_fail; + } + + _eina_list_accounting_mp = eina_mempool_add + (choice, "list_accounting", NULL, sizeof(Eina_List_Accounting), 80); + if (!_eina_list_accounting_mp) + { + ERR( + "ERROR: Mempool for list accounting cannot be allocated in list init."); + eina_mempool_del(_eina_list_mp); + goto on_init_fail; + } + +#define EMS(n) eina_magic_string_static_set(n, n ## _STR) + EMS(EINA_MAGIC_LIST); + EMS(EINA_MAGIC_LIST_ITERATOR); + EMS(EINA_MAGIC_LIST_ACCESSOR); + EMS(EINA_MAGIC_LIST_ACCOUNTING); +#undef EMS + + return EINA_TRUE; + +on_init_fail: + eina_log_domain_unregister(_eina_list_log_dom); + _eina_list_log_dom = -1; + return EINA_FALSE; +} + +/** + * @internal + * @brief Shut down the list module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the list module set up by + * eina_list_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_list_shutdown(void) +{ + eina_mempool_del(_eina_list_accounting_mp); + eina_mempool_del(_eina_list_mp); + + eina_log_domain_unregister(_eina_list_log_dom); + _eina_list_log_dom = -1; + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_List_Group List + * + * @brief These functions provide double linked list management. + * + * For more information, you can look at the @ref tutorial_list_page. + * + * @{ + */ + +/** + * @brief Append the given data to the given linked list. + * + * @param list The given list. + * @param data The data to append. + * @return A list pointer. + * + * This function appends @p data to @p list. If @p list is @c NULL, a + * new list is returned. On success, a new list pointer that should be + * used in place of the one given to this function is + * returned. Otherwise, the old pointer is returned. + * + * The following example code demonstrates how to ensure that the + * given data has been successfully appended. + * + * @code + * Eina_List *list = NULL; + * extern void *my_data; + * + * list = eina_list_append(list, my_data); + * if (eina_error_get()) + * { + * fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n"); + * exit(-1); + * } + * @endcode + */ +EAPI Eina_List * +eina_list_append(Eina_List *list, const void *data) +{ + Eina_List *l, *new_l; + + eina_error_set(0); + new_l = _eina_list_mempool_list_new(list); + if (!new_l) + return list; + + new_l->next = NULL; + new_l->data = (void *)data; + if (!list) + { + new_l->prev = NULL; + return _eina_list_setup_accounting(new_l); + } + + EINA_MAGIC_CHECK_LIST(list, NULL); + + l = list->accounting->last; + list->accounting->last = new_l; + + l->next = new_l; + new_l->prev = l; + + _eina_list_update_accounting(list, new_l); + return list; +} + +/** + * @brief Prepends the given data to the given linked list. + * + * @param list The given list. + * @param data The data to prepend. + * @return A list pointer. + * + * This function prepends @p data to @p list. If @p list is @c NULL, a + * new list is returned. On success, a new list pointer that should be + * used in place of the one given to this function is + * returned. Otherwise, the old pointer is returned. + * + * The following example code demonstrates how to ensure that the + * given data has been successfully prepended. + * + * Example: + * @code + * Eina_List *list = NULL; + * extern void *my_data; + * + * list = eina_list_prepend(list, my_data); + * if (eina_error_get()) + * { + * fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n"); + * exit(-1); + * } + * @endcode + */ +EAPI Eina_List * +eina_list_prepend(Eina_List *list, const void *data) +{ + Eina_List *new_l; + + eina_error_set(0); + new_l = _eina_list_mempool_list_new(list); + if (!new_l) + return list; + + new_l->prev = NULL; + new_l->next = list; + new_l->data = (void *)data; + + if (!list) + return _eina_list_setup_accounting(new_l); + + EINA_MAGIC_CHECK_LIST(list, NULL); + + list->prev = new_l; + + _eina_list_update_accounting(list, new_l); + + return new_l; +} + +/** + * @brief Insert the given data into the given linked list after the specified data. + * + * @param list The given linked list. + * @param data The data to insert. + * @param relative The data to insert after. + * @return A list pointer. + * + * This function inserts @p data to @p list after @p relative. If + * @p relative is not in the list, @p data is appended to the end of + * the list. If @p list is @c NULL, a new list is returned. If there + * are multiple instances of @p relative in the list, @p data is + * inserted after the first instance.On success, a new list pointer + * that should be used in place of the one given to this function is + * returned. Otherwise, the old pointer is returned. + * + * The following example code demonstrates how to ensure that the + * given data has been successfully inserted. + * + * @code + * Eina_List *list = NULL; + * extern void *my_data; + * extern void *relative_member; + * + * list = eina_list_append(list, relative_member); + * if (eina_error_get()) + * { + * fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n"); + * exit(-1); + * } + * list = eina_list_append_relative(list, my_data, relative_member); + * if (eina_error_get()) + * { + * fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n"); + * exit(-1); + * } + * @endcode + */ +EAPI Eina_List * +eina_list_append_relative(Eina_List *list, + const void *data, + const void *relative) +{ + Eina_List *l; + void *list_data; + + if (list) + EINA_MAGIC_CHECK_LIST(list, NULL); + + EINA_LIST_FOREACH(list, l, list_data) + { + if (list_data == relative) + return eina_list_append_relative_list(list, data, l); + } + + return eina_list_append(list, data); +} + +/** + * @brief Append a list node to a linked list after the specified member + * + * @param list The given linked list. + * @param data The data to insert. + * @param relative The list node to insert after. + * @return A list pointer. + * + * This function inserts @p data to @p list after the list node + * @p relative. If @p list or @p relative are @c NULL, @p data is just + * appended to @p list using eina_list_append(). If @p list is + * @c NULL, a new list is returned. If there are multiple instances + * of @p relative in the list, @p data is inserted after the first + * instance. On success, a new list pointer that should be used in + * place of the one given to this function is returned. Otherwise, the + * old pointer is returned. + */ +EAPI Eina_List * +eina_list_append_relative_list(Eina_List *list, + const void *data, + Eina_List *relative) +{ + Eina_List *new_l; + + if ((!list) || (!relative)) + return eina_list_append(list, data); + + eina_error_set(0); + new_l = _eina_list_mempool_list_new(list); + if (!new_l) + return list; + + EINA_MAGIC_CHECK_LIST(relative, NULL); + new_l->next = relative->next; + new_l->data = (void *)data; + + if (relative->next) + relative->next->prev = new_l; + + relative->next = new_l; + new_l->prev = relative; + + _eina_list_update_accounting(list, new_l); + + if (!new_l->next) + new_l->accounting->last = new_l; + + return list; +} + +/** + * @brief Prepend a data pointer to a linked list before the specified member + * + * @param list The given linked list. + * @param data The data to insert. + * @param relative The data to insert before. + * @return A list pointer. + * + * This function inserts @p data to @p list before @p relative. If + * @p relative is not in the list, @p data is prepended to the list + * with eina_list_prepend(). If @p list is @c NULL, a new list is + * returned. If there are multiple instances of @p relative in the + * list, @p data is inserted before the first instance. On success, a + * new list pointer that should be used in place of the one given to + * this function is returned. Otherwise, the old pointer is returned. + * + * The following code example demonstrates how to ensure that the + * given data has been successfully inserted. + * + * @code + * Eina_List *list = NULL; + * extern void *my_data; + * extern void *relative_member; + * + * list = eina_list_append(list, relative_member); + * if (eina_error_get_error()) + * { + * fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n"); + * exit(-1); + * } + * list = eina_list_prepend_relative(list, my_data, relative_member); + * if (eina_error_get()) + * { + * fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n"); + * exit(-1); + * } + * @endcode + */ +EAPI Eina_List * +eina_list_prepend_relative(Eina_List *list, + const void *data, + const void *relative) +{ + Eina_List *l; + void *list_data; + + if (list) + EINA_MAGIC_CHECK_LIST(list, NULL); + + EINA_LIST_FOREACH(list, l, list_data) + { + if (list_data == relative) + return eina_list_prepend_relative_list(list, data, l); + } + return eina_list_prepend(list, data); +} + +/** + * @brief Prepend a list node to a linked list before the specified member + * + * @param list The given linked list. + * @param data The data to insert. + * @param relative The list node to insert before. + * @return A list pointer. + * + * This function inserts @p data to @p list before the list node + * @p relative. If @p list or @p relative are @c NULL, @p data is just + * prepended to @p list using eina_list_prepend(). If @p list is + * @c NULL, a new list is returned. If there are multiple instances + * of @p relative in the list, @p data is inserted before the first + * instance. On success, a new list pointer that should be used in + * place of the one given to this function is returned. Otherwise, the + * old pointer is returned. + */ +EAPI Eina_List * +eina_list_prepend_relative_list(Eina_List *list, + const void *data, + Eina_List *relative) +{ + Eina_List *new_l; + + if ((!list) || (!relative)) + return eina_list_prepend(list, data); + + eina_error_set(0); + new_l = _eina_list_mempool_list_new(list); + if (!new_l) + return list; + + EINA_MAGIC_CHECK_LIST(relative, NULL); + + new_l->prev = relative->prev; + new_l->next = relative; + new_l->data = (void *)data; + + if (relative->prev) + relative->prev->next = new_l; + + relative->prev = new_l; + + _eina_list_update_accounting(list, new_l); + + if (new_l->prev) + return list; + + return new_l; +} + +/** + * @brief Insert a new node into a sorted list. + * + * @param list The given linked list, @b must be sorted. + * @param func The function called for the sort. + * @param data The data to insert sorted. + * @return A list pointer. + * + * This function inserts values into a linked list assuming it was + * sorted and the result will be sorted. If @p list is @c NULLL, a new + * list is returned. On success, a new list pointer that should be + * used in place of the one given to this function is + * returned. Otherwise, the old pointer is returned. See eina_error_get(). + * + * @note O(log2(n)) comparisons (calls to @p func) average/worst case + * performance as it uses eina_list_search_sorted_near_list() and thus + * is bounded to that. As said in eina_list_search_sorted_near_list(), + * lists do not have O(1) access time, so walking to the correct node + * can be costly, consider worst case to be almost O(n) pointer + * dereference (list walk). + */ +EAPI Eina_List * +eina_list_sorted_insert(Eina_List *list, Eina_Compare_Cb func, const void *data) +{ + Eina_List *lnear; + int cmp; + + if (!list) + return eina_list_append(NULL, data); + + lnear = eina_list_search_sorted_near_list(list, func, data, &cmp); + if (cmp < 0) + return eina_list_append_relative_list(list, data, lnear); + else + return eina_list_prepend_relative_list(list, data, lnear); +} + +/** + * @brief Remove the first instance of the specified data from the given list. + * + * @param list The given list. + * @param data The specified data. + * @return A list pointer. + * + * This function removes the first instance of @p data from + * @p list. If the specified data is not in the given list (tihis + * include the case where @p data is @c NULL), nothing is done. If + * @p list is @c NULL, @c NULL is returned, otherwise a new list + * pointer that should be used in place of the one passed to this + * function. + */ +EAPI Eina_List * +eina_list_remove(Eina_List *list, const void *data) +{ + Eina_List *l; + + if (list) + EINA_MAGIC_CHECK_LIST(list, NULL); + + l = eina_list_data_find_list(list, data); + return eina_list_remove_list(list, l); +} + +/** + * @brief Remove the specified data. + * + * @param list The given linked list. + * @param remove_list The list node which is to be removed. + * @return A list pointer. + * + * This function removes the list node @p remove_list from @p list and + * frees the list node structure @p remove_list. If @p list is + * @c NULL, this function returns @c NULL. If @p remove_list is + * @c NULL, it returns @p list, otherwise, a new list pointer that + * should be used in place of the one passed to this function. + * + * The following code gives an example (notice we use EINA_LIST_FOREACH + * instead of EINA_LIST_FOREACH_SAFE because we stop the loop after + * removing the current node). + * + * @code + * extern Eina_List *list; + * Eina_List *l; + * extern void *my_data; + * void *data + * + * EINA_LIST_FOREACH(list, l, data) + * { + * if (data == my_data) + * { + * list = eina_list_remove_list(list, l); + * break; + * } + * } + * @endcode + */ +EAPI Eina_List * +eina_list_remove_list(Eina_List *list, Eina_List *remove_list) +{ + Eina_List *return_l; + + if (!list) + return NULL; + + if (!remove_list) + return list; + + EINA_MAGIC_CHECK_LIST(remove_list, NULL); + + if (remove_list->next) + remove_list->next->prev = remove_list->prev; + + if (remove_list->prev) + { + remove_list->prev->next = remove_list->next; + return_l = list; + } + else + return_l = remove_list->next; + + if (remove_list == remove_list->accounting->last) + { + EINA_MAGIC_CHECK_LIST(list, NULL); + list->accounting->last = remove_list->prev; + } + + _eina_list_mempool_list_free(remove_list); + return return_l; +} + +/** + * @brief Free an entire list and all the nodes, ignoring the data contained. + + * @param list The list to free + * @return A NULL pointer + * + * This function frees all the nodes of @p list. It does not free the + * data of the nodes. To free them, use #EINA_LIST_FREE. + */ +EAPI Eina_List * +eina_list_free(Eina_List *list) +{ + Eina_List *l, *free_l; + + if (!list) + return NULL; + + EINA_MAGIC_CHECK_LIST(list, NULL); + + for (l = list; l; ) + { + free_l = l; + l = l->next; + + _eina_list_mempool_list_free(free_l); + } + + return NULL; +} + +/** + * @brief Move the specified data to the head of the list. + * + * @param list The list handle to move the data. + * @param move_list The list node to move. + * @return A new list handle to replace the old one + * + * This function move @p move_list to the front of @p list. If list is + * @c NULL, @c NULL is returned. If @p move_list is @c NULL, + * @p list is returned. Otherwise, a new list pointer that should be + * used in place of the one passed to this function. + * + * Example: + * @code + * extern Eina_List *list; + * Eina_List *l; + * extern void *my_data; + * void *data; + * + * EINA_LIST_FOREACH(list, l, data) + * { + * if (data == my_data) + * { + * list = eina_list_promote_list(list, l); + * break; + * } + * } + * @endcode + */ +EAPI Eina_List * +eina_list_promote_list(Eina_List *list, Eina_List *move_list) +{ + if (!list) + return NULL; + + if (!move_list) + { + return list; /* Promoting head to be head. */ + + } + + if (move_list == list) + return list; + + if (move_list->next == list) + return move_list; + + EINA_MAGIC_CHECK_LIST(list, NULL); + EINA_MAGIC_CHECK_LIST(move_list, NULL); + + /* Remove the promoted item from the list. */ + if (!move_list->prev) + move_list->next->prev = NULL; + else + { + move_list->prev->next = move_list->next; + if (move_list == list->accounting->last) + list->accounting->last = move_list->prev; + else + move_list->next->prev = move_list->prev; + } + + /* Add the promoted item in the list. */ + move_list->next = list; + move_list->prev = list->prev; + list->prev = move_list; + if (move_list->prev) + move_list->prev->next = move_list; + + return move_list; +} + +/** + * @brief Move the specified data to the tail of the list. + * + * @param list The list handle to move the data. + * @param move_list The list node to move. + * @return A new list handle to replace the old one + * + * This function move @p move_list to the back of @p list. If list is + * @c NULL, @c NULL is returned. If @p move_list is @c NULL, + * @p list is returned. Otherwise, a new list pointer that should be + * used in place of the one passed to this function. + * + * Example: + * @code + * extern Eina_List *list; + * Eina_List *l; + * extern void *my_data; + * void *data; + * + * EINA_LIST_FOREACH(list, l, data) + * { + * if (data == my_data) + * { + * list = eina_list_demote_list(list, l); + * break; + * } + * } + * @endcode + */ +EAPI Eina_List * +eina_list_demote_list(Eina_List *list, Eina_List *move_list) +{ + if (!list) + return NULL; + + if (!move_list) + { + return list; /* Demoting tail to be tail. */ + + } + + if (move_list == list->accounting->last) + return list; + + EINA_MAGIC_CHECK_LIST(list, NULL); + EINA_MAGIC_CHECK_LIST(move_list, NULL); + + /* Update pointer list if necessary. */ + if (list == move_list) + { + list = move_list->next; /* Remove the demoted item from the list. */ + + } + + if (move_list->prev) + move_list->prev->next = move_list->next; + + move_list->next->prev = move_list->prev; + /* Add the demoted item in the list. */ + move_list->prev = list->accounting->last; + move_list->prev->next = move_list; + move_list->next = NULL; + list->accounting->last = move_list; + + return list; +} + +/** + * @brief Find a member of a list and return the member. + * + * @param list The list to search for a data. + * @param data The data pointer to find in the list. + * @return The found member data pointer if foun, @c NULL otherwise. + * + * This function searches in @p list from beginning to end for the + * first member whose data pointer is @p data. If it is found, @p data + * will be returned, otherwise NULL will be returned. + * + * Example: + * @code + * extern Eina_List *list; + * extern void *my_data; + * + * if (eina_list_data_find(list, my_data) == my_data) + * { + * printf("Found member %p\n", my_data); + * } + * @endcode + */ +EAPI void * +eina_list_data_find(const Eina_List *list, const void *data) +{ + if (eina_list_data_find_list(list, data)) + return (void *)data; + + return NULL; +} + +/** + * @brief Find a member of a list and return the list node containing that member. + * + * @param list The list to search for data. + * @param data The data pointer to find in the list. + * @return The found members list node on success, @c NULL otherwise. + * + * This function searches in @p list from beginning to end for the + * first member whose data pointer is @p data. If it is found, the + * list node containing the specified member is returned, otherwise + * @c NULL is returned. + */ +EAPI Eina_List * +eina_list_data_find_list(const Eina_List *list, const void *data) +{ + const Eina_List *l; + void *list_data; + + if (list) + EINA_MAGIC_CHECK_LIST(list, NULL); + + EINA_LIST_FOREACH(list, l, list_data) + { + if (list_data == data) + return (Eina_List *)l; + } + + return NULL; +} + +/** + * @brief Get the nth member's data pointer in a list. + * + * @param list The list to get the specified member number from. + * @param n The number of the element (0 being the first). + * @return The data pointer stored in the specified element. + * + * This function returns the data pointer of element number @p n, in + * the @p list. The first element in the array is element number 0. If + * the element number @p n does not exist, @c NULL is + * returned. Otherwise, the data of the found element is returned. + */ +EAPI void * +eina_list_nth(const Eina_List *list, unsigned int n) +{ + Eina_List *l; + + l = eina_list_nth_list(list, n); + return l ? l->data : NULL; +} + +/** + * @brief Get the nth member's list node in a list. + * + * @param list The list to get the specfied member number from. + * @param n The number of the element (0 being the first). + * @return The list node stored in the numbered element. + * + * This function returns the list node of element number @p n, in + * @ list. The first element in the array is element number 0. If the + * element number @p n does not exist or @p list is @c NULL or @p n is + * greater than the count of elements in @p list minus 1, @c NULL is + * returned. Otherwise the list node stored in the numbered element is + * returned. + */ +EAPI Eina_List * +eina_list_nth_list(const Eina_List *list, unsigned int n) +{ + const Eina_List *l; + unsigned int i; + + if (list) + EINA_MAGIC_CHECK_LIST(list, NULL); + + /* check for non-existing nodes */ + if ((!list) || (n > (list->accounting->count - 1))) + return NULL; + + /* if the node is in the 2nd half of the list, search from the end + * else, search from the beginning. + */ + if (n > (list->accounting->count / 2)) + for (i = list->accounting->count - 1, + l = list->accounting->last; + l; + l = l->prev, i--) + { + if (i == n) + return (Eina_List *)l; + } + else + for (i = 0, l = list; l; l = l->next, i++) + { + if (i == n) + return (Eina_List *)l; + } + + abort(); +} + +/** + * @brief Reverse all the elements in the list. + * + * @param list The list to reverse. + * @return The list head after it has been reversed. + * + * This function reverses the order of all elements in @p list, so the + * last member is now first, and so on. If @p list is @c NULL, this + * functon returns @c NULL. + * + * @note @b in-place: this will change the given list, so you should + * now point to the new list head that is returned by this function. + * + * @see eina_list_reverse_clone() + * @see eina_list_iterator_reversed_new() + */ +EAPI Eina_List * +eina_list_reverse(Eina_List *list) +{ + Eina_List *l1, *l2; + + if (!list) + return NULL; + + EINA_MAGIC_CHECK_LIST(list, NULL); + + l1 = list; + l2 = list->accounting->last; + while (l1 != l2) + { + void *data; + + data = l1->data; + l1->data = l2->data; + l2->data = data; + l1 = l1->next; + if (l1 == l2) + break; + + l2 = l2->prev; + } + + return list; +} + +/** + * @brief Clone (copy) all the elements in the list in reverse order. + * + * @param list The list to reverse. + * @return The new list that has been reversed. + * + * This function reverses the order of all elements in @p list, so the + * last member is now first, and so on. If @p list is @c NULL, this + * functon returns @c NULL. This returns a copy of the given list. + * + * @note @b copy: this will copy the list and you should then + * eina_list_free() when it is not required anymore. + * + * @see eina_list_reverse() + * @see eina_list_clone() + */ +EAPI Eina_List * +eina_list_reverse_clone(const Eina_List *list) +{ + const Eina_List *l; + Eina_List *lclone; + void *data; + + if (!list) + return NULL; + + EINA_MAGIC_CHECK_LIST(list, NULL); + + lclone = NULL; + EINA_LIST_FOREACH(list, l, data) + lclone = eina_list_prepend(lclone, data); + + return lclone; +} + +/** + * @brief Clone (copy) all the elements in the list in exact order. + * + * @param list The list to clone. + * @return The new list that has been cloned. + * + * This function clone in order of all elements in @p list. If @p list + * is @c NULL, this functon returns @c NULL. This returns a copy of + * the given list. + * + * @note @b copy: this will copy the list and you should then + * eina_list_free() when it is not required anymore. + * + * @see eina_list_reverse_clone() + */ +EAPI Eina_List * +eina_list_clone(const Eina_List *list) +{ + const Eina_List *l; + Eina_List *lclone; + void *data; + + if (!list) + return NULL; + + EINA_MAGIC_CHECK_LIST(list, NULL); + + lclone = NULL; + EINA_LIST_FOREACH(list, l, data) + lclone = eina_list_append(lclone, data); + + return lclone; +} + +/** + * @brief Sort a list according to the ordering func will return. + * + * @param list The list handle to sort. + * @param size The length of the list to sort. + * @param func A function pointer that can handle comparing the list data + * nodes. + * @return the new head of list. + * + * This function sorts @p list. @p size if the number of the first + * element to sort. If @p size is 0 or greater than the number of + * elements in @p list, all the elements are sorted. @p func is used to + * compare two elements of @p list. If @p list or @p func are @c NULL, + * this function returns @c NULL. + * + * @note @b in-place: this will change the given list, so you should + * now point to the new list head that is returned by this function. + * + * @note worst case is O(n * log2(n)) comparisons (calls to func()), + * O(n) comparisons average case. That means that for 1,000,000 list + * elements, sort will usually do 1,000,000 comparisons, but may do up + * to 20,000,000. + * + * Example: + * @code + * int + * sort_cb(const void *d1, const void *d2) + * { + * const char *txt = NULL; + * const char *txt2 = NULL; + * + * if(!d1) return(1); + * if(!d2) return(-1); + * + * return(strcmp((const char*)d1, (const char*)d2)); + * } + * extern Eina_List *list; + * + * list = eina_list_sort(list, eina_list_count(list), sort_cb); + * @endcode + */ +EAPI Eina_List * +eina_list_sort(Eina_List *list, unsigned int size, Eina_Compare_Cb func) +{ + unsigned int i = 0; + unsigned int n = 0; + Eina_List *tail = list; + Eina_List *unsort = NULL; + Eina_List *stack[EINA_LIST_SORT_STACK_SIZE]; + + EINA_SAFETY_ON_NULL_RETURN_VAL(func, list); + if (!list) + return NULL; + + EINA_MAGIC_CHECK_LIST(list, NULL); + + /* if the caller specified an invalid size, sort the whole list */ + if ((size == 0) || + (size > list->accounting->count)) + size = list->accounting->count; + + if (size != list->accounting->count) + { + unsort = eina_list_nth_list(list, size); + if (unsort) + unsort->prev->next = NULL; + } + + while (tail) + { + unsigned int idx, tmp; + + Eina_List *a = tail; + Eina_List *b = tail->next; + + if (!b) + { + stack[i++] = a; + break; + } + + tail = b->next; + + if (func(a->data, b->data) < 0) + ((stack[i++] = a)->next = b)->next = 0; + else + ((stack[i++] = b)->next = a)->next = 0; + + tmp = n++; + for (idx = n ^ tmp; idx &= idx - 1; i--) + stack[i - 2] = eina_list_sort_merge(stack[i - 2], stack[i - 1], func); + } + + while (i-- > 1) + stack[i - 1] = eina_list_sort_merge(stack[i - 1], stack[i], func); + + list = stack[0]; + tail = eina_list_sort_rebuild_prev(list); + + if (unsort) + { + tail->next = unsort; + unsort->prev = tail; + } + else + list->accounting->last = tail; + + return list; +} + +/** + * @brief Merge two list. + * + * @param left Head list to merge. + * @param right Tail list to merge. + * @return A new merged list. + * + * This function put right at the end of left and return the head. + * + * Both left and right does not exist anymore after the merge. + * + * @note merge cost is O(n), being @b n the size of the smallest + * list. This is due the need to fix accounting of that segment, + * making count and last access O(1). + */ +EAPI Eina_List * +eina_list_merge(Eina_List *left, Eina_List *right) +{ + unsigned int n_left, n_right; + + if (!left) + return right; + + if (!right) + return left; + + left->accounting->last->next = right; + right->prev = left->accounting->last; + + n_left = left->accounting->count; + n_right = right->accounting->count; + + if (n_left >= n_right) + { + Eina_List *itr = right; + left->accounting->last = right->accounting->last; + left->accounting->count += n_right; + + _eina_list_mempool_accounting_free(right->accounting); + + do + { + itr->accounting = left->accounting; + itr = itr->next; + } + while (itr); + } + else + { + Eina_List *itr = left->accounting->last; + right->accounting->count += n_left; + + _eina_list_mempool_accounting_free(left->accounting); + + do + { + itr->accounting = right->accounting; + itr = itr->prev; + } + while (itr); + } + + return left; +} + + +/** + * @brief Split a list into 2 lists. + * + * @param list List to split. + * @param relative The list will be split after @p relative. + * @param right The head of the new right list. + * @return The new left list + * + * This function split @p list into two lists ( left and right ) after the node @p relative. @p Relative + * will become the last node of the left list. If @p list or @p right are NULL list is returns. + * If @p relative is NULL right is set to @p list and NULL is returns. + * If @p relative is the last node of @p list list is returns and @p right is set to NULL. + * + * list does not exist anymore after the split. + * + */ +EAPI Eina_List * +eina_list_split_list(Eina_List *list, Eina_List *relative, Eina_List **right) +{ + Eina_List *next; + Eina_List *itr; + + if(!right) + return list; + + *right = NULL; + + if (!list) + return NULL; + + if (!relative) + { + *right = list; + return NULL; + } + + if (relative == eina_list_last(list)) + return list; + + next = eina_list_next(relative); + next->prev = NULL; + next->accounting = _eina_list_mempool_accounting_new(next); + next->accounting->last = list->accounting->last; + *right = next; + + itr = next; + do + { + itr->accounting = next->accounting; + next->accounting->count++; + itr = itr->next; + } + while (itr); + + relative->next = NULL; + list->accounting->last = relative; + list->accounting->count = list->accounting->count - next->accounting->count; + + return list; +} + +/** + * @brief Merge two sorted list according to the ordering func will return. + * + * @param left First list to merge. + * @param right Second list to merge. + * @param func A function pointer that can handle comparing the list data + * nodes. + * @return A new sorted list. + * + * This function compare the head of @p left and @p right, and choose the + * smallest one to be head of the returned list. It will continue this process + * for all entry of both list. + * + * Both left and right does not exist anymore after the merge. + * If @p func is NULL, it will return NULL. + * + * Example: + * @code + * int + * sort_cb(void *d1, void *d2) + * { + * const char *txt = NULL; + * const char *txt2 = NULL; + * + * if(!d1) return(1); + * if(!d2) return(-1); + * + * return(strcmp((const char*)d1, (const char*)d2)); + * } + * extern Eina_List *sorted1; + * extern Eina_List *sorted2; + * + * list = eina_list_sorted_merge(sorted1, sorted2, sort_cb); + * @endcode + */ +EAPI Eina_List * +eina_list_sorted_merge(Eina_List *left, Eina_List *right, Eina_Compare_Cb func) +{ + Eina_List *ret; + Eina_List *current; + + EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL); + + if (!left) + return right; + + if (!right) + return left; + + if (func(left->data, right->data) < 0) + { + ret = left; + current = left; + left = left->next; + ret->accounting->count += right->accounting->count; + + _eina_list_mempool_accounting_free(right->accounting); + } + else + { + ret = right; + current = right; + right = right->next; + ret->accounting->count += left->accounting->count; + + _eina_list_mempool_accounting_free(left->accounting); + } + + while (left && right) + { + if (func(left->data, right->data) < 0) + { + current->next = left; + left->prev = current; + left = left->next; + } + else + { + current->next = right; + right->prev = current; + right = right->next; + } + + current = current->next; + current->accounting = ret->accounting; + } + + if (left) + { + current->next = left; + left->prev = current; + current->accounting = ret->accounting; + } + + if (right) + { + current->next = right; + right->prev = current; + current->accounting = ret->accounting; + } + + while (current->next) + { + current = current->next; + current->accounting = ret->accounting; + } + + ret->accounting->last = current; + + return ret; +} + +/** + * @brief Returns node nearest to data is in the sorted list. + * + * @param list The list to search for data, @b must be sorted. + * @param func A function pointer that can handle comparing the list data nodes. + * @param data reference value to search. + * @param result_cmp if provided returns the result of + * func(node->data, data) node being the last (returned) node. If node + * was found (exact match), then it is 0. If returned node is smaller + * than requested data, it is less than 0 and if it's bigger it's + * greater than 0. It is the last value returned by func(). + * @return the nearest node, NULL if not found. + * + * This can be used to check if some value is inside the list and get + * the nearest container node in this case. It should be used when list is + * known to be sorted as it will do binary search for results. + * + * Example: imagine user gives a string, you check if it's in the list + * before duplicating its contents, otherwise you want to insert it + * sorted. In this case you get the result of this function and either + * append or prepend the value. + * + * @note O(log2(n)) average/worst case performance, for 1,000,000 + * elements it will do a maximum of 20 comparisons. This is much + * faster than the 1,000,000 comparisons made naively walking the list + * from head to tail, so depending on the number of searches and + * insertions, it may be worth to eina_list_sort() the list and do the + * searches later. As lists do not have O(1) access time, walking to + * the correct node can be costly, consider worst case to be almost + * O(n) pointer dereference (list walk). + * + * @see eina_list_search_sorted_list() + * @see eina_list_sort() + * @see eina_list_sorted_merge() + */ +EAPI Eina_List * +eina_list_search_sorted_near_list(const Eina_List *list, + Eina_Compare_Cb func, + const void *data, + int *result_cmp) +{ + const Eina_List *ct; + unsigned int inf, sup, cur; + int cmp; + + if (!list) + { + if (result_cmp) + *result_cmp = 0; + + return NULL; + } + + if (list->accounting->count == 1) + { + if (result_cmp) + *result_cmp = func(list->data, data); + + return (Eina_List *)list; + } + + /* list walk is expensive, do quick check: tail */ + ct = list->accounting->last; + cmp = func(ct->data, data); + if (cmp <= 0) + goto end; + + /* list walk is expensive, do quick check: head */ + ct = list; + cmp = func(ct->data, data); + if (cmp >= 0) + goto end; + + /* inclusive bounds */ + inf = 1; + sup = list->accounting->count - 2; + cur = 1; + ct = list->next; + + /* no loop, just compare if comparison value is important to caller */ + if (inf > sup) + { + if (result_cmp) + cmp = func(ct->data, data); + + goto end; + } + + while (inf <= sup) + { + unsigned int tmp = cur; + cur = inf + ((sup - inf) >> 1); + if (tmp < cur) + for (; tmp != cur; tmp++, ct = ct->next) ; + else if (tmp > cur) + for (; tmp != cur; tmp--, ct = ct->prev) ; + + cmp = func(ct->data, data); + if (cmp == 0) + break; + else if (cmp < 0) + inf = cur + 1; + else if (cmp > 0) + { + if (cur > 0) + sup = cur - 1; + else + break; + } + else + break; + } + +end: + if (result_cmp) + *result_cmp = cmp; + + return (Eina_List *)ct; +} + +/** + * @brief Returns node if data is in the sorted list. + * + * @param list The list to search for data, @b must be sorted. + * @param func A function pointer that can handle comparing the list data nodes. + * @param data reference value to search. + * @return the node if func(node->data, data) == 0, NULL if not found. + * + * This can be used to check if some value is inside the list and get + * the container node in this case. It should be used when list is + * known to be sorted as it will do binary search for results. + * + * Example: imagine user gives a string, you check if it's in the list + * before duplicating its contents. + * + * @note O(log2(n)) average/worst case performance, for 1,000,000 + * elements it will do a maximum of 20 comparisons. This is much + * faster than the 1,000,000 comparisons made by + * eina_list_search_unsorted_list(), so depending on the number of + * searches and insertions, it may be worth to eina_list_sort() the + * list and do the searches later. As said in + * eina_list_search_sorted_near_list(), lists do not have O(1) access + * time, so walking to the correct node can be costly, consider worst + * case to be almost O(n) pointer dereference (list walk). + * + * @see eina_list_search_sorted() + * @see eina_list_sort() + * @see eina_list_sorted_merge() + * @see eina_list_search_unsorted_list() + * @see eina_list_search_sorted_near_list() + */ +EAPI Eina_List * +eina_list_search_sorted_list(const Eina_List *list, + Eina_Compare_Cb func, + const void *data) +{ + Eina_List *lnear; + int cmp; + + lnear = eina_list_search_sorted_near_list(list, func, data, &cmp); + if (!lnear) + return NULL; + + if (cmp == 0) + return lnear; + + return NULL; +} + + +/** + * @brief Returns node data if it is in the sorted list. + * + * @param list The list to search for data, @b must be sorted. + * @param func A function pointer that can handle comparing the list data nodes. + * @param data reference value to search. + * @return the node value (@c node->data) if func(node->data, data) == 0, + * NULL if not found. + * + * This can be used to check if some value is inside the list and get + * the existing instance in this case. It should be used when list is + * known to be sorted as it will do binary search for results. + * + * Example: imagine user gives a string, you check if it's in the list + * before duplicating its contents. + * + * @note O(log2(n)) average/worst case performance, for 1,000,000 + * elements it will do a maximum of 20 comparisons. This is much + * faster than the 1,000,000 comparisons made by + * eina_list_search_unsorted(), so depending on the number of + * searches and insertions, it may be worth to eina_list_sort() the + * list and do the searches later. As said in + * eina_list_search_sorted_near_list(), lists do not have O(1) access + * time, so walking to the correct node can be costly, consider worst + * case to be almost O(n) pointer dereference (list walk). + * + * @see eina_list_search_sorted_list() + * @see eina_list_sort() + * @see eina_list_sorted_merge() + * @see eina_list_search_unsorted_list() + */ +EAPI void * +eina_list_search_sorted(const Eina_List *list, + Eina_Compare_Cb func, + const void *data) +{ + return eina_list_data_get(eina_list_search_sorted_list(list, func, data)); +} + +/** + * @brief Returns node if data is in the unsorted list. + * + * @param list The list to search for data, may be unsorted. + * @param func A function pointer that can handle comparing the list data nodes. + * @param data reference value to search. + * @return the node if func(node->data, data) == 0, NULL if not found. + * + * This can be used to check if some value is inside the list and get + * the container node in this case. + * + * Example: imagine user gives a string, you check if it's in the list + * before duplicating its contents. + * + * @note this is expensive and may walk the whole list, it's order-N, + * that is for 1,000,000 elements list it may walk and compare + * 1,000,000 nodes. + * + * @see eina_list_search_sorted_list() + * @see eina_list_search_unsorted() + */ +EAPI Eina_List * +eina_list_search_unsorted_list(const Eina_List *list, + Eina_Compare_Cb func, + const void *data) +{ + const Eina_List *l; + void *d; + + EINA_LIST_FOREACH(list, l, d) + { + if (!func(d, data)) + return (Eina_List *)l; + } + return NULL; +} + +/** + * @brief Returns node data if it is in the unsorted list. + * + * @param list The list to search for data, may be unsorted. + * @param func A function pointer that can handle comparing the list data nodes. + * @param data reference value to search. + * @return the node value (@c node->data) if func(node->data, data) == 0, + * NULL if not found. + * + * This can be used to check if some value is inside the list and get + * the existing instance in this case. + * + * Example: imagine user gives a string, you check if it's in the list + * before duplicating its contents. + * + * @note this is expensive and may walk the whole list, it's order-N, + * that is for 1,000,000 elements list it may walk and compare + * 1,000,000 nodes. + * + * @see eina_list_search_sorted() + * @see eina_list_search_unsorted_list() + */ +EAPI void * +eina_list_search_unsorted(const Eina_List *list, + Eina_Compare_Cb func, + const void *data) +{ + return eina_list_data_get(eina_list_search_unsorted_list(list, func, data)); +} + + +/** + * @brief Returned a new iterator associated to a list. + * + * @param list The list. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * list. If @p list is @c NULL or the count member of @p list is less + * or equal than 0, this function still returns a valid iterator that + * will always return false on eina_iterator_next(), thus keeping API + * sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the list structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_list_iterator_new(const Eina_List *list) +{ + Eina_Iterator_List *it; + + eina_error_set(0); + it = calloc(1, sizeof (Eina_Iterator_List)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(it, EINA_MAGIC_LIST_ITERATOR); + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->head = list; + it->current = list; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(eina_list_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + eina_list_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(eina_list_iterator_free); + + return &it->iterator; +} + +/** + * @brief Returned a new reversed iterator associated to a list. + * + * @param list The list. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * list. If @p list is @c NULL or the count member of @p list is less + * or equal than 0, this function still returns a valid iterator that + * will always return false on eina_iterator_next(), thus keeping API + * sane. + * + * Unlike eina_list_iterator_new(), this will walk the list backwards. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the list structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_list_iterator_reversed_new(const Eina_List *list) +{ + Eina_Iterator_List *it; + + eina_error_set(0); + it = calloc(1, sizeof (Eina_Iterator_List)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(it, EINA_MAGIC_LIST_ITERATOR); + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->head = eina_list_last(list); + it->current = it->head; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(eina_list_iterator_prev); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + eina_list_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(eina_list_iterator_free); + + return &it->iterator; +} + +/** + * @brief Returned a new accessor associated to a list. + * + * @param list The list. + * @return A new accessor. + * + * This function returns a newly allocated accessor associated to + * @p list. If @p list is @c NULL or the count member of @p list is + * less or equal than 0, this function returns NULL. If the memory can + * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is + * set. Otherwise, a valid accessor is returned. + */ +EAPI Eina_Accessor * +eina_list_accessor_new(const Eina_List *list) +{ + Eina_Accessor_List *ac; + + eina_error_set(0); + ac = calloc(1, sizeof (Eina_Accessor_List)); + if (!ac) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(ac, EINA_MAGIC_LIST_ACCESSOR); + EINA_MAGIC_SET(&ac->accessor, EINA_MAGIC_ACCESSOR); + + ac->head = list; + ac->current = list; + ac->index = 0; + + ac->accessor.version = EINA_ACCESSOR_VERSION; + ac->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_list_accessor_get_at); + ac->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER( + eina_list_accessor_get_container); + ac->accessor.free = FUNC_ACCESSOR_FREE(eina_list_accessor_free); + + return &ac->accessor; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_log.c b/tests/suite/ecore/src/lib/eina_log.c new file mode 100644 index 0000000000..aa9b7819ac --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_log.c @@ -0,0 +1,2483 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2009 Jorge Luis Zapata Muga, Cedric Bail, Andre Dieb + * Martins + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +/** + * @page tutorial_log_page Log Tutorial + * + * @section tutorial_log_introduction Introduction + * + * The Eina Log module provides logging facilities for libraries and + * applications. It provides colored logging, basic logging levels (error, + * warning, debug, info, critical) and loggers - called logging domains - + * which will be covered on next sections. + * + * @section tutorial_log_basic_usage Basic Usage + * + * Log messages can be displayed using the following macros: + * + * @li EINA_LOG_ERR(), + * @li EINA_LOG_INFO(), + * @li EINA_LOG_WARN(), + * @li EINA_LOG_DBG(). + * + * Here is an example: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * + * #include <Eina.h> + * + * void test(int i) + * { + * EINA_LOG_DBG("Entering test"); + * + * if (i < 0) + * { + * EINA_LOG_ERR("Argument is negative"); + * return; + * } + * + * EINA_LOG_INFO("argument non negative"); + * + * EINA_LOG_DBG("Exiting test"); + * } + * + * int main(void) + * { + * if (!eina_init()) + * { + * printf("log during the initialization of Eina_Log module\n"); + * return EXIT_FAILURE; + * } + * + * test(-1); + * test(0); + * + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * } + * @endcode + * + * If you compiled Eina without debug mode, execution will yield only one log + * message, which is "argument is negative". + * + * Here we introduce the concept of logging domains (or loggers), which might + * already be familiar to readers. It is basically a way to separate a set of + * log messages into a context (e.g. a module) and provide a way of controlling + * this set as a whole. + * + * For example, suppose you have 3 different modules on your application and you + * want to get logging only from one of them (e.g. create some sort of filter). + * For achieving that, all you need to do is create a logging domain for each + * module so that all logging inside a module can be considered as a whole. + * + * Logging domains are specified by a name, color applied to the name and the + * level. The first two (name and color) are set through code, that is, inside + * your application/module/library. + * + * The level is used for controlling which messages should appear. It + * specifies the lowest level that should be displayed (e.g. a message + * with level 11 being logged on a domain with level set to 10 would be + * displayed, while a message with level 9 wouldn't). + * + * The domain level is set during runtime (in contrast with the name and + * color) through the environment variable EINA_LOG_LEVELS. This variable + * expects a list in the form domain_name1:level1,domain_name2:level2,... . For + * example: + * + * @code + * + * EINA_LOG_LEVELS=mymodule1:5,mymodule2:2,mymodule3:0 ./myapp + * + * @endcode + * + * This line would set mymodule1 level to 5, mymodule2 level to 2 and mymodule3 + * level to 0. + * + * + * There's also a global logger to which EINA_LOG_(ERR, DBG, INFO, CRIT, WARN) + * macros do log on. It is a logger that is created internally by Eina Log with + * an empty name and can be used for general logging (where logging domains do + * not apply). + * + * Since this global logger doesn't have a name, you can't set its level through + * EINA_LOG_LEVELS variable. Here we introduce a second environment variable + * that is a bit more special: EINA_LOG_LEVEL. + * + * This variable specifies the level of the global logging domain and the level + * of domains that haven't been set through EINA_LOG_LEVELS. Here's an example: + * + * @code + * + * EINA_LOG_LEVEL=3 EINA_LOG_LEVELS=module1:10,module3:2 ./myapp + * + * @endcode + * + * Supposing you have modules named "module1", "module2" and "module3", this + * line would result in module1 with level 10, module2 with level 3 and module3 + * with level 2. Note that module2's level wasn't specified, so it's level is + * set to the global level. This way we can easily apply filters to multiple + * domains with only one parameter (EINA_LOG_LEVEL=num). + * + * The global level (EINA_LOG_LEVEL) can also be set through code, using + * eina_log_level_set() function. + * + * + * While developing your libraries or applications, you may notice that + * EINA_LOG_DOM_(ERR, DBG, INFO, CRIT, WARN) macros also print out + * messages from eina itself. Here we introduce another environment variable + * that is a bit more special: EINA_LOG_LEVELS_GLOB. + * + * This variable allows you to disable the logging of any/all code in eina itself. + * This is useful when developing your libraries or applications so that you can + * see your own domain's messages easier without having to sift through a lot of + * internal eina debug messages. Here's an example: + * + * @code + * + * EINA_LOG_LEVEL=3 EINA_LOG_LEVELS_GLOB=eina_*:0 ./myapp + * + * @endcode + * + * This will disable eina_log output from all internal eina code thus allowing + * you to see your own domain messages easier. + * + * @section tutorial_log_advanced_display Advanced usage of print callbacks + * + * The log module allows the user to change the way + * eina_log_print() displays the messages. It suffices to pass to + * eina_log_print_cb_set() the function used to display the + * message. That function must be of type #Eina_Log_Print_Cb. As a + * custom data can be passed to that callback, powerful display + * messages can be displayed. + * + * It is suggested to not use __FILE__, __FUNCTION__ or __LINE__ when + * writing that callback, but when defining macros (like + * EINA_LOG_ERR() and other macros). + * + * Here is an example of custom callback, whose behavior can be + * changed at runtime: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * + * #include <eina_log.h> + * + * #define log(fmt, ...) \ + * eina_log_print(EINA_LOG_LEVEL_ERR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + * + * typedef struct _Data Data; + * + * struct _Data + * { + * int to_stderr; + * }; + * + * void print_cb(const Eina_Log_Domain *domain, + * Eina_Log_Level level, + * const char *file, + * const char *fnc, + * int line, + * const char *fmt, + * void *data, + * va_list args) + * { + * Data *d; + * FILE *output; + * char *str; + * + * d = (Data *)data; + * if (d->to_stderr) + * { + * output = stderr; + * str = "stderr"; + * } + * else + * { + * output = stdout; + * str = "stdout"; + * } + * + * fprintf(output, "%s:%s:%s (%d) %s: ", + * domain->domain_str, file, fnc, line, str); + * vfprintf(output, fmt, args); + * putc('\n', output); + * } + * + * void test(Data *data, int i) + * { + * if (i < 0) + * data->to_stderr = 0; + * else + * data->to_stderr = 1; + * + * log("log message..."); + * } + * + * int main(void) + * { + * Data data; + * + * if (!eina_init()) + * { + * printf("log during the initialization of Eina_Log module\n"); + * return EXIT_FAILURE; + * } + * + * eina_log_print_cb_set(print_cb, &data); + * + * test(&data, -1); + * test(&data, 0); + * + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * } + * @endcode + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fnmatch.h> +#include <assert.h> + +#ifndef _MSC_VER +# include <unistd.h> +#endif + +#ifdef EFL_HAVE_POSIX_THREADS +# include <pthread.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_inlist.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_log.h" + +/* TODO + * + printing logs to stdout or stderr can be implemented + * using a queue, useful for multiple threads printing + * + add a wrapper for assert? + */ + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +#define EINA_LOG_ENV_ABORT "EINA_LOG_ABORT" +#define EINA_LOG_ENV_ABORT_LEVEL "EINA_LOG_ABORT_LEVEL" +#define EINA_LOG_ENV_LEVEL "EINA_LOG_LEVEL" +#define EINA_LOG_ENV_LEVELS "EINA_LOG_LEVELS" +#define EINA_LOG_ENV_LEVELS_GLOB "EINA_LOG_LEVELS_GLOB" +#define EINA_LOG_ENV_COLOR_DISABLE "EINA_LOG_COLOR_DISABLE" +#define EINA_LOG_ENV_FILE_DISABLE "EINA_LOG_FILE_DISABLE" +#define EINA_LOG_ENV_FUNCTION_DISABLE "EINA_LOG_FUNCTION_DISABLE" + + +// Structure for storing domain level settings passed from the command line +// that will be matched with application-defined domains. +typedef struct _Eina_Log_Domain_Level_Pending Eina_Log_Domain_Level_Pending; +struct _Eina_Log_Domain_Level_Pending +{ + EINA_INLIST; + unsigned int level; + size_t namelen; + char name[]; +}; + +/* + * List of levels for domains set by the user before the domains are registered, + * updates the domain levels on the first log and clears itself. + */ +static Eina_Inlist *_pending_list = NULL; +static Eina_Inlist *_glob_list = NULL; + +// Disable color flag (can be changed through the env var +// EINA_LOG_ENV_COLOR_DISABLE). +static Eina_Bool _disable_color = EINA_FALSE; +static Eina_Bool _disable_file = EINA_FALSE; +static Eina_Bool _disable_function = EINA_FALSE; +static Eina_Bool _abort_on_critical = EINA_FALSE; +static int _abort_level_on_critical = EINA_LOG_LEVEL_CRITICAL; + +#ifdef EFL_HAVE_THREADS + +static Eina_Bool _threads_enabled = EINA_FALSE; + +# ifdef EFL_HAVE_POSIX_THREADS + +typedef pthread_t Thread; + +static pthread_t _main_thread; + +# define SELF() pthread_self() +# define IS_MAIN(t) pthread_equal(t, _main_thread) +# define IS_OTHER(t) EINA_UNLIKELY(!IS_MAIN(t)) +# define CHECK_MAIN(...) \ + do { \ + if (!IS_MAIN(pthread_self())) { \ + fprintf(stderr, \ + "ERR: not main thread! current=%lu, main=%lu\n", \ + (unsigned long)pthread_self(), \ + (unsigned long)_main_thread); \ + return __VA_ARGS__; \ + } \ + } while (0) + +# ifdef EFL_HAVE_POSIX_THREADS_SPINLOCK + +static pthread_spinlock_t _log_lock; +# define LOG_LOCK() \ + if (_threads_enabled) \ + do { \ + if (0) { \ + fprintf(stderr, "+++LOG LOG_LOCKED! [%s, %lu]\n", \ + __FUNCTION__, (unsigned long)pthread_self()); } \ + if (EINA_UNLIKELY(_threads_enabled)) { \ + pthread_spin_lock(&_log_lock); } \ + } while (0) +# define LOG_UNLOCK() \ + if (_threads_enabled) \ + do { \ + if (EINA_UNLIKELY(_threads_enabled)) { \ + pthread_spin_unlock(&_log_lock); } \ + if (0) { \ + fprintf(stderr, \ + "---LOG LOG_UNLOCKED! [%s, %lu]\n", \ + __FUNCTION__, (unsigned long)pthread_self()); } \ + } while (0) +# define INIT() pthread_spin_init(&_log_lock, PTHREAD_PROCESS_PRIVATE) +# define SHUTDOWN() pthread_spin_destroy(&_log_lock) + +# else /* ! EFL_HAVE_POSIX_THREADS_SPINLOCK */ + +static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER; +# define LOG_LOCK() if(_threads_enabled) {pthread_mutex_lock(&_log_mutex); } +# define LOG_UNLOCK() if(_threads_enabled) {pthread_mutex_unlock(&_log_mutex); } +# define INIT() (1) +# define SHUTDOWN() do {} while (0) + +# endif /* ! EFL_HAVE_POSIX_THREADS_SPINLOCK */ + +# else /* EFL_HAVE_WIN32_THREADS */ + +typedef DWORD Thread; + +static DWORD _main_thread; + +# define SELF() GetCurrentThreadId() +# define IS_MAIN(t) (t == _main_thread) +# define IS_OTHER(t) EINA_UNLIKELY(!IS_MAIN(t)) +# define CHECK_MAIN(...) \ + do { \ + if (!IS_MAIN(GetCurrentThreadId())) { \ + fprintf(stderr, \ + "ERR: not main thread! current=%lu, main=%lu\n", \ + GetCurrentThreadId(), _main_thread); \ + return __VA_ARGS__; \ + } \ + } while (0) + +static HANDLE _log_mutex = NULL; + +# define LOG_LOCK() if(_threads_enabled) WaitForSingleObject(_log_mutex, INFINITE) +# define LOG_UNLOCK() if(_threads_enabled) ReleaseMutex(_log_mutex) +# define INIT() ((_log_mutex = CreateMutex(NULL, FALSE, NULL)) ? 1 : 0) +# define SHUTDOWN() if (_log_mutex) CloseHandle(_log_mutex) + +# endif /* EFL_HAVE_WIN32_THREADS */ + +#else /* ! EFL_HAVE_THREADS */ + +# define LOG_LOCK() do {} while (0) +# define LOG_UNLOCK() do {} while (0) +# define IS_MAIN(t) (1) +# define IS_OTHER(t) (0) +# define CHECK_MAIN(...) do {} while (0) +# define INIT() (1) +# define SHUTDOWN() do {} while (0) + +#endif /* ! EFL_HAVE_THREADS */ + + +// List of domains registered +static Eina_Log_Domain *_log_domains = NULL; +static unsigned int _log_domains_count = 0; +static size_t _log_domains_allocated = 0; + +// Default function for printing on domains +static Eina_Log_Print_Cb _print_cb = eina_log_print_cb_stderr; +static void *_print_cb_data = NULL; + +#ifdef DEBUG +static Eina_Log_Level _log_level = EINA_LOG_LEVEL_DBG; +#elif DEBUG_CRITICAL +static Eina_Log_Level _log_level = EINA_LOG_LEVEL_CRITICAL; +#else +static Eina_Log_Level _log_level = EINA_LOG_LEVEL_ERR; +#endif + +/* NOTE: if you change this, also change: + * eina_log_print_level_name_get() + * eina_log_print_level_name_color_get() + */ +static const char *_names[] = { + "CRI", + "ERR", + "WRN", + "INF", + "DBG", +}; + +#ifdef _WIN32 +static int +eina_log_win32_color_get(const char *domain_str) +{ + char *str; + char *tmp; + char *tmp2; + int code = -1; + int lighted = 0; + int ret = 0; + + str = strdup(domain_str); + if (!str) + return 0; + + /* this should not append */ + if (str[0] != '\033') + { + free(str); + return 0; + } + + /* we skip the first char and the [ */ + tmp = tmp2 = str + 2; + while (*tmp != 'm') + { + if (*tmp == ';') + { + *tmp = '\0'; + code = atol(tmp2); + tmp++; + tmp2 = tmp; + } + + tmp++; + } + *tmp = '\0'; + if (code < 0) + code = atol(tmp2); + else + lighted = atol(tmp2); + + free(str); + + if (code < lighted) + { + int c; + + c = code; + code = lighted; + lighted = c; + } + + if (lighted) + ret = FOREGROUND_INTENSITY; + + if (code == 31) + ret |= FOREGROUND_RED; + else if (code == 32) + ret |= FOREGROUND_GREEN; + else if (code == 33) + ret |= FOREGROUND_RED | FOREGROUND_GREEN; + else if (code == 34) + ret |= FOREGROUND_BLUE; + else if (code == 36) + ret |= FOREGROUND_GREEN | FOREGROUND_BLUE; + else if (code == 37) + ret |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + + return ret; +} +#endif + +static inline void +eina_log_print_level_name_get(int level, const char **p_name) +{ + static char buf[4]; + /* NOTE: if you change this, also change + * eina_log_print_level_name_color_get() + * eina_log_level_name_get() (at eina_inline_log.x) + */ + if (EINA_UNLIKELY(level < 0)) + { + snprintf(buf, sizeof(buf), "%03d", level); + *p_name = buf; + } + else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS)) + { + snprintf(buf, sizeof(buf), "%03d", level); + *p_name = buf; + } + else + *p_name = _names[level]; +} + +#ifdef _WIN32 +static inline void +eina_log_print_level_name_color_get(int level, + const char **p_name, + int *p_color) +{ + static char buf[4]; + /* NOTE: if you change this, also change: + * eina_log_print_level_name_get() + */ + if (EINA_UNLIKELY(level < 0)) + { + snprintf(buf, sizeof(buf), "%03d", level); + *p_name = buf; + } + else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS)) + { + snprintf(buf, sizeof(buf), "%03d", level); + *p_name = buf; + } + else + *p_name = _names[level]; + + *p_color = eina_log_win32_color_get(eina_log_level_color_get(level)); +} +#else +static inline void +eina_log_print_level_name_color_get(int level, + const char **p_name, + const char **p_color) +{ + static char buf[4]; + /* NOTE: if you change this, also change: + * eina_log_print_level_name_get() + */ + if (EINA_UNLIKELY(level < 0)) + { + snprintf(buf, sizeof(buf), "%03d", level); + *p_name = buf; + } + else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS)) + { + snprintf(buf, sizeof(buf), "%03d", level); + *p_name = buf; + } + else + *p_name = _names[level]; + + *p_color = eina_log_level_color_get(level); +} +#endif + +#define DECLARE_LEVEL_NAME(level) const char *name; \ + eina_log_print_level_name_get(level, &name) +#ifdef _WIN32 +# define DECLARE_LEVEL_NAME_COLOR(level) const char *name; int color; \ + eina_log_print_level_name_color_get(level, &name, &color) +#else +# define DECLARE_LEVEL_NAME_COLOR(level) const char *name, *color; \ + eina_log_print_level_name_color_get(level, &name, &color) +#endif + +/** No threads, No color */ +static void +eina_log_print_prefix_NOthreads_NOcolor_file_func(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line) +{ + DECLARE_LEVEL_NAME(level); + fprintf(fp, "%s:%s %s:%d %s() ", name, d->domain_str, file, line, fnc); +} + +static void +eina_log_print_prefix_NOthreads_NOcolor_NOfile_func(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file __UNUSED__, + const char *fnc, + int line __UNUSED__) +{ + DECLARE_LEVEL_NAME(level); + fprintf(fp, "%s:%s %s() ", name, d->domain_str, fnc); +} + +static void +eina_log_print_prefix_NOthreads_NOcolor_file_NOfunc(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc __UNUSED__, + int line) +{ + DECLARE_LEVEL_NAME(level); + fprintf(fp, "%s:%s %s:%d ", name, d->domain_str, file, line); +} + +/* No threads, color */ +static void +eina_log_print_prefix_NOthreads_color_file_func(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line) +{ + DECLARE_LEVEL_NAME_COLOR(level); +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + color); + fprintf(fp, "%s", name); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, ":"); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + eina_log_win32_color_get(d->domain_str)); + fprintf(fp, "%s", d->name); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, " %s:%d ", file, line); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_INTENSITY | FOREGROUND_RED | + FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, "%s()", fnc); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, " "); +#else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s %s:%d " + EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ", + color, name, d->domain_str, file, line, fnc); +#endif +} + +static void +eina_log_print_prefix_NOthreads_color_NOfile_func(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file __UNUSED__, + const char *fnc, + int line __UNUSED__) +{ + DECLARE_LEVEL_NAME_COLOR(level); +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + color); + fprintf(fp, "%s", name); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, ":"); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + eina_log_win32_color_get(d->domain_str)); + fprintf(fp, "%s", d->name); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_INTENSITY | FOREGROUND_RED | + FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, "%s()", fnc); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, " "); +#else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s " + EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ", + color, name, d->domain_str, fnc); +#endif +} + +static void +eina_log_print_prefix_NOthreads_color_file_NOfunc(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc __UNUSED__, + int line) +{ + DECLARE_LEVEL_NAME_COLOR(level); +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + color); + fprintf(fp, "%s", name); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, ":"); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + eina_log_win32_color_get(d->domain_str)); + fprintf(fp, "%s", d->name); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, " %s:%d ", file, line); +#else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s %s:%d ", + color, name, d->domain_str, file, line); +#endif +} + +/** threads, No color */ +#ifdef EFL_HAVE_THREADS +static void +eina_log_print_prefix_threads_NOcolor_file_func(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line) +{ + Thread cur; + + DECLARE_LEVEL_NAME(level); + cur = SELF(); + if (IS_OTHER(cur)) + { + fprintf(fp, "%s:%s[T:%lu] %s:%d %s() ", + name, d->domain_str, (unsigned long)cur, file, line, fnc); + return; + } + + fprintf(fp, "%s:%s %s:%d %s() ", name, d->domain_str, file, line, fnc); +} + +static void +eina_log_print_prefix_threads_NOcolor_NOfile_func(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file __UNUSED__, + const char *fnc, + int line __UNUSED__) +{ + Thread cur; + + DECLARE_LEVEL_NAME(level); + cur = SELF(); + if (IS_OTHER(cur)) + { + fprintf(fp, "%s:%s[T:%lu] %s() ", + name, d->domain_str, (unsigned long)cur, fnc); + return; + } + + fprintf(fp, "%s:%s %s() ", name, d->domain_str, fnc); +} + +static void +eina_log_print_prefix_threads_NOcolor_file_NOfunc(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc __UNUSED__, + int line) +{ + Thread cur; + + DECLARE_LEVEL_NAME(level); + cur = SELF(); + if (IS_OTHER(cur)) + { + fprintf(fp, "%s:%s[T:%lu] %s:%d ", + name, d->domain_str, (unsigned long)cur, file, line); + return; + } + + fprintf(fp, "%s:%s %s:%d ", name, d->domain_str, file, line); +} + +/* threads, color */ +static void +eina_log_print_prefix_threads_color_file_func(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line) +{ + Thread cur; + + DECLARE_LEVEL_NAME_COLOR(level); + cur = SELF(); + if (IS_OTHER(cur)) + { +# ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + color); + fprintf(fp, "%s", name); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, ":"); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + eina_log_win32_color_get(d->domain_str)); + fprintf(fp, "%s[T:", d->name); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, "[T:"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, "%lu", (unsigned long)cur); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, "] %s:%d ", file, line); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_INTENSITY | FOREGROUND_RED | + FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, "%s()", fnc); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, " "); +# else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s[T:" + EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] %s:%d " + EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ", + color, name, d->domain_str, (unsigned long)cur, file, + line, fnc); +# endif + return; + } + +# ifdef _WIN32 + eina_log_print_prefix_NOthreads_color_file_func(fp, + d, + level, + file, + fnc, + line); +# else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s %s:%d " + EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ", + color, name, d->domain_str, file, line, fnc); +# endif +} + +static void +eina_log_print_prefix_threads_color_NOfile_func(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file __UNUSED__, + const char *fnc, + int line __UNUSED__) +{ + Thread cur; + + DECLARE_LEVEL_NAME_COLOR(level); + cur = SELF(); + if (IS_OTHER(cur)) + { +# ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + color); + fprintf(fp, "%s", name); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, ":"); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + eina_log_win32_color_get(d->domain_str)); + fprintf(fp, "%s[T:", d->name); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, "[T:"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, "%lu", (unsigned long)cur); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_INTENSITY | FOREGROUND_RED | + FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, "%s()", fnc); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, " "); +# else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s[T:" + EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] " + EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ", + color, name, d->domain_str, (unsigned long)cur, fnc); +# endif + return; + } + +# ifdef _WIN32 + eina_log_print_prefix_NOthreads_color_NOfile_func(fp, + d, + level, + file, + fnc, + line); +# else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s " + EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ", + color, name, d->domain_str, fnc); +# endif +} + +static void +eina_log_print_prefix_threads_color_file_NOfunc(FILE *fp, + const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc __UNUSED__, + int line) +{ + Thread cur; + + DECLARE_LEVEL_NAME_COLOR(level); + cur = SELF(); + if (IS_OTHER(cur)) + { +# ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + color); + fprintf(fp, "%s", name); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, ":"); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + eina_log_win32_color_get(d->domain_str)); + fprintf(fp, "%s[T:", d->name); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, "[T:"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_GREEN | FOREGROUND_BLUE); + fprintf(fp, "%lu", (unsigned long)cur); + SetConsoleTextAttribute(GetStdHandle( + STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + fprintf(fp, "] %s:%d ", file, line); +# else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s[T:" + EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] %s:%d ", + color, name, d->domain_str, (unsigned long)cur, file, line); +# endif + return; + } + +# ifdef _WIN32 + eina_log_print_prefix_NOthreads_color_file_NOfunc(fp, + d, + level, + file, + fnc, + line); +# else + fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s %s:%d ", + color, name, d->domain_str, file, line); +# endif +} +#endif /* EFL_HAVE_THREADS */ + +static void (*_eina_log_print_prefix)(FILE *fp, const Eina_Log_Domain *d, + Eina_Log_Level level, const char *file, + const char *fnc, + int line) = + eina_log_print_prefix_NOthreads_color_file_func; + +static inline void +eina_log_print_prefix_update(void) +{ + if (_disable_file && _disable_function) + { + fprintf(stderr, "ERROR: cannot have " EINA_LOG_ENV_FILE_DISABLE " and " + EINA_LOG_ENV_FUNCTION_DISABLE " set at the same time, will " + "just disable function.\n"); + _disable_file = 0; + } + +#define S(NOthread, NOcolor, NOfile, NOfunc) \ + _eina_log_print_prefix = \ + eina_log_print_prefix_ ## NOthread ## threads_ ## NOcolor ## color_ ## \ + NOfile \ + ## file_ ## NOfunc ## func + +#ifdef EFL_HAVE_THREADS + if (_threads_enabled) + { + if (_disable_color) + { + if (_disable_file) + S(,NO,NO,); + else if (_disable_function) + S(,NO,,NO); + else + S(,NO,,); + } + else + { + if (_disable_file) + S(,,NO,); + else if (_disable_function) + S(,,,NO); + else + S(,,,); + } + + return; + } + +#endif + + if (_disable_color) + { + if (_disable_file) + S(NO,NO,NO,); + else if (_disable_function) + S(NO,NO,,NO); + else + S(NO,NO,,); + } + else + { + if (_disable_file) + S(NO,,NO,); + else if (_disable_function) + S(NO,,,NO); + else + S(NO,,,); + } + +#undef S +} + +/* + * Creates a colored domain name string. + */ +static const char * +eina_log_domain_str_get(const char *name, const char *color) +{ + const char *d; + + if (color) + { + size_t name_len; + size_t color_len; + + name_len = strlen(name); + color_len = strlen(color); + d = + malloc(sizeof(char) * + (color_len + name_len + strlen(EINA_COLOR_RESET) + 1)); + if (!d) + return NULL; + + memcpy((char *)d, color, color_len); + memcpy((char *)(d + color_len), name, name_len); + memcpy((char *)(d + color_len + name_len), EINA_COLOR_RESET, + strlen(EINA_COLOR_RESET)); + ((char *)d)[color_len + name_len + strlen(EINA_COLOR_RESET)] = '\0'; + } + else + d = strdup(name); + + return d; +} + +/* + * Setups a new logging domain to the name and color specified. Note that this + * constructor acts upon an pre-allocated object. + */ +static Eina_Log_Domain * +eina_log_domain_new(Eina_Log_Domain *d, const char *name, const char *color) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL); + + d->level = EINA_LOG_LEVEL_UNKNOWN; + d->deleted = EINA_FALSE; + + if (name) + { + if ((color) && (!_disable_color)) + d->domain_str = eina_log_domain_str_get(name, color); + else + d->domain_str = eina_log_domain_str_get(name, NULL); + + d->name = strdup(name); + d->namelen = strlen(name); + } + else + { + d->domain_str = NULL; + d->name = NULL; + d->namelen = 0; + } + + return d; +} + +/* + * Frees internal strings of a log domain, keeping the log domain itself as a + * slot for next domain registers. + */ +static void +eina_log_domain_free(Eina_Log_Domain *d) +{ + EINA_SAFETY_ON_NULL_RETURN(d); + + if (d->domain_str) + free((char *)d->domain_str); + + if (d->name) + free((char *)d->name); +} + +/* + * Parses domain levels passed through the env var. + */ +static void +eina_log_domain_parse_pendings(void) +{ + const char *start; + + if (!(start = getenv(EINA_LOG_ENV_LEVELS))) + return; + + // name1:level1,name2:level2,name3:level3,... + while (1) + { + Eina_Log_Domain_Level_Pending *p; + char *end = NULL; + char *tmp = NULL; + long int level; + + end = strchr(start, ':'); + if (!end) + break; + + // Parse level, keep going if failed + level = strtol((char *)(end + 1), &tmp, 10); + if (tmp == (end + 1)) + goto parse_end; + + // Parse name + p = malloc(sizeof(Eina_Log_Domain_Level_Pending) + end - start + 1); + if (!p) + break; + + p->namelen = end - start; + memcpy((char *)p->name, start, end - start); + ((char *)p->name)[end - start] = '\0'; + p->level = level; + + _pending_list = eina_inlist_append(_pending_list, EINA_INLIST_GET(p)); + +parse_end: + start = strchr(tmp, ','); + if (start) + start++; + else + break; + } +} + +static void +eina_log_domain_parse_pending_globs(void) +{ + const char *start; + + if (!(start = getenv(EINA_LOG_ENV_LEVELS_GLOB))) + return; + + // name1:level1,name2:level2,name3:level3,... + while (1) + { + Eina_Log_Domain_Level_Pending *p; + char *end = NULL; + char *tmp = NULL; + long int level; + + end = strchr(start, ':'); + if (!end) + break; + + // Parse level, keep going if failed + level = strtol((char *)(end + 1), &tmp, 10); + if (tmp == (end + 1)) + goto parse_end; + + // Parse name + p = malloc(sizeof(Eina_Log_Domain_Level_Pending) + end - start + 1); + if (!p) + break; + + p->namelen = 0; /* not that useful */ + memcpy((char *)p->name, start, end - start); + ((char *)p->name)[end - start] = '\0'; + p->level = level; + + _glob_list = eina_inlist_append(_glob_list, EINA_INLIST_GET(p)); + +parse_end: + start = strchr(tmp, ','); + if (start) + start++; + else + break; + } +} + +static inline int +eina_log_domain_register_unlocked(const char *name, const char *color) +{ + Eina_Log_Domain_Level_Pending *pending = NULL; + size_t namelen; + unsigned int i; + + for (i = 0; i < _log_domains_count; i++) + { + if (_log_domains[i].deleted) + { + // Found a flagged slot, free domain_str and replace slot + eina_log_domain_new(&_log_domains[i], name, color); + goto finish_register; + } + } + + if (_log_domains_count >= _log_domains_allocated) + { + Eina_Log_Domain *tmp; + size_t size; + + if (!_log_domains) + // special case for init, eina itself will allocate a dozen of domains + size = 24; + else + // grow 8 buckets to minimize reallocs + size = _log_domains_allocated + 8; + + tmp = realloc(_log_domains, sizeof(Eina_Log_Domain) * size); + + if (tmp) + { + // Success! + _log_domains = tmp; + _log_domains_allocated = size; + } + else + return -1; + } + + // Use an allocated slot + eina_log_domain_new(&_log_domains[i], name, color); + _log_domains_count++; + +finish_register: + namelen = _log_domains[i].namelen; + + EINA_INLIST_FOREACH(_pending_list, pending) + { + if ((namelen == pending->namelen) && (strcmp(pending->name, name) == 0)) + { + _log_domains[i].level = pending->level; + _pending_list = + eina_inlist_remove(_pending_list, EINA_INLIST_GET(pending)); + free(pending); + break; + } + } + + if (_log_domains[i].level == EINA_LOG_LEVEL_UNKNOWN) + { + EINA_INLIST_FOREACH(_glob_list, pending) + { + if (!fnmatch(pending->name, name, 0)) + { + _log_domains[i].level = pending->level; + break; + } + } + } + + // Check if level is still UNKNOWN, set it to global + if (_log_domains[i].level == EINA_LOG_LEVEL_UNKNOWN) + _log_domains[i].level = _log_level; + + return i; +} + +static inline Eina_Bool +eina_log_term_color_supported(const char *term) +{ + const char *tail; + + if (!term) + return EINA_FALSE; + + tail = term + 1; + switch (term[0]) + { + /* list of known to support color terminals, + * take from gentoo's portage. + */ + + case 'x': /* xterm and xterm-color */ + return ((strncmp(tail, "term", sizeof("term") - 1) == 0) && + ((tail[sizeof("term") - 1] == '\0') || + (strcmp(tail + sizeof("term") - 1, "-color") == 0))); + + case 'E': /* Eterm */ + case 'a': /* aterm */ + case 'k': /* kterm */ + return (strcmp(tail, "term") == 0); + + case 'r': /* xrvt or rxvt-unicode */ + return ((strncmp(tail, "xvt", sizeof("xvt") - 1) == 0) && + ((tail[sizeof("xvt") - 1] == '\0') || + (strcmp(tail + sizeof("xvt") - 1, "-unicode") == 0))); + + case 's': /* screen */ + return (strcmp(tail, "creen") == 0); + + case 'g': /* gnome */ + return (strcmp(tail, "nome") == 0); + + case 'i': /* interix */ + return (strcmp(tail, "nterix") == 0); + + default: + return EINA_FALSE; + } +} + +static inline void +eina_log_domain_unregister_unlocked(int domain) +{ + Eina_Log_Domain *d; + + if ((unsigned int)domain >= _log_domains_count) + return; + + d = &_log_domains[domain]; + eina_log_domain_free(d); + d->deleted = 1; +} + +static inline void +eina_log_print_unlocked(int domain, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line, + const char *fmt, + va_list args) +{ + Eina_Log_Domain *d; + +#ifdef EINA_SAFETY_CHECKS + if (EINA_UNLIKELY((unsigned int)domain >= _log_domains_count) || + EINA_UNLIKELY(domain < 0)) + { + if (file && fnc && fmt) + fprintf( + stderr, + "CRI: %s:%d %s() eina_log_print() unknown domain %d, original message format '%s'\n", + file, + line, + fnc, + domain, + fmt); + else + fprintf( + stderr, + "CRI: eina_log_print() unknown domain %d, original message format '%s'\n", + domain, + fmt ? fmt : ""); + + if (_abort_on_critical) + abort(); + + return; + } + +#endif + d = _log_domains + domain; +#ifdef EINA_SAFETY_CHECKS + if (EINA_UNLIKELY(d->deleted)) + { + fprintf(stderr, + "ERR: eina_log_print() domain %d is deleted\n", + domain); + return; + } + +#endif + + if (level > d->level) + return; + +#ifdef _WIN32 + { + char *wfmt; + char *tmp; + + wfmt = strdup(fmt); + if (!wfmt) + { + fprintf(stderr, "ERR: %s: can not allocate memory\n", __FUNCTION__); + return; + } + + tmp = wfmt; + while (strchr(tmp, "%")) + { + tmp++; + if (*tmp == 'z') + *tmp = 'I'; + } + _print_cb(d, level, file, fnc, line, wfmt, _print_cb_data, args); + free(wfmt); + } +#else + _print_cb(d, level, file, fnc, line, fmt, _print_cb_data, args); +#endif + + if (EINA_UNLIKELY(_abort_on_critical) && + EINA_UNLIKELY(level <= _abort_level_on_critical)) + abort(); +} + +/** + * @endcond + */ + + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the log module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the log module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + * + * @warning Not-MT: just call this function from main thread! The + * place where this function was called the first time is + * considered the main thread. + */ +Eina_Bool +eina_log_init(void) +{ + const char *level, *tmp; + int color_disable; + + assert((sizeof(_names) / sizeof(_names[0])) == EINA_LOG_LEVELS); + + if ((tmp = getenv(EINA_LOG_ENV_COLOR_DISABLE))) + color_disable = atoi(tmp); + else + color_disable = -1; + + /* Check if color is explicitly disabled */ + if (color_disable == 1) + _disable_color = EINA_TRUE; + +#ifndef _WIN32 + /* color was not explicitly disabled or enabled, guess it */ + else if (color_disable == -1) + { + if (!eina_log_term_color_supported(getenv("TERM"))) + _disable_color = EINA_TRUE; + else + { + /* if not a terminal, but redirected to a file, disable color */ + int fd; + + if (_print_cb == eina_log_print_cb_stderr) + fd = STDERR_FILENO; + else if (_print_cb == eina_log_print_cb_stdout) + fd = STDOUT_FILENO; + else + fd = -1; + + if ((fd >= 0) && (!isatty(fd))) + _disable_color = EINA_TRUE; + } + } +#endif + + if ((tmp = getenv(EINA_LOG_ENV_FILE_DISABLE)) && (atoi(tmp) == 1)) + _disable_file = EINA_TRUE; + + if ((tmp = getenv(EINA_LOG_ENV_FUNCTION_DISABLE)) && (atoi(tmp) == 1)) + _disable_function = EINA_TRUE; + + if ((tmp = getenv(EINA_LOG_ENV_ABORT)) && (atoi(tmp) == 1)) + _abort_on_critical = EINA_TRUE; + + if ((tmp = getenv(EINA_LOG_ENV_ABORT_LEVEL))) + _abort_level_on_critical = atoi(tmp); + + eina_log_print_prefix_update(); + + // Global log level + if ((level = getenv(EINA_LOG_ENV_LEVEL))) + _log_level = atoi(level); + + // Register UNKNOWN domain, the default logger + EINA_LOG_DOMAIN_GLOBAL = eina_log_domain_register("", NULL); + + if (EINA_LOG_DOMAIN_GLOBAL < 0) + { + fprintf(stderr, "Failed to create global logging domain.\n"); + return EINA_FALSE; + } + + // Parse pending domains passed through EINA_LOG_LEVELS_GLOB + eina_log_domain_parse_pending_globs(); + + // Parse pending domains passed through EINA_LOG_LEVELS + eina_log_domain_parse_pendings(); + + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the log module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the log module set up by + * eina_log_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + * + * @warning Not-MT: just call this function from main thread! The + * place where eina_log_init() (eina_init()) was called the + * first time is considered the main thread. + */ +Eina_Bool +eina_log_shutdown(void) +{ + Eina_Inlist *tmp; + + while (_log_domains_count--) + { + if (_log_domains[_log_domains_count].deleted) + continue; + + eina_log_domain_free(&_log_domains[_log_domains_count]); + } + + free(_log_domains); + + _log_domains = NULL; + _log_domains_count = 0; + _log_domains_allocated = 0; + + while (_glob_list) + { + tmp = _glob_list; + _glob_list = _glob_list->next; + free(tmp); + } + + while (_pending_list) + { + tmp = _pending_list; + _pending_list = _pending_list->next; + free(tmp); + } + + return EINA_TRUE; +} + +#ifdef EFL_HAVE_THREADS + +/** + * @internal + * @brief Activate the log mutex. + * + * This function activate the mutex in the eina log module. It is called by + * eina_threads_init(). + * + * @see eina_threads_init() + */ +void +eina_log_threads_init(void) +{ + _main_thread = SELF(); + if (INIT()) + _threads_enabled = EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the log mutex. + * + * This function shuts down the mutex in the log module. + * It is called by eina_threads_shutdown(). + * + * @see eina_threads_shutdown() + */ +void +eina_log_threads_shutdown(void) +{ + CHECK_MAIN(); + SHUTDOWN(); + _threads_enabled = EINA_FALSE; +} + +#endif + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Log_Group Log + * + * @brief Full-featured logging system. + * + * Eina provides eina_log_print(), a standard function to manage all + * logging messages. This function may be called directly or using the + * helper macros such as EINA_LOG_DBG(), EINA_LOG_ERR() or those that + * take a specific domain as argument EINA_LOG_DOM_DBG(), + * EINA_LOG_DOM_ERR(). Internally, eina_log_print() will call the + * function defined with eina_log_print_cb_set(), that defaults to + * eina_log_print_cb_stderr(), but may be changed to do whatever you + * need, such as networking or syslog logging. + * + * The logging system is thread safe once initialized with + * eina_log_threads_enable(). The thread that calls this function + * first is considered "main thread" and other threads will have their + * thread id (pthread_self()) printed in the log message so it is easy + * to detect from where it is coming. + * + * Log domains is the Eina way to differentiate messages. There might + * be different domains to represent different modules, different + * feature-set, different categories and so on. Filtering can be + * applied to domain names by means of @c EINA_LOG_LEVELS environment + * variable or eina_log_domain_level_set(). + * + * The different logging levels serve to customize the amount of + * debugging one want to take and may be used to automatically call + * abort() once some given level message is printed. This is + * controlled by environment variable @c EINA_LOG_ABORT and the level + * to be considered critical with @c EINA_LOG_ABORT_LEVEL. These can + * be changed with eina_log_abort_on_critical_set() and + * eina_log_abort_on_critical_level_set(). + * + * The default maximum level to print is defined by environment + * variable @c EINA_LOG_LEVEL, but may be set per-domain with @c + * EINA_LOG_LEVELS. It will default to #EINA_LOG_ERR. This can be + * changed with eina_log_level_set(). + * + * To use the log system Eina must be initialized with eina_init() and + * later shut down with eina_shutdown(). Here is a straightforward + * example: + * + * @code + * #include <stdlib.h> + * #include <stdio.h> + * + * #include <eina_log.h> + * + * void test_warn(void) + * { + * EINA_LOG_WARN("Here is a warning message"); + * } + * + * int main(void) + * { + * if (!eina_init()) + * { + * printf("log during the initialization of Eina_Log module\n"); + * return EXIT_FAILURE; + * } + * + * test_warn(); + * + * eina_shutdown(); + * + * return EXIT_SUCCESS; + * } + * @endcode + * + * Compile this code with the following command: + * + * @code + * gcc -Wall -o test_Eina_Log test_eina.c `pkg-config --cflags --libs eina` + * @endcode + * + * Now execute the program with: + * + * @code + * EINA_LOG_LEVEL=2 ./test_eina_log + * @endcode + * + * You should see a message displayed in the terminal. + * + * For more information, you can look at the @ref tutorial_log_page. + * + * @{ + */ + + +/** + * @cond LOCAL + */ + +EAPI int EINA_LOG_DOMAIN_GLOBAL = 0; + +/** + * @endcond + */ + + +/** + * Enable logging module to handle threads. + * + * There is no disable option on purpose, if it is enabled, there is + * no way back until you call the last eina_shutdown(). + * + * There is no function to retrieve if threads are enabled as one is + * not supposed to know this from outside. + * + * After this call is executed at least once, if Eina was compiled + * with threads support then logging will lock around debug messages + * and threads that are not the main thread will have its identifier + * printed. + * + * The main thread is considered the thread where the first + * eina_init() was called. + */ +EAPI void +eina_log_threads_enable(void) +{ +#ifdef EFL_HAVE_THREADS + _threads_enabled = 1; + eina_log_print_prefix_update(); +#endif +} + +/** + * Sets logging method to use. + * + * @param cb The callback to call when printing a log. + * @param data The data to pass to the callback. + * + * By default, eina_log_print_cb_stderr() is used. + * + * @note MT: safe to call from any thread. + * + * @note MT: given function @a cb will be called protected by mutex. + * This means you're safe from other calls but you should never + * call eina_log_print(), directly or indirectly. + */ +EAPI void +eina_log_print_cb_set(Eina_Log_Print_Cb cb, void *data) +{ + LOG_LOCK(); + _print_cb = cb; + _print_cb_data = data; + eina_log_print_prefix_update(); + LOG_UNLOCK(); +} + +/** + * @brief Set the default log level. + * + * @param level The log level. + * + * This function sets the log level @p level. It is used in + * eina_log_print(). + * + * @note this is initially set to envvar EINA_LOG_LEVEL by eina_init(). + * + * @see eina_log_level_get() + */ +EAPI void +eina_log_level_set(int level) +{ + _log_level = level; + if (EINA_LIKELY((EINA_LOG_DOMAIN_GLOBAL >= 0) && + ((unsigned int)EINA_LOG_DOMAIN_GLOBAL < _log_domains_count))) + _log_domains[EINA_LOG_DOMAIN_GLOBAL].level = level; +} + +/** + * @brief Get the default log level. + * + * @return the log level that limits eina_log_print(). + * + * @see eina_log_level_set() + */ +EAPI int +eina_log_level_get(void) +{ + return _log_level; +} + +/** + * Checks if current thread is the main thread. + * + * @return #EINA_TRUE if threads were enabled and the current thread + * is the one that called eina_log_threads_init(). If there is + * no thread support (compiled with --disable-pthreads) or + * they were not enabled, then #EINA_TRUE is also + * returned. The only case where #EINA_FALSE is returned is + * when threads were successfully enabled but the current + * thread is not the main (one that called + * eina_log_threads_init()). + */ +EAPI Eina_Bool +eina_log_main_thread_check(void) +{ +#ifdef EFL_HAVE_THREADS + return ((!_threads_enabled) || IS_MAIN(SELF())); +#else + return EINA_TRUE; +#endif +} + +/** + * @brief Set if color logging should be disabled. + * + * @param disabled if #EINA_TRUE, color logging should be disabled. + * + * @note this is initially set to envvar EINA_LOG_COLOR_DISABLE by eina_init(). + * + * @see eina_log_color_disable_get() + */ +EAPI void +eina_log_color_disable_set(Eina_Bool disabled) +{ + _disable_color = disabled; +} + +/** + * @brief Get if color logging should be disabled. + * + * @return if #EINA_TRUE, color logging should be disabled. + * + * @see eina_log_color_disable_set() + */ +EAPI Eina_Bool +eina_log_color_disable_get(void) +{ + return _disable_color; +} + +/** + * @brief Set if originating file name logging should be disabled. + * + * @param disabled if #EINA_TRUE, file name logging should be disabled. + * + * @note this is initially set to envvar EINA_LOG_FILE_DISABLE by eina_init(). + * + * @see eina_log_file_disable_get() + */ +EAPI void +eina_log_file_disable_set(Eina_Bool disabled) +{ + _disable_file = disabled; +} + +/** + * @brief Get if originating file name logging should be disabled. + * + * @return if #EINA_TRUE, file name logging should be disabled. + * + * @see eina_log_file_disable_set() + */ +EAPI Eina_Bool +eina_log_file_disable_get(void) +{ + return _disable_file; +} + +/** + * @brief Set if originating function name logging should be disabled. + * + * @param disabled if #EINA_TRUE, function name logging should be disabled. + * + * @note this is initially set to envvar EINA_LOG_FUNCTION_DISABLE by + * eina_init(). + * + * @see eina_log_function_disable_get() + */ +EAPI void +eina_log_function_disable_set(Eina_Bool disabled) +{ + _disable_function = disabled; +} + +/** + * @brief Get if originating function name logging should be disabled. + * + * @return if #EINA_TRUE, function name logging should be disabled. + * + * @see eina_log_function_disable_set() + */ +EAPI Eina_Bool +eina_log_function_disable_get(void) +{ + return _disable_function; +} + +/** + * @brief Set if critical messages should abort the program. + * + * @param abort_on_critical if #EINA_TRUE, messages with level equal + * or smaller than eina_log_abort_on_critical_level_get() will + * abort the program. + * + * @note this is initially set to envvar EINA_LOG_ABORT by + * eina_init(). + * + * @see eina_log_abort_on_critical_get() + * @see eina_log_abort_on_critical_level_set() + */ +EAPI void +eina_log_abort_on_critical_set(Eina_Bool abort_on_critical) +{ + _abort_on_critical = abort_on_critical; +} + +/** + * @brief Get if critical messages should abort the program. + * + * @return if #EINA_TRUE, any messages with level equal or smaller + * than eina_log_abort_on_critical_level_get() will abort the + * program. + * + * @see eina_log_abort_on_critical_set() + * @see eina_log_abort_on_critical_level_set() + */ +EAPI Eina_Bool +eina_log_abort_on_critical_get(void) +{ + return _abort_on_critical; +} + +/** + * @brief Set level that triggers abort if abort-on-critical is set. + * + * @param critical_level levels equal or smaller than the given value + * will trigger program abortion if + * eina_log_abort_on_critical_get() returns #EINA_TRUE. + * + * @note this is initially set to envvar EINA_LOG_ABORT_LEVEL by + * eina_init(). + * + * @see eina_log_abort_on_critical_level_get() + * @see eina_log_abort_on_critical_get() + */ +EAPI void +eina_log_abort_on_critical_level_set(int critical_level) +{ + _abort_level_on_critical = critical_level; +} + +/** + * @brief Get level that triggers abort if abort-on-critical is set. + * + * @return critical level equal or smaller than value will trigger + * program abortion if eina_log_abort_on_critical_get() returns + * #EINA_TRUE. + * + * @see eina_log_abort_on_critical_level_set() + * @see eina_log_abort_on_critical_get() + */ +EAPI int +eina_log_abort_on_critical_level_get(void) +{ + return _abort_level_on_critical; +} + +/** + * @param name Domain name + * @param color Color of the domain name + * + * @return Domain index that will be used as the DOMAIN parameter on log + * macros. A negative return value means an log occurred. + * + * @note MT: safe to call from any thread. + */ +EAPI int +eina_log_domain_register(const char *name, const char *color) +{ + int r; + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, -1); + + LOG_LOCK(); + r = eina_log_domain_register_unlocked(name, color); + LOG_UNLOCK(); + return r; +} + +/** + * Forget about a logging domain registered by eina_log_domain_register() + * + * @param domain domain identifier as reported by eina_log_domain_register(), + * must be >= 0. + * + * @note MT: safe to call from any thread. + */ +EAPI void +eina_log_domain_unregister(int domain) +{ + EINA_SAFETY_ON_FALSE_RETURN(domain >= 0); + LOG_LOCK(); + eina_log_domain_unregister_unlocked(domain); + LOG_UNLOCK(); +} + +/** + * Set the domain level given its name. + * + * This call has the same effect as setting + * EINA_LOG_LEVELS=<@p domain_name>:<@p level> + * + * @param domain_name domain name to change the level. It may be of a + * still not registered domain. If the domain is not registered + * yet, it will be saved as a pending set and applied upon + * registration. + * @param level level to use to limit eina_log_print() for given domain. + */ +EAPI void +eina_log_domain_level_set(const char *domain_name, int level) +{ + Eina_Log_Domain_Level_Pending *pending; + size_t namelen; + unsigned int i; + + EINA_SAFETY_ON_NULL_RETURN(domain_name); + + namelen = strlen(domain_name); + + for (i = 0; i < _log_domains_count; i++) + { + if (_log_domains[i].deleted) + continue; + + if ((namelen != _log_domains[i].namelen) || + (strcmp(_log_domains[i].name, domain_name) != 0)) + continue; + + _log_domains[i].level = level; + return; + } + + EINA_INLIST_FOREACH(_pending_list, pending) + { + if ((namelen == pending->namelen) && + (strcmp(pending->name, domain_name) == 0)) + { + pending->level = level; + return; + } + } + + pending = malloc(sizeof(Eina_Log_Domain_Level_Pending) + namelen + 1); + if (!pending) + return; + + pending->level = level; + pending->namelen = namelen; + memcpy(pending->name, domain_name, namelen + 1); + + _pending_list = eina_inlist_append(_pending_list, EINA_INLIST_GET(pending)); +} + +/** + * Get the domain level given its name. + * + * @param domain_name domain name to retrieve the level. It may be of + * a still not registered domain. If the domain is not + * registered yet, but there is a pending value, either from + * eina_log_domain_level_set(),EINA_LOG_LEVELS environment + * variable or from EINA_LOG_LEVELS_GLOB, these are + * returned. If nothing else was found, then the global/default + * level (eina_log_level_get()) is returned. + * + * @return level to use to limit eina_log_print() for given + * domain. On error (@p domain_name == NULL), + * EINA_LOG_LEVEL_UNKNOWN is returned. + * + * @see eina_log_domain_level_set() + * @see eina_log_domain_registered_level_get() + */ +EAPI int +eina_log_domain_level_get(const char *domain_name) +{ + Eina_Log_Domain_Level_Pending *pending; + size_t namelen; + unsigned int i; + + EINA_SAFETY_ON_NULL_RETURN_VAL(domain_name, EINA_LOG_LEVEL_UNKNOWN); + + namelen = strlen(domain_name); + + for (i = 0; i < _log_domains_count; i++) + { + if (_log_domains[i].deleted) + continue; + + if ((namelen != _log_domains[i].namelen) || + (strcmp(_log_domains[i].name, domain_name) != 0)) + continue; + + return _log_domains[i].level; + } + + EINA_INLIST_FOREACH(_pending_list, pending) + { + if ((namelen == pending->namelen) && + (strcmp(pending->name, domain_name) == 0)) + return pending->level; + } + + EINA_INLIST_FOREACH(_glob_list, pending) + { + if (!fnmatch(pending->name, domain_name, 0)) + return pending->level; + } + + return _log_level; +} + +/** + * Get the domain level given its identifier. + * + * @param domain identifier, so it must be previously registered with + * eina_log_domain_register(). It's a much faster version of + * eina_log_domain_level_get(), but relies on domain being + * present. + * + * @return level to use to limit eina_log_print() for given domain. On + * error EINA_LOG_LEVEL_UNKNOWN is returned. + */ +EAPI int +eina_log_domain_registered_level_get(int domain) +{ + EINA_SAFETY_ON_FALSE_RETURN_VAL(domain >= 0, EINA_LOG_LEVEL_UNKNOWN); + EINA_SAFETY_ON_FALSE_RETURN_VAL((unsigned int)domain < _log_domains_count, + EINA_LOG_LEVEL_UNKNOWN); + EINA_SAFETY_ON_TRUE_RETURN_VAL(_log_domains[domain].deleted, + EINA_LOG_LEVEL_UNKNOWN); + return _log_domains[domain].level; +} + +/** + * Default logging method, this will output to standard error stream. + * + * This method will colorize output based on domain provided color and + * message logging level. + * + * To disable color, set environment variable + * EINA_LOG_COLOR_DISABLE=1. To enable color, even if directing to a + * file or when using a non-supported color terminal, use + * EINA_LOG_COLOR_DISABLE=0. If EINA_LOG_COLOR_DISABLE is unset (or + * -1), then Eina will disable color if terminal ($TERM) is + * unsupported or if redirecting to a file. + + . Similarly, to disable file and line + * information, set EINA_LOG_FILE_DISABLE=1 or + * EINA_LOG_FUNCTION_DISABLE=1 to avoid function name in output. It is + * not acceptable to have both EINA_LOG_FILE_DISABLE and + * EINA_LOG_FUNCTION_DISABLE at the same time, in this case just + * EINA_LOG_FUNCTION_DISABLE will be considered and file information + * will be printed anyways. + * + * @note MT: if threads are enabled, this function is called within locks. + * @note MT: Threads different from main thread will have thread id + * appended to domain name. + */ +EAPI void +eina_log_print_cb_stderr(const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line, + const char *fmt, + __UNUSED__ void *data, + va_list args) +{ + _eina_log_print_prefix(stderr, d, level, file, fnc, line); + vfprintf(stderr, fmt, args); + putc('\n', stderr); +} + +/** + * Alternative logging method, this will output to standard output stream. + * + * @param d The domain. + * @param level The level. + * @param file The file which is logged. + * @param fnc The function which is logged. + * @param line The line which is logged. + * @param fmt The ouptut format to use. + * @param data Not used. + * @param args The arguments needed by the format. + * + * This method will colorize output based on domain provided color and + * message logging level. To disable color, set environment variable + * EINA_LOG_COLOR_DISABLE=1. Similarly, to disable file and line + * information, set EINA_LOG_FILE_DISABLE=1 or + * EINA_LOG_FUNCTION_DISABLE=1 to avoid function name in output. It is + * not acceptable to have both EINA_LOG_FILE_DISABLE and + * EINA_LOG_FUNCTION_DISABLE at the same time, in this case just + * EINA_LOG_FUNCTION_DISABLE will be considered and file information + * will be printed anyways. + * + * @note MT: if threads are enabled, this function is called within locks. + * @note MT: Threads different from main thread will have thread id + * appended to domain name. + */ +EAPI void +eina_log_print_cb_stdout(const Eina_Log_Domain *d, + Eina_Log_Level level, + const char *file, + const char *fnc, + int line, + const char *fmt, + __UNUSED__ void *data, + va_list args) +{ + _eina_log_print_prefix(stdout, d, level, file, fnc, line); + vprintf(fmt, args); + putchar('\n'); +} + +/** + * Alternative logging method, this will output to given file stream. + * + * @param d The domain. + * @param level Not used. + * @param file The file which is logged. + * @param fnc The function which is logged. + * @param line The line which is logged. + * @param fmt The ouptut format to use. + * @param data The file which will store the output (as a FILE *). + * @param args The arguments needed by the format. + * + * This method will never output color. + * + * @note MT: if threads are enabled, this function is called within locks. + * @note MT: Threads different from main thread will have thread id + * appended to domain name. + */ +EAPI void +eina_log_print_cb_file(const Eina_Log_Domain *d, + __UNUSED__ Eina_Log_Level level, + const char *file, + const char *fnc, + int line, + const char *fmt, + void *data, + va_list args) +{ + FILE *f = data; +#ifdef EFL_HAVE_THREADS + if (_threads_enabled) + { + Thread cur; + + cur = SELF(); + if (IS_OTHER(cur)) + { + fprintf(f, "%s[T:%lu] %s:%d %s() ", d->name, (unsigned long)cur, + file, line, fnc); + goto end; + } + } + +#endif + fprintf(f, "%s %s:%d %s() ", d->name, file, line, fnc); +#ifdef EFL_HAVE_THREADS +end: +#endif + vfprintf(f, fmt, args); + putc('\n', f); +} + +/** + * Print out log message using given domain and level. + * + * @note Usually you'll not use this function directly but the helper + * macros EINA_LOG(), EINA_LOG_DOM_CRIT(), EINA_LOG_CRIT() and + * so on. See eina_log.h + * + * @param domain logging domain to use or @c EINA_LOG_DOMAIN_GLOBAL if + * you registered none. It is recommended that modules and + * applications have their own logging domain. + * @param level message level, those with level greater than user + * specified value (eina_log_level_set() or environment + * variables EINA_LOG_LEVEL, EINA_LOG_LEVELS) will be ignored. + * @param file filename that originated the call, must @b not be @c NULL. + * @param fnc function that originated the call, must @b not be @c NULL. + * @param line originating line in @a file. + * @param fmt printf-like format to use. Should not provide trailing + * '\n' as it is automatically included. + * + * @note MT: this function may be called from different threads if + * eina_log_threads_enable() was called before. + */ +EAPI void +eina_log_print(int domain, Eina_Log_Level level, const char *file, + const char *fnc, int line, const char *fmt, ...) +{ + va_list args; + +#ifdef EINA_SAFETY_CHECKS + if (EINA_UNLIKELY(!file)) + { + fputs("ERR: eina_log_print() file == NULL\n", stderr); + return; + } + + if (EINA_UNLIKELY(!fnc)) + { + fputs("ERR: eina_log_print() fnc == NULL\n", stderr); + return; + } + + if (EINA_UNLIKELY(!fmt)) + { + fputs("ERR: eina_log_print() fmt == NULL\n", stderr); + return; + } + +#endif + va_start(args, fmt); + LOG_LOCK(); + eina_log_print_unlocked(domain, level, file, fnc, line, fmt, args); + LOG_UNLOCK(); + va_end(args); +} + +/** + * Print out log message using given domain and level. + * + * @note Usually you'll not use this function directly but the helper + * macros EINA_LOG(), EINA_LOG_DOM_CRIT(), EINA_LOG_CRIT() and + * so on. See eina_log.h + * + * @param domain logging domain to use or @c EINA_LOG_DOMAIN_GLOBAL if + * you registered none. It is recommended that modules and + * applications have their own logging domain. + * @param level message level, those with level greater than user + * specified value (eina_log_level_set() or environment + * variables EINA_LOG_LEVEL, EINA_LOG_LEVELS) will be ignored. + * @param file filename that originated the call, must @b not be @c NULL. + * @param fnc function that originated the call, must @b not be @c NULL. + * @param line originating line in @a file. + * @param fmt printf-like format to use. Should not provide trailing + * '\n' as it is automatically included. + * @param args the arguments needed by the format. + * + * @note MT: this function may be called from different threads if + * eina_log_threads_enable() was called before. + * + * @see eina_log_print() + */ +EAPI void +eina_log_vprint(int domain, Eina_Log_Level level, const char *file, + const char *fnc, int line, const char *fmt, va_list args) +{ +#ifdef EINA_SAFETY_CHECKS + if (EINA_UNLIKELY(!file)) + { + fputs("ERR: eina_log_print() file == NULL\n", stderr); + return; + } + + if (EINA_UNLIKELY(!fnc)) + { + fputs("ERR: eina_log_print() fnc == NULL\n", stderr); + return; + } + + if (EINA_UNLIKELY(!fmt)) + { + fputs("ERR: eina_log_print() fmt == NULL\n", stderr); + return; + } + +#endif + LOG_LOCK(); + eina_log_print_unlocked(domain, level, file, fnc, line, fmt, args); + LOG_UNLOCK(); +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_magic.c b/tests/suite/ecore/src/lib/eina_magic.c new file mode 100644 index 0000000000..83cd247d26 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_magic.c @@ -0,0 +1,487 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_error.h" +#include "eina_log.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_magic.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +typedef struct _Eina_Magic_String Eina_Magic_String; +struct _Eina_Magic_String +{ + Eina_Magic magic; + Eina_Bool string_allocated; + const char *string; +}; + +static int _eina_magic_string_log_dom = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_magic_string_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_magic_string_log_dom, __VA_ARGS__) + +static Eina_Magic_String *_eina_magic_strings = NULL; +static size_t _eina_magic_strings_count = 0; +static size_t _eina_magic_strings_allocated = 0; +static Eina_Bool _eina_magic_strings_dirty = 0; + +static int +_eina_magic_strings_sort_cmp(const void *p1, const void *p2) +{ + const Eina_Magic_String *a = p1, *b = p2; + return a->magic - b->magic; +} + +static int +_eina_magic_strings_find_cmp(const void *p1, const void *p2) +{ + Eina_Magic a = (long)p1; + const Eina_Magic_String *b = p2; + return a - b->magic; +} + +static Eina_Magic_String * +_eina_magic_strings_alloc(void) +{ + size_t idx; + + if (_eina_magic_strings_count == _eina_magic_strings_allocated) + { + void *tmp; + size_t size; + + if (EINA_UNLIKELY(_eina_magic_strings_allocated == 0)) + size = 48; + else + size = _eina_magic_strings_allocated + 16; + + tmp = realloc(_eina_magic_strings, sizeof(Eina_Magic_String) * size); + if (!tmp) + { +#ifdef _WIN32 + ERR("could not realloc magic_strings from %Iu to %Iu buckets.", +#else + ERR("could not realloc magic_strings from %zu to %zu buckets.", +#endif + _eina_magic_strings_allocated, size); + return NULL; + } + + _eina_magic_strings = tmp; + _eina_magic_strings_allocated = size; + } + + idx = _eina_magic_strings_count; + _eina_magic_strings_count++; + return _eina_magic_strings + idx; +} + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the magic string module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the magic string module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_magic_string_init(void) +{ + _eina_magic_string_log_dom = eina_log_domain_register + ("eina_magic_string", EINA_LOG_COLOR_DEFAULT); + if (_eina_magic_string_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_magic_string"); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the magic string module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the magic string module set up by + * eina_magic string_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_magic_string_shutdown(void) +{ + Eina_Magic_String *ems, *ems_end; + + ems = _eina_magic_strings; + ems_end = ems + _eina_magic_strings_count; + + for (; ems < ems_end; ems++) + if (ems->string_allocated) + free((char *)ems->string); + + free(_eina_magic_strings); + _eina_magic_strings = NULL; + _eina_magic_strings_count = 0; + _eina_magic_strings_allocated = 0; + + eina_log_domain_unregister(_eina_magic_string_log_dom); + _eina_magic_string_log_dom = -1; + + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Magic_Group Magic + * + * @brief These functions provide runtime type-checking (magic checks) + * management for projects. + * + * C is a weak statically typed language, in other words, it will just + * check for types during compile time and any cast will make the + * compiler believe the type is correct. + * + * In real world projects we often need to deal with casts, either + * explicit or implicit by means of @c void*. We also need to resort + * to casts when doing inheritance in C, as seen in the example below: + * + * @code + * struct base { + * int id; + * char *name; + * }; + * int base_id_get(struct base *ptr) { + * return ptr->id; + * } + * + * struct subtype { + * struct base base; + * time_t date; + * }; + * @endcode + * + * It is perfectly valid to use @c {struct subtype} blobs for functions + * that expect @c {struct base}, since the fields will have the same + * offset (as base member is the first, at offset 0). We could give + * the functions the @c {&subtype->base} and avoid the cast, but often + * we just cast. + * + * In any case, we might be safe and check if the given pointer is + * actually of the expected type. We can do so by using eina_magic, + * that is nothing more than attaching an unique type identifier to + * the members and check for it elsewhere. + * + * @code + * #define BASE_MAGIC 0x12345 + * #define SUBTYPE_MAGIC 0x3333 + * struct base { + * int id; + * char *name; + * EINA_MAGIC; + * }; + * int base_id_get(struct base *ptr) { + * if (!EINA_MAGIC_CHECK(ptr, BASE_MAGIC)) { + * EINA_MAGIC_FAIL(ptr, BASE_MAGIC); + * return -1; + * } + * return ptr->id; + * } + * void base_free(struct base *ptr) { + * if (!EINA_MAGIC_CHECK(ptr, BASE_MAGIC)) { + * EINA_MAGIC_FAIL(ptr, BASE_MAGIC); + * return; + * } + * EINA_MAGIC_SET(ptr, EINA_MAGIC_NONE); + * free(ptr->name); + * free(ptr); + * } + * struct base *base_new(int id, const char *name) { + * struct base *ptr = malloc(sizeof(struct base)); + * EINA_MAGIC_SET(ptr, BASE_MAGIC); + * ptr->id = id; + * ptr->name = strdup(name); + * } + * + * struct subtype { + * struct base base; + * EINA_MAGIC; + * time_t date; + * }; + * + * int my_init(void) { + * eina_init(); + * eina_magic_string_set(BASE_MAGIC, "base type"); + * eina_magic_string_set(SUBTYPE_MAGIC, "subtype"); + * } + * @endcode + * + * This code also shows that it is a good practice to set magic to + * #EINA_MAGIC_NONE before freeing pointer. Sometimes the pointers are + * in pages that are still live in memory, so kernel will not send + * SEGV signal to the process and it may go unnoticed that you're + * using already freed pointers. By setting them to #EINA_MAGIC_NONE + * you avoid using the bogus pointer any further and gets a nice error + * message. + * + * @{ + */ + +/** + * @brief Return the string associated to the given magic identifier. + * + * @param magic The magic identifier. + * @return The string associated to the identifier. + * + * This function returns the string associated to @p magic. If none + * are found, the this function still returns non @c NULL, in this + * case an identifier such as "(none)", "(undefined)" or + * "(unknown)". The returned value must not be freed. + * + * The following identifiers may be returned whenever magic is + * invalid, with their meanings: + * + * - (none): no magic was registered exists at all. + * - (undefined): magic was registered and found, but no string associated. + * - (unknown): magic was not found in the registry. + */ +EAPI const char * +eina_magic_string_get(Eina_Magic magic) +{ + Eina_Magic_String *ems; + + if (!_eina_magic_strings) + return "(none)"; + + if (_eina_magic_strings_dirty) + { + qsort(_eina_magic_strings, _eina_magic_strings_count, + sizeof(Eina_Magic_String), _eina_magic_strings_sort_cmp); + _eina_magic_strings_dirty = 0; + } + + ems = bsearch((void *)(long)magic, _eina_magic_strings, + _eina_magic_strings_count, sizeof(Eina_Magic_String), + _eina_magic_strings_find_cmp); + if (ems) + return ems->string ? ems->string : "(undefined)"; + + return "(unknown)"; +} + +/** + * @brief Set the string associated to the given magic identifier. + * + * @param magic The magic identifier. + * @param magic_name The string associated to the identifier, must not + * be @c NULL. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets the string @p magic_name to @p magic. It is not + * checked if number or string are already set, then you might end + * with duplicates in that case. + * + * @see eina_magic_string_static_set() + */ +EAPI Eina_Bool +eina_magic_string_set(Eina_Magic magic, const char *magic_name) +{ + Eina_Magic_String *ems; + + EINA_SAFETY_ON_NULL_RETURN_VAL(magic_name, EINA_FALSE); + + ems = _eina_magic_strings_alloc(); + if (!ems) + return EINA_FALSE; + + ems->magic = magic; + ems->string_allocated = EINA_TRUE; + ems->string = strdup(magic_name); + if (!ems->string) + { + ERR("could not allocate string '%s'", magic_name); + _eina_magic_strings_count--; + return EINA_FALSE; + } + + _eina_magic_strings_dirty = 1; + return EINA_TRUE; +} + +/** + * @brief Set the string associated to the given magic identifier. + * + * @param magic The magic identifier. + * @param magic_name The string associated to the identifier, must not be + * @c NULL, it will not be duplcated, just referenced thus it must + * be live during magic number usage. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets the string @p magic_name to @p magic. It is not + * checked if number or string are already set, then you might end + * with duplicates in that case. + * + * @see eina_magic_string_set() + */ +EAPI Eina_Bool +eina_magic_string_static_set(Eina_Magic magic, const char *magic_name) +{ + Eina_Magic_String *ems; + + EINA_SAFETY_ON_NULL_RETURN_VAL(magic_name, EINA_FALSE); + + ems = _eina_magic_strings_alloc(); + if (!ems) + return EINA_FALSE; + + ems->magic = magic; + ems->string_allocated = EINA_FALSE; + ems->string = magic_name; + + _eina_magic_strings_dirty = 1; + return EINA_TRUE; +} + +#ifdef eina_magic_fail +# undef eina_magic_fail +#endif + +/** + * @brief Display a message or abort is a magic check failed. + * + * @param d The checked data pointer. + * @param m The magic identifer to check. + * @param req_m The requested magic identifier to check. + * @param file The file in which the magic check failed. + * @param fnc The function in which the magic check failed. + * @param line The line at which the magic check failed. + * + * This function displays an error message if a magic check has + * failed, using the following logic in the following order: + * @li If @p d is @c NULL, a message warns about a @c NULL pointer. + * @li Otherwise, if @p m is equal to #EINA_MAGIC_NONE, a message + * warns about a handle that was already freed. + * @li Otherwise, if @p m is equal to @p req_m, a message warns about + * a handle that is of wrong type. + * @li Otherwise, a message warns you about ab-using that function... + * + * If the environment variable EINA_ERROR_ABORT is set, abort() is + * called and the program stops. It is useful for debugging programs + * with gdb. + */ +EAPI void +eina_magic_fail(void *d, + Eina_Magic m, + Eina_Magic req_m, + const char *file, + const char *fnc, + int line) +{ + if (!d) + eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL, + file, fnc, line, + "*** Eina Magic Check Failed !!!\n" + " Input handle pointer is NULL !\n" + "*** NAUGHTY PROGRAMMER!!!\n" + "*** SPANK SPANK SPANK!!!\n" + "*** Now go fix your code. Tut tut tut!\n" + "\n"); + else + if (m == EINA_MAGIC_NONE) + eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL, + file, fnc, line, + "*** Eina Magic Check Failed !!!\n" + " Input handle has already been freed!\n" + "*** NAUGHTY PROGRAMMER!!!\n" + "*** SPANK SPANK SPANK!!!\n" + "*** Now go fix your code. Tut tut tut!\n" + "\n"); + else + if (m != req_m) + eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL, + file, fnc, line, + "*** Eina Magic Check Failed !!!\n" + " Input handle is wrong type\n" + " Expected: %08x - %s\n" + " Supplied: %08x - %s\n" + "*** NAUGHTY PROGRAMMER!!!\n" + "*** SPANK SPANK SPANK!!!\n" + "*** Now go fix your code. Tut tut tut!\n" + "\n", + req_m, eina_magic_string_get(req_m), + m, eina_magic_string_get(m)); + else + eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL, + file, fnc, line, + "*** Eina Magic Check Failed !!!\n" + " Why did you call me !\n" + "*** NAUGHTY PROGRAMMER!!!\n" + "*** SPANK SPANK SPANK!!!\n" + "*** Now go fix your code. Tut tut tut!\n" + "\n"); +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_main.c b/tests/suite/ecore/src/lib/eina_main.c new file mode 100644 index 0000000000..ceae398363 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_main.c @@ -0,0 +1,377 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#ifdef EFL_HAVE_POSIX_THREADS +# include <pthread.h> +#endif + +#ifdef EFL_HAVE_WIN32_THREADS +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_types.h" +#include "eina_main.h" +#include "eina_error.h" +#include "eina_log.h" +#include "eina_hash.h" +#include "eina_binshare.h" +#include "eina_stringshare.h" +#include "eina_ustringshare.h" +#include "eina_list.h" +#include "eina_matrixsparse.h" +#include "eina_array.h" +#include "eina_counter.h" +#include "eina_benchmark.h" +#include "eina_magic.h" +#include "eina_rectangle.h" +#include "eina_safety_checks.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static Eina_Version _version = { VMAJ, VMIN, VMIC, VREV }; + +static int _eina_main_count = 0; +#ifdef EFL_HAVE_THREADS +static int _eina_main_thread_count = 0; +#endif +static int _eina_log_dom = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_log_dom, __VA_ARGS__) + +#ifdef EFL_HAVE_THREADS +static Eina_Bool _threads_activated = EINA_FALSE; +# ifdef EFL_HAVE_POSIX_THREADS +static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER; +# define LOCK() if(_threads_activated) pthread_mutex_lock(&_mutex) +# define UNLOCK() if(_threads_activated) pthread_mutex_unlock(&_mutex) +# define UNLOCK_FORCE() pthread_mutex_unlock(&_mutex) +# else /* EFL_HAVE_WIN32_THREADS */ +static HANDLE _mutex = NULL; +# define LOCK() if(_threads_activated) WaitForSingleObject(_mutex, INFINITE) +# define UNLOCK() if(_threads_activated) ReleaseMutex(_mutex) +# define UNLOCK_FORCE() ReleaseMutex(_mutex) +# endif +#else +# define LOCK() do {} while (0) +# define UNLOCK() do {} while (0) +# define UNLOCK_FORCE() do {} while (0) +#endif + +/* place module init/shutdown functions here to avoid other modules + * calling them by mistake. + */ +#define S(x) extern Eina_Bool eina_ ## x ## _init(void); \ + extern Eina_Bool eina_ ## x ## _shutdown(void) + S(log); + S(error); + S(safety_checks); + S(magic_string); + S(iterator); + S(accessor); + S(array); + S(module); + S(mempool); + S(list); + S(binshare); + S(stringshare); + S(ustringshare); + S(matrixsparse); + S(convert); + S(counter); + S(benchmark); + S(rectangle); + S(strbuf); + S(ustrbuf); + S(quadtree); +#undef S + +struct eina_desc_setup +{ + const char *name; + Eina_Bool (*init)(void); + Eina_Bool (*shutdown)(void); +}; + +static const struct eina_desc_setup _eina_desc_setup[] = { +#define S(x) {# x, eina_ ## x ## _init, eina_ ## x ## _shutdown} + /* log is a special case as it needs printf */ + S(error), + S(safety_checks), + S(magic_string), + S(iterator), + S(accessor), + S(array), + S(module), + S(mempool), + S(list), + S(binshare), + S(stringshare), + S(ustringshare), + S(matrixsparse), + S(convert), + S(counter), + S(benchmark), + S(rectangle), + S(strbuf), + S(ustrbuf), + S(quadtree) +#undef S +}; +static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) / + sizeof(_eina_desc_setup[0]); + +static void +_eina_shutdown_from_desc(const struct eina_desc_setup *itr) +{ + for (itr--; itr >= _eina_desc_setup; itr--) + { + if (!itr->shutdown()) + ERR("Problems shutting down eina module '%s', ignored.", itr->name); + } + + eina_log_domain_unregister(_eina_log_dom); + _eina_log_dom = -1; + eina_log_shutdown(); +} + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Main_Group Main + * + * @brief These functions provide general initialisation and shut down + * functions. + * + * @{ + */ + +/** + * @var eina_version + * @brief Eina version (defined at configuration time) + */ +EAPI Eina_Version *eina_version = &_version; + +/** + * @brief Initialize the Eina library. + * + * @return 1 or greater on success, 0 on error. + * + * This function sets up all the eina modules. It returns 0 on + * failure (that is, when one of the module fails to initialize), + * otherwise it returns the number of times it has already been + * called. + * + * When Eina is not used anymore, call eina_shutdown() to shut down + * the Eina library. + */ +EAPI int +eina_init(void) +{ + const struct eina_desc_setup *itr, *itr_end; + + if (EINA_LIKELY(_eina_main_count > 0)) + return ++_eina_main_count; + + if (!eina_log_init()) + { + fprintf(stderr, "Could not initialize eina logging system.\n"); + return 0; + } + + _eina_log_dom = eina_log_domain_register("eina", EINA_LOG_COLOR_DEFAULT); + if (_eina_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina"); + eina_log_shutdown(); + return 0; + } + + itr = _eina_desc_setup; + itr_end = itr + _eina_desc_setup_len; + for (; itr < itr_end; itr++) + { + if (!itr->init()) + { + ERR("Could not initialize eina module '%s'.", itr->name); + _eina_shutdown_from_desc(itr); + return 0; + } + } + + _eina_main_count = 1; + return 1; +} + +/** + * @brief Shut down the Eina library. + * + * @return 0 when all the modules is completely shut down, 1 or + * greater otherwise. + * + * This function shuts down the Eina library. It returns 0 when it has + * been called the same number of times than eina_init(). In that case + * it shut down all the Eina modules. + * + * Once this function succeeds (that is, @c 0 is returned), you must + * not call any of the Eina function anymore. You must call + * eina_init() again to use the Eina functions again. + */ +EAPI int +eina_shutdown(void) +{ + _eina_main_count--; + if (EINA_UNLIKELY(_eina_main_count == 0)) + _eina_shutdown_from_desc(_eina_desc_setup + _eina_desc_setup_len); + + return _eina_main_count; +} + + +/** + * @brief Initialize the mutexes of the Eina library. + * + * @return 1 or greater on success, 0 on error. + * + * This function sets up all the mutexes in all eina modules. It returns 0 on + * failure (that is, when one of the module fails to initialize), + * otherwise it returns the number of times it has already been + * called. + * + * When the mutexes are not used anymore, call eina_threads_shutdown() to shut down + * the mutexes. + */ +EAPI int +eina_threads_init(void) +{ +#ifdef EFL_HAVE_THREADS + int ret; + +# ifdef EFL_HAVE_WIN32_THREADS + if (!_mutex) + _mutex = CreateMutex(NULL, FALSE, NULL); + + if (!_mutex) + return 0; + +# endif + + LOCK(); + ++_eina_main_thread_count; + ret = _eina_main_thread_count; + + if(_eina_main_thread_count > 1) + { + UNLOCK(); + return ret; + } + + eina_share_common_threads_init(); + eina_log_threads_init(); + _threads_activated = EINA_TRUE; + + return ret; +#else + return 0; +#endif +} + +/** + * @brief Shut down mutexes in the Eina library. + * + * @return 0 when all mutexes are completely shut down, 1 or + * greater otherwise. + * + * This function shuts down the mutexes in the Eina library. It returns 0 when it has + * been called the same number of times than eina_threads_init(). In that case + * it shut down all the mutexes. + * + * Once this function succeeds (that is, @c 0 is returned), you must + * not call any of the Eina function in a thread anymore. You must call + * eina_threads_init() again to use the Eina functions in a thread again. + */ +EAPI int +eina_threads_shutdown(void) +{ +#ifdef EFL_HAVE_THREADS + int ret; + + LOCK(); + ret = --_eina_main_thread_count; + if(_eina_main_thread_count > 0) + { + UNLOCK(); + return ret; + } + + eina_share_common_threads_shutdown(); + eina_log_threads_shutdown(); + + _threads_activated = EINA_FALSE; + + UNLOCK_FORCE(); + +# ifdef EFL_HAVE_WIN32_THREADS + if (_mutex) + CloseHandle(_mutex); + +# endif + + return ret; +#else + return 0; +#endif +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_matrixsparse.c b/tests/suite/ecore/src/lib/eina_matrixsparse.c new file mode 100644 index 0000000000..e19f24aa86 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_matrixsparse.c @@ -0,0 +1,1692 @@ +/* EINA - EFL data type library + * Copyright (C) 2009 Gustavo Sverzut Barbieri + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +/** + * @page tutorial_matrixsparse_page Sparse Matrix Tutorial + * + * to be written... + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_error.h" +#include "eina_log.h" +#include "eina_magic.h" +#include "eina_mempool.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_matrixsparse.h" + + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static const char EINA_MAGIC_MATRIXSPARSE_STR[] = "Eina Matrixsparse"; +static const char EINA_MAGIC_MATRIXSPARSE_ROW_STR[] = "Eina Matrixsparse Row"; +static const char EINA_MAGIC_MATRIXSPARSE_CELL_STR[] = "Eina Matrixsparse Cell"; +static const char EINA_MAGIC_MATRIXSPARSE_ITERATOR_STR[] = + "Eina Matrixsparse Iterator"; +static const char EINA_MAGIC_MATRIXSPARSE_ROW_ACCESSOR_STR[] = + "Eina Matrixsparse Row Accessor"; +static const char EINA_MAGIC_MATRIXSPARSE_ROW_ITERATOR_STR[] = + "Eina Matrixsparse Row Iterator"; +static const char EINA_MAGIC_MATRIXSPARSE_CELL_ACCESSOR_STR[] = + "Eina Matrixsparse Cell Accessor"; +static const char EINA_MAGIC_MATRIXSPARSE_CELL_ITERATOR_STR[] = + "Eina Matrixsparse Cell Iterator"; + + +#define EINA_MAGIC_CHECK_MATRIXSPARSE(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_MATRIXSPARSE)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_MATRIXSPARSE); \ + return __VA_ARGS__; \ + } \ + } while(0) + +#define EINA_MAGIC_CHECK_MATRIXSPARSE_ROW(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_MATRIXSPARSE_ROW)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_MATRIXSPARSE_ROW); \ + return __VA_ARGS__; \ + } \ + } while(0) + +#define EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_MATRIXSPARSE_CELL)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_MATRIXSPARSE_CELL); \ + return __VA_ARGS__; \ + } \ + } while(0) + +#define EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_MATRIXSPARSE_ITERATOR)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_MATRIXSPARSE_ITERATOR); \ + return __VA_ARGS__; \ + } \ + } while(0) + +struct _Eina_Matrixsparse_Cell +{ + Eina_Matrixsparse_Cell *next; + Eina_Matrixsparse_Cell *prev; + + void *data; + unsigned long col; + + Eina_Matrixsparse_Row *parent; + + EINA_MAGIC +}; + +struct _Eina_Matrixsparse_Row +{ + Eina_Matrixsparse_Row *next; + Eina_Matrixsparse_Row *prev; + + Eina_Matrixsparse_Cell *cols; + Eina_Matrixsparse_Cell *last_col; + Eina_Matrixsparse_Cell *last_used; /* fast sequential access */ + unsigned long row; + + Eina_Matrixsparse *parent; + + EINA_MAGIC +}; + +struct _Eina_Matrixsparse +{ + Eina_Matrixsparse_Row *rows; + Eina_Matrixsparse_Row *last_row; + Eina_Matrixsparse_Row *last_used; /* fast sequential access */ + + struct + { + unsigned long rows; + unsigned long cols; + } size; + + struct + { + void (*func)(void *user_data, void *cell_data); + void *user_data; + } free; + + EINA_MAGIC +}; + +typedef struct _Eina_Matrixsparse_Iterator Eina_Matrixsparse_Iterator; +typedef struct _Eina_Matrixsparse_Iterator_Complete +Eina_Matrixsparse_Iterator_Complete; + +struct _Eina_Matrixsparse_Iterator +{ + Eina_Iterator iterator; + + const Eina_Matrixsparse *m; + struct + { + const Eina_Matrixsparse_Row *row; + const Eina_Matrixsparse_Cell *col; + } ref; + + EINA_MAGIC +}; + +struct _Eina_Matrixsparse_Iterator_Complete +{ + Eina_Iterator iterator; + + const Eina_Matrixsparse *m; + struct + { + const Eina_Matrixsparse_Row *row; + const Eina_Matrixsparse_Cell *col; + } ref; + + struct + { + unsigned long row, col; + } idx; + + struct + { + Eina_Matrixsparse_Row row; + Eina_Matrixsparse_Cell col; + } dummy; + + EINA_MAGIC +}; + +/** + * @todo Eina_Matrixsparse_Row_Iterator: iterator over rows in matrix + * @todo Eina_Matrixsparse_Row_Accessor: accessor over rows in matrix + * @todo Eina_Matrixsparse_Cell_Iterator: iterator over cells in row + * @todo Eina_Matrixsparse_Cell_Accessor: accessor over cells in row + */ + +static int _eina_matrixsparse_log_dom = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_matrixsparse_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_matrixsparse_log_dom, __VA_ARGS__) + +static Eina_Mempool *_eina_matrixsparse_cell_mp = NULL; +static Eina_Mempool *_eina_matrixsparse_row_mp = NULL; + +static inline void +_eina_matrixsparse_cell_free(Eina_Matrixsparse_Cell *c, void (*free_func)( + void *, + void *), void *user_data) +{ + if (free_func) + free_func(user_data, c->data); + + EINA_MAGIC_SET(c, EINA_MAGIC_NONE); + eina_mempool_free(_eina_matrixsparse_cell_mp, c); +} + +static inline void +_eina_matrixsparse_cell_unlink(Eina_Matrixsparse_Cell *c) +{ + Eina_Matrixsparse_Row *r = c->parent; + + if (r->last_used == c) + { + if (c->next) + r->last_used = c->next; + else + r->last_used = c->prev; + } + + if (r->last_col == c) + r->last_col = c->prev; + + if (r->cols == c) + r->cols = c->next; + + if (c->next && c->prev) + { + c->next->prev = c->prev; + c->prev->next = c->next; + } + else if (c->next) + c->next->prev = NULL; + else if (c->prev) + c->prev->next = NULL; +} + +static inline void +_eina_matrixsparse_row_cells_free(Eina_Matrixsparse_Row *r, void (*free_func)( + void *, + void *), void *user_data) +{ + Eina_Matrixsparse_Cell *c = r->cols; + while (c) + { + Eina_Matrixsparse_Cell *c_aux = c; + c = c->next; + _eina_matrixsparse_cell_free(c_aux, free_func, user_data); + } +} + +static inline void +_eina_matrixsparse_row_free(Eina_Matrixsparse_Row *r, void (*free_func)(void *, + void *), + void *user_data) +{ + _eina_matrixsparse_row_cells_free(r, free_func, user_data); + EINA_MAGIC_SET(r, EINA_MAGIC_NONE); + eina_mempool_free(_eina_matrixsparse_row_mp, r); +} + +static inline void +_eina_matrixsparse_row_unlink(Eina_Matrixsparse_Row *r) +{ + Eina_Matrixsparse *m = r->parent; + + if (m->last_used == r) + { + if (r->next) + m->last_used = r->next; + else + m->last_used = r->prev; + } + + if (m->last_row == r) + m->last_row = r->prev; + + if (m->rows == r) + m->rows = r->next; + + if (r->next && r->prev) + { + r->next->prev = r->prev; + r->prev->next = r->next; + } + else if (r->next) + r->next->prev = NULL; + else if (r->prev) + r->prev->next = NULL; +} + +static inline void +_eina_matrixsparse_row_find_parms_get(const Eina_Matrixsparse *m, + unsigned long row, + Eina_Matrixsparse_Row **p_r, + int *p_dir) +{ + Eina_Matrixsparse_Row *r; + unsigned long dist; + int dir; + + dist = row - m->rows->row; + r = m->rows; + dir = 1; + if (dist > m->last_row->row - row) + { + dist = m->last_row->row - row; + r = m->last_row; + dir = -1; + } + + if (m->last_used) + { + if (m->last_used->row < row) + { + if (dist > row - m->last_used->row) + { +/* dist = row = m->last_used->row; */ + r = m->last_used; + dir = 1; + } + } + else if (dist > m->last_used->row - row) + { +/* dist = m->last_used->row - row; */ + r = m->last_used; + dir = -1; + } + } + + *p_r = r; + *p_dir = dir; +} + +static inline void +_eina_matrixsparse_row_cell_find_parms_get(const Eina_Matrixsparse_Row *r, + unsigned long col, + Eina_Matrixsparse_Cell **p_c, + int *p_dir) +{ + Eina_Matrixsparse_Cell *c; + unsigned long dist; + int dir; + + dist = col - r->cols->col; + c = r->cols; + dir = 1; + if (dist > r->last_col->col - col) + { + dist = r->last_col->col - col; + c = r->last_col; + dir = -1; + } + + if (r->last_used) + { + if (r->last_used->col < col) + { + if (dist > col - r->last_used->col) + { +/* dist = col = r->last_used->col; */ + c = r->last_used; + dir = 1; + } + } + else if (dist > r->last_used->col - col) + { +/* dist = r->last_used->col - col; */ + c = r->last_used; + dir = -1; + } + } + + *p_c = c; + *p_dir = dir; +} + +static inline Eina_Matrixsparse_Row * +_eina_matrixsparse_row_idx_get(const Eina_Matrixsparse *m, unsigned long row) +{ + Eina_Matrixsparse_Row *r; + int dir; + + if (!m->rows) + return NULL; + + if (m->rows->row == row) + return m->rows; + else if (m->rows->row > row) + return NULL; + + if (m->last_row->row == row) + return m->last_row; + else if (m->last_row->row < row) + return NULL; + + if ((m->last_used) && (m->last_used->row == row)) + return m->last_used; + + _eina_matrixsparse_row_find_parms_get(m, row, &r, &dir); + assert(dir != 0); + if (dir > 0) + { + for (; r; r = r->next) + if (r->row == row) + { + ((Eina_Matrixsparse *)m)->last_used = r; + return r; + } + else if (r->row > row) + return NULL; + + } + else if (dir < 0) + { + for (; r; r = r->prev) + if (r->row == row) + { + ((Eina_Matrixsparse *)m)->last_used = r; + return r; + } + else if (r->row < row) + return NULL; + } + + return NULL; +} + +static inline Eina_Matrixsparse_Cell * +_eina_matrixsparse_row_cell_idx_get(const Eina_Matrixsparse_Row *r, + unsigned long col) +{ + Eina_Matrixsparse_Cell *c; + int dir; + + if (!r->cols) + return NULL; + + if (r->cols->col == col) + return r->cols; + else if (r->cols->col > col) + return NULL; + + if (r->last_col->col == col) + return r->last_col; + else if (r->last_col->col < col) + return NULL; + + if ((r->last_used) && (r->last_used->col == col)) + return r->last_used; + + _eina_matrixsparse_row_cell_find_parms_get(r, col, &c, &dir); + assert(dir != 0); + if (dir > 0) + { + for (; r; c = c->next) + if (c->col == col) + { + ((Eina_Matrixsparse_Row *)r)->last_used = c; + return c; + } + else if (c->col > col) + return NULL; + + } + else if (dir < 0) + { + for (; r; c = c->prev) + if (c->col == col) + { + ((Eina_Matrixsparse_Row *)r)->last_used = c; + return c; + } + else if (c->col < col) + return NULL; + } + + return NULL; +} + +static inline Eina_Matrixsparse_Cell * +_eina_matrixsparse_cell_idx_get(const Eina_Matrixsparse *m, + unsigned long row, + unsigned long col) +{ + Eina_Matrixsparse_Row *r = _eina_matrixsparse_row_idx_get(m, row); + if (!r) + return NULL; + + return _eina_matrixsparse_row_cell_idx_get(r, col); +} + +static inline void +_eina_matrixsparse_row_idx_siblings_find(const Eina_Matrixsparse *m, + unsigned long row, + Eina_Matrixsparse_Row **p_prev, + Eina_Matrixsparse_Row **p_next) +{ + Eina_Matrixsparse_Row *r; + int dir; + + _eina_matrixsparse_row_find_parms_get(m, row, &r, &dir); + assert(dir != 0); + if (dir > 0) + { + for (; r; r = r->next) + if (r->row > row) + break; + + assert(r != NULL); + *p_prev = r->prev; + *p_next = r; + } + else if (dir < 0) + { + for (; r; r = r->prev) + if (r->row < row) + break; + + assert(r != NULL); + *p_prev = r; + *p_next = r->next; + } +} + +static inline void +_eina_matrixsparse_row_cell_idx_siblings_find(const Eina_Matrixsparse_Row *r, + unsigned long col, + Eina_Matrixsparse_Cell **p_prev, + Eina_Matrixsparse_Cell **p_next) +{ + Eina_Matrixsparse_Cell *c; + int dir; + + _eina_matrixsparse_row_cell_find_parms_get(r, col, &c, &dir); + assert(dir != 0); + if (dir > 0) + { + for (; c; c = c->next) + if (c->col > col) + break; + + assert(c != NULL); + *p_prev = c->prev; + *p_next = c; + } + else if (dir < 0) + { + for (; c; c = c->prev) + if (c->col < col) + break; + + assert(c != NULL); + *p_prev = c; + *p_next = c->next; + } +} + +static inline Eina_Matrixsparse_Row * +_eina_matrixsparse_row_idx_add(Eina_Matrixsparse *m, unsigned long row) +{ + Eina_Matrixsparse_Row *r = eina_mempool_malloc + (_eina_matrixsparse_row_mp, sizeof(Eina_Matrixsparse_Row)); + if (!r) + return NULL; + + if (!m->rows) + { + r->prev = NULL; + r->next = NULL; + m->rows = r; + m->last_row = r; + } + else if (row < m->rows->row) + { + r->prev = NULL; + r->next = m->rows; + m->rows->prev = r; + m->rows = r; + } + else if (row > m->last_row->row) + { + r->prev = m->last_row; + m->last_row->next = r; + r->next = NULL; + m->last_row = r; + } + else + { + Eina_Matrixsparse_Row *prev = NULL, *next = NULL; + _eina_matrixsparse_row_idx_siblings_find(m, row, &prev, &next); + assert(prev != NULL); + assert(next != NULL); + r->prev = prev; + r->next = next; + prev->next = r; + next->prev = r; + } + + r->cols = NULL; + r->last_col = NULL; + r->last_used = NULL; + r->row = row; + r->parent = m; + EINA_MAGIC_SET(r, EINA_MAGIC_MATRIXSPARSE_ROW); + m->last_used = r; + return r; +} + +static inline Eina_Matrixsparse_Cell * +_eina_matrixsparse_row_cell_idx_add(Eina_Matrixsparse_Row *r, + unsigned long col, + const void *data) +{ + Eina_Matrixsparse_Cell *c = eina_mempool_malloc + (_eina_matrixsparse_cell_mp, sizeof(Eina_Matrixsparse_Cell)); + if (!c) + return NULL; + + if (!r->cols) + { + c->prev = NULL; + c->next = NULL; + r->cols = c; + r->last_col = c; + } + else if (col < r->cols->col) + { + c->prev = NULL; + c->next = r->cols; + r->cols->prev = c; + r->cols = c; + } + else if (col > r->last_col->col) + { + c->prev = r->last_col; + r->last_col->next = c; + c->next = NULL; + r->last_col = c; + } + else + { + Eina_Matrixsparse_Cell *prev = NULL, *next = NULL; + _eina_matrixsparse_row_cell_idx_siblings_find(r, col, &prev, &next); + assert(prev != NULL); + assert(next != NULL); + c->prev = prev; + c->next = next; + prev->next = c; + next->prev = c; + } + + c->data = (void *)data; + c->col = col; + c->parent = r; + EINA_MAGIC_SET(c, EINA_MAGIC_MATRIXSPARSE_CELL); + r->last_used = c; + return c; +} + +static inline Eina_Bool +_eina_matrixsparse_cell_idx_add(Eina_Matrixsparse *m, + unsigned long row, + unsigned long col, + const void *data) +{ + Eina_Matrixsparse_Row *r = _eina_matrixsparse_row_idx_get(m, row); + if (!r) + r = _eina_matrixsparse_row_idx_add(m, row); + + if (!r) + return 0; + + if (_eina_matrixsparse_row_cell_idx_add(r, col, data)) + return 1; + + if (r->cols) + return 0; + + _eina_matrixsparse_row_unlink(r); + _eina_matrixsparse_row_free(r, m->free.func, m->free.user_data); + return 0; +} + +/*============================================================================* +* Iterators * +*============================================================================*/ +static Eina_Bool +_eina_matrixsparse_iterator_next(Eina_Matrixsparse_Iterator *it, void **data) +{ + EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it, EINA_FALSE); + + /* do not touch it->idx */ + + if (!it->ref.col) + return 0; + + *data = (Eina_Matrixsparse_Cell *)it->ref.col; + + it->ref.col = it->ref.col->next; + if (!it->ref.col) + { + it->ref.row = it->ref.row->next; + if (it->ref.row) + it->ref.col = it->ref.row->cols; + } + + return 1; +} + +static Eina_Matrixsparse * +_eina_matrixsparse_iterator_get_container(Eina_Matrixsparse_Iterator *it) +{ + EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it, NULL); + return (Eina_Matrixsparse *)it->m; +} + +static void +_eina_matrixsparse_iterator_free(Eina_Matrixsparse_Iterator *it) +{ + EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it); + EINA_MAGIC_SET(it, EINA_MAGIC_NONE); + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_NONE); + free(it); +} + +static Eina_Bool +_eina_matrixsparse_iterator_complete_next( + Eina_Matrixsparse_Iterator_Complete *it, + void **data) +{ + EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it, EINA_FALSE); + + if (it->idx.row >= it->m->size.rows) + return 0; + + if (it->dummy.col.data) + ERR("Last iterator call changed dummy cell!"); + + if ((it->ref.col) && + (it->ref.col->col == it->idx.col) && + (it->ref.row->row == it->idx.row)) + { + *data = (Eina_Matrixsparse_Cell *)it->ref.col; + it->ref.col = it->ref.col->next; + if (!it->ref.col) + { + it->ref.row = it->ref.row->next; + if (it->ref.row) + it->ref.col = it->ref.row->cols; + } + } + else + { + it->dummy.col.data = NULL; + it->dummy.col.col = it->idx.col; + it->dummy.row.row = it->idx.row; + *data = &it->dummy.col; + } + + it->idx.col++; + if (it->idx.col == it->m->size.cols) + { + it->idx.col = 0; + it->idx.row++; + } + + return 1; +} + +static Eina_Matrixsparse * +_eina_matrixsparse_iterator_complete_get_container( + Eina_Matrixsparse_Iterator_Complete *it) +{ + EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it, NULL); + return (Eina_Matrixsparse *)it->m; +} + +static void +_eina_matrixsparse_iterator_complete_free( + Eina_Matrixsparse_Iterator_Complete *it) +{ + EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it); + + if (it->dummy.col.data) + ERR("Last iterator call changed dummy cell!"); + + EINA_MAGIC_SET(it, EINA_MAGIC_NONE); + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_NONE); + free(it); +} + + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the matrixsparse module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the matrixsparse module of Eina. It is called by + * eina_init(). + * + * This function creates mempool to speed up matrix rows and cells + * management, using EINA_MEMPOOL environment variable if it is set to + * choose the memory pool type to use. + * + * @see eina_init() + */ +Eina_Bool +eina_matrixsparse_init(void) +{ + const char *choice, *tmp; + + _eina_matrixsparse_log_dom = eina_log_domain_register("eina_matrixsparse", + EINA_LOG_COLOR_DEFAULT); + if (_eina_matrixsparse_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_matrixsparse"); + 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_matrixsparse_cell_mp = eina_mempool_add + (choice, + "matrixsparse_cell", + NULL, + sizeof (Eina_Matrixsparse_Cell), + 120); + if (!_eina_matrixsparse_cell_mp) + { + ERR( + "Mempool for matrixsparse_cell cannot be allocated in matrixsparse init."); + goto on_init_fail; + } + + _eina_matrixsparse_row_mp = eina_mempool_add + (choice, "matrixsparse_row", NULL, sizeof (Eina_Matrixsparse_Row), 120); + if (!_eina_matrixsparse_row_mp) + { + ERR( + "Mempool for matrixsparse_row cannot be allocated in matrixsparse init."); + goto on_init_fail; + } + +#define EMS(n) eina_magic_string_static_set(n, n ## _STR) + EMS(EINA_MAGIC_MATRIXSPARSE); + EMS(EINA_MAGIC_MATRIXSPARSE_ROW); + EMS(EINA_MAGIC_MATRIXSPARSE_CELL); + EMS(EINA_MAGIC_MATRIXSPARSE_ITERATOR); + EMS(EINA_MAGIC_MATRIXSPARSE_ROW_ACCESSOR); + EMS(EINA_MAGIC_MATRIXSPARSE_ROW_ITERATOR); + EMS(EINA_MAGIC_MATRIXSPARSE_CELL_ACCESSOR); + EMS(EINA_MAGIC_MATRIXSPARSE_CELL_ITERATOR); +#undef EMS + + return EINA_TRUE; + +on_init_fail: + eina_log_domain_unregister(_eina_matrixsparse_log_dom); + _eina_matrixsparse_log_dom = -1; + return EINA_FALSE; +} + +/** + * @internal + * @brief Shut down the matrixsparse module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the matrixsparse module set up by + * eina_matrixsparse_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_matrixsparse_shutdown(void) +{ + eina_mempool_del(_eina_matrixsparse_row_mp); + eina_mempool_del(_eina_matrixsparse_cell_mp); + + eina_log_domain_unregister(_eina_matrixsparse_log_dom); + _eina_matrixsparse_log_dom = -1; + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Matrixsparse_Group Sparse Matrix + * + * @brief These functions provide matrix sparse management. + * + * For more information, you can look at the @ref tutorial_matrixsparse_page. + * + * @{ + */ + +/** + * @brief Create a new Sparse Matrix. + * + * @param rows number of rows in matrix. Operations with rows greater than this + * value will fail. + * @param cols number of columns in matrix. Operations with columns greater + * than this value will fail. + * @param free_func used to delete cell data contents, used by + * eina_matrixsparse_free(), eina_matrixsparse_size_set(), + * eina_matrixsparse_row_idx_clear(), + * eina_matrixsparse_column_idx_clear(), + * eina_matrixsparse_cell_idx_clear() and possible others. + * @param user_data given to @a free_func as first parameter. + * + * @return newly allocated matrix or NULL if allocation failed and eina_error + * is set. + */ +EAPI Eina_Matrixsparse * +eina_matrixsparse_new(unsigned long rows, unsigned long cols, void (*free_func)( + void *user_data, + void *cell_data), const void *user_data) +{ + Eina_Matrixsparse *m; + + EINA_SAFETY_ON_FALSE_RETURN_VAL(rows > 0, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(cols > 0, NULL); + + m = malloc(sizeof(Eina_Matrixsparse)); + if (!m) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(m, EINA_MAGIC_MATRIXSPARSE); + + m->rows = NULL; + m->last_row = NULL; + m->last_used = NULL; + + m->size.rows = rows; + m->size.cols = cols; + m->free.func = free_func; + m->free.user_data = (void *)user_data; + + eina_error_set(0); + return m; +} + +/** + * @brief Free resources allocated to Sparse Matrix. + * + * @param m The Sparse Matrix instance to free, must @b not be @c NULL. + */ +EAPI void +eina_matrixsparse_free(Eina_Matrixsparse *m) +{ + void (*free_func)(void *, void *); + void *user_data; + + Eina_Matrixsparse_Row *r; + EINA_MAGIC_CHECK_MATRIXSPARSE(m); + + free_func = m->free.func; + user_data = m->free.user_data; + + r = m->rows; + while (r) + { + Eina_Matrixsparse_Row *r_aux = r; + r = r->next; + _eina_matrixsparse_row_free(r_aux, free_func, user_data); + } + + EINA_MAGIC_SET(m, EINA_MAGIC_NONE); + free(m); +} + +/** + * @brief Get the current size of Sparse Matrix. + * + * The given parameters are guaranteed to be set if they're not NULL, + * even if this function fails (ie: @a m is not a valid matrix instance). + * + * @param m the sparse matrix to operate on. + * @param rows returns the number of rows, may be NULL. If @a m is invalid, + * returned value is zero, otherwise it's a positive integer. + * @param cols returns the number of columns, may be NULL. If @a m is + * invalid, returned value is zero, otherwise it's a positive integer. + */ +EAPI void +eina_matrixsparse_size_get(const Eina_Matrixsparse *m, + unsigned long *rows, + unsigned long *cols) +{ + if (rows) + *rows = 0; + + if (cols) + *cols = 0; + + EINA_MAGIC_CHECK_MATRIXSPARSE(m); + if (rows) + *rows = m->size.rows; + + if (cols) + *cols = m->size.cols; +} + +/** + * @brief Resize the Sparse Matrix. + * + * This will resize the sparse matrix, possibly freeing cells on rows + * and columns that will cease to exist. + * + * @param m the sparse matrix to operate on. + * @param rows the new number of rows, must be greater than zero. + * @param cols the new number of columns, must be greater than zero. + * @return 1 on success, 0 on failure. + * + * @warning cells, rows or columns are not reference counted and thus + * after this call any reference might be invalid if instance were + * freed. + */ +EAPI Eina_Bool +eina_matrixsparse_size_set(Eina_Matrixsparse *m, + unsigned long rows, + unsigned long cols) +{ + Eina_Bool update_last_used_row; + Eina_Matrixsparse_Row *r; + void (*free_func)(void *, void *); + void *user_data; + + EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(rows > 0, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(cols > 0, 0); + + if ((rows == m->size.rows) && (cols == m->size.cols)) + return 1; + + update_last_used_row = ((m->last_used) && (m->last_used->row >= rows)); + free_func = m->free.func; + user_data = m->free.user_data; + + r = m->last_row; + while (r && r->row >= rows) + { + Eina_Matrixsparse_Row *r_aux = r; + r = r->prev; + _eina_matrixsparse_row_free(r_aux, free_func, user_data); + } + if (!r) + { + m->last_row = NULL; + m->rows = NULL; + } + else if (r != m->last_row) + { + r->next = NULL; + m->last_row = r; + } + + if (update_last_used_row) + m->last_used = m->last_row; + + r = m->rows; + while (r) + { + Eina_Matrixsparse_Cell *c = r->last_col; + Eina_Bool update_last_used_col; + update_last_used_col = ((r->last_used) && (r->last_used->col >= cols)); + while (c && c->col >= cols) + { + Eina_Matrixsparse_Cell *c_aux = c; + c = c->prev; + _eina_matrixsparse_cell_free(c_aux, free_func, user_data); + } + if (!c) + { + Eina_Matrixsparse_Row *r_aux = r; + r->cols = NULL; + r->last_col = NULL; + if (r->next) + r->next->prev = r->prev; + else + m->last_row = r->prev; + + if (r->prev) + r->prev->next = r->next; + else + m->rows = r->next; + + r = r->next; + _eina_matrixsparse_row_free(r_aux, free_func, user_data); + } + else + { + if (c != r->last_col) + { + c->next = NULL; + r->last_col = c; + } + + if (update_last_used_col) + r->last_used = r->last_col; + + r = r->next; + } + } + + update_last_used_row = 0; + if (m->last_used) + { + if (m->last_row) + update_last_used_row = m->last_used->row > m->last_row->row; + else + update_last_used_row = 1; + } + + if (update_last_used_row) + m->last_used = m->last_row; + + m->size.rows = rows; + m->size.cols = cols; + return 1; +} + +/** + * Get the cell reference inside Sparse Matrix. + * + * @param m the sparse matrix to operate on. + * @param row the new number of row to clear. + * @param col the new number of column to clear. + * @param cell pointer to return cell reference, if any exists. + * + * @return 1 on success, 0 on failure. It is considered success if did not + * exist but index is inside matrix size, in this case @c *cell == NULL + * + * @see eina_matrixsparse_cell_data_get() + * @see eina_matrixsparse_data_idx_get() + */ +EAPI Eina_Bool +eina_matrixsparse_cell_idx_get(const Eina_Matrixsparse *m, + unsigned long row, + unsigned long col, + Eina_Matrixsparse_Cell **cell) +{ + EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0); + EINA_SAFETY_ON_NULL_RETURN_VAL(cell, 0); + *cell = NULL; + EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0); + *cell = _eina_matrixsparse_cell_idx_get(m, row, col); + return 1; +} + +/** + * Get data associated with given cell reference. + * + * @param cell given cell reference, must @b not be @c NULL. + * + * @return data associated with given cell. + * + * @see eina_matrixsparse_cell_idx_get() + * @see eina_matrixsparse_data_idx_get() + */ +EAPI void * +eina_matrixsparse_cell_data_get(const Eina_Matrixsparse_Cell *cell) +{ + EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, NULL); + return cell->data; +} + +/** + * Get data associated with given cell given its indexes. + * + * @param m the sparse matrix to operate on. + * @param row the new number of row to clear. + * @param col the new number of column to clear. + * + * @return data associated with given cell or NULL if nothing is associated. + * + * @see eina_matrixsparse_cell_idx_get() + * @see eina_matrixsparse_cell_data_get() + */ +EAPI void * +eina_matrixsparse_data_idx_get(const Eina_Matrixsparse *m, + unsigned long row, + unsigned long col) +{ + Eina_Matrixsparse_Cell *c; + EINA_MAGIC_CHECK_MATRIXSPARSE(m, NULL); + c = _eina_matrixsparse_cell_idx_get(m, row, col); + if (c) + return c->data; + else + return NULL; +} + +/** + * Get position (indexes) of the given cell. + * + * @param cell the cell reference, must @b not be @c NULL. + * @param row where to store cell row number, may be @c NULL. + * @param col where to store cell column number, may be @c NULL. + * + * @return 1 on success, 0 otherwise (@c cell is @c NULL). + */ +EAPI Eina_Bool +eina_matrixsparse_cell_position_get(const Eina_Matrixsparse_Cell *cell, + unsigned long *row, + unsigned long *col) +{ + if (row) + *row = 0; + + if (col) + *col = 0; + + EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, 0); + EINA_MAGIC_CHECK_MATRIXSPARSE_ROW(cell->parent, 0); + if (row) + *row = cell->parent->row; + + if (col) + *col = cell->col; + + return 1; +} + +/** + * Change cell reference value without freeing the possibly existing old value. + * + * @param cell the cell reference, must @b not be @c NULL. + * @param data new data to set. + * @param p_old returns the old value intact (not freed). + * + * @return 1 on success, 0 otherwise (@a cell is @c NULL). + * + * @see eina_matrixsparse_cell_data_set() + * @see eina_matrixsparse_data_idx_replace() + */ +EAPI Eina_Bool +eina_matrixsparse_cell_data_replace(Eina_Matrixsparse_Cell *cell, + const void *data, + void **p_old) +{ + if (p_old) + *p_old = NULL; + + EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, 0); + + if (p_old) + *p_old = cell->data; + + cell->data = (void *)data; + return 1; +} + +/** + * Change cell value freeing the possibly existing old value. + * + * In contrast to eina_matrixsparse_cell_data_replace(), this function will + * call @c free_func() on existing value. + * + * @param cell the cell reference, must @b not be @c NULL. + * @param data new data to set. + * + * @return 1 on success, 0 otherwise (@a cell is @c NULL). + * + * @see eina_matrixsparse_cell_data_replace() + * @see eina_matrixsparse_data_idx_set() + */ +EAPI Eina_Bool +eina_matrixsparse_cell_data_set(Eina_Matrixsparse_Cell *cell, const void *data) +{ + Eina_Matrixsparse *m; + + EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, 0); + EINA_MAGIC_CHECK_MATRIXSPARSE_ROW(cell->parent, 0); + EINA_MAGIC_CHECK_MATRIXSPARSE(cell->parent->parent, 0); + + m = cell->parent->parent; + + if (m->free.func) + m->free.func(m->free.user_data, cell->data); + + cell->data = (void *)data; + return 1; +} + +/** + * Change cell value without freeing the possibly existing old value, using + * indexes. + * + * @param m the sparse matrix, must @b not be @c NULL. + * @param row the row number to set the value. + * @param col the column number to set the value. + * @param data new data to set. + * @param p_old returns the old value intact (not freed). + * + * @return 1 on success, 0 otherwise (@a m is @c NULL, indexes are not valid). + * + * @see eina_matrixsparse_cell_data_replace() + * @see eina_matrixsparse_data_idx_set() + */ +EAPI Eina_Bool +eina_matrixsparse_data_idx_replace(Eina_Matrixsparse *m, + unsigned long row, + unsigned long col, + const void *data, + void **p_old) +{ + Eina_Matrixsparse_Cell *cell; + + if (p_old) + *p_old = NULL; + + EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0); + + cell = _eina_matrixsparse_cell_idx_get(m, row, col); + if (cell) + { + if (p_old) + *p_old = cell->data; + + cell->data = (void *)data; + return 1; + } + + return _eina_matrixsparse_cell_idx_add(m, row, col, data); +} + +/** + * Change cell value freeing the possibly existing old value, using + * indexes. + * + * In contrast to eina_matrixsparse_data_idx_replace(), this function will + * call @c free_func() on existing value. + * + * @param m the sparse matrix, must @b not be @c NULL. + * @param row the row number to set the value. + * @param col the column number to set the value. + * @param data new data to set. + * + * @return 1 on success, 0 otherwise (@a m is @c NULL, indexes are not valid). + * + * @see eina_matrixsparse_cell_data_replace() + */ +EAPI Eina_Bool +eina_matrixsparse_data_idx_set(Eina_Matrixsparse *m, + unsigned long row, + unsigned long col, + const void *data) +{ + Eina_Matrixsparse_Cell *cell; + + EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0); + + cell = _eina_matrixsparse_cell_idx_get(m, row, col); + if (cell) + { + if (m->free.func) + m->free.func(m->free.user_data, cell->data); + + cell->data = (void *)data; + return 1; + } + + return _eina_matrixsparse_cell_idx_add(m, row, col, data); +} + +/** + * Clear (erase all cells) of row given its index. + * + * Existing cells will be cleared with @c free_func() given to + * eina_matrixsparse_new(). + * + * @param m the sparse matrix to operate on. + * @param row the new number of row to clear. + * + * @return 1 on success, 0 on failure. It is considered success if row + * had no cells filled. Failure is asking for clear row outside + * matrix size. + * + * @warning cells, rows or columns are not reference counted and thus + * after this call any reference might be invalid if instance were + * freed. + */ +EAPI Eina_Bool +eina_matrixsparse_row_idx_clear(Eina_Matrixsparse *m, unsigned long row) +{ + Eina_Matrixsparse_Row *r; + + EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0); + + r = _eina_matrixsparse_row_idx_get(m, row); + if (!r) + return 1; + + _eina_matrixsparse_row_unlink(r); + _eina_matrixsparse_row_free(r, m->free.func, m->free.user_data); + + return 1; +} + +/** + * Clear (erase all cells) of column given its index. + * + * Existing cells will be cleared with @c free_func() given to + * eina_matrixsparse_new(). + * + * @param m the sparse matrix to operate on. + * @param col the new number of column to clear. + * + * @return 1 on success, 0 on failure. It is considered success if column + * had no cells filled. Failure is asking for clear column outside + * matrix size. + * + * @warning cells, rows or columns are not reference counted and thus + * after this call any reference might be invalid if instance were + * freed. + */ +EAPI Eina_Bool +eina_matrixsparse_column_idx_clear(Eina_Matrixsparse *m, unsigned long col) +{ + Eina_Matrixsparse_Row *r; + void (*free_func)(void *, void *); + void *user_data; + + EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0); + + free_func = m->free.func; + user_data = m->free.user_data; + + for (r = m->rows; r; ) + { + Eina_Matrixsparse_Row *r_aux = r; + Eina_Matrixsparse_Cell *c; + + c = _eina_matrixsparse_row_cell_idx_get(r, col); + r = r->next; + + if (!c) + continue; + + if ((r_aux->cols != c) || (r_aux->last_col != c)) + { + _eina_matrixsparse_cell_unlink(c); + _eina_matrixsparse_cell_free(c, free_func, user_data); + } + else + { + _eina_matrixsparse_row_unlink(r_aux); + _eina_matrixsparse_row_free(r_aux, free_func, user_data); + } + } + + return 1; +} + +/** + * Clear (erase) cell given its indexes. + * + * Existing cell will be cleared with @c free_func() given to + * eina_matrixsparse_new(). + * + * @param m the sparse matrix to operate on. + * @param row the new number of row to clear. + * @param col the new number of column to clear. + * + * @return 1 on success, 0 on failure. It is considered success if did not + * exist but index is inside matrix size. + * + * @warning cells, rows or columns are not reference counted and thus + * after this call any reference might be invalid if instance were + * freed. Note that this call might delete container column and + * row if this cell was the last remainder. + */ +EAPI Eina_Bool +eina_matrixsparse_cell_idx_clear(Eina_Matrixsparse *m, + unsigned long row, + unsigned long col) +{ + Eina_Matrixsparse_Cell *c; + + EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0); + EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0); + + c = _eina_matrixsparse_cell_idx_get(m, row, col); + if (!c) + return 1; + + _eina_matrixsparse_cell_unlink(c); + _eina_matrixsparse_cell_free(c, m->free.func, m->free.user_data); + + return 1; +} + +/** + * Clear (erase) cell given its reference. + * + * @param cell the cell reference, must @b not be @c NULL. + * + * @return 1 on success, 0 on failure. + * + * @warning cells, rows or columns are not reference counted and thus + * after this call any reference might be invalid if instance were + * freed. Note that this call might delete container column and + * row if this cell was the last remainder. + */ +EAPI Eina_Bool +eina_matrixsparse_cell_clear(Eina_Matrixsparse_Cell *cell) +{ + Eina_Matrixsparse *m; + + EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, 0); + EINA_MAGIC_CHECK_MATRIXSPARSE_ROW(cell->parent, 0); + EINA_MAGIC_CHECK_MATRIXSPARSE(cell->parent->parent, 0); + + m = cell->parent->parent; + + _eina_matrixsparse_cell_unlink(cell); + _eina_matrixsparse_cell_free(cell, m->free.func, m->free.user_data); + return 1; +} + +/** + * Creates a new iterator over existing matrix cells. + * + * This is a cheap walk, it will just report existing cells and holes + * in the sparse matrix will be ignored. That means the reported + * indexes will not be sequential. + * + * The iterator data will be the cell reference, one may query current + * position with eina_matrixsparse_cell_position_get() and cell value + * with eina_matrixsparse_cell_data_get(). + * + * @param m The Sparse Matrix reference, must @b not be @c NULL. + * @return A new iterator. + * + * @warning if the matrix structure changes then the iterator becomes + * invalid! That is, if you add or remove cells this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_matrixsparse_iterator_new(const Eina_Matrixsparse *m) +{ + Eina_Matrixsparse_Iterator *it; + + it = calloc(1, sizeof(*it)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(it, EINA_MAGIC_MATRIXSPARSE_ITERATOR); + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->m = m; + it->ref.row = m->rows; + it->ref.col = m->rows ? m->rows->cols : NULL; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_matrixsparse_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _eina_matrixsparse_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_matrixsparse_iterator_free); + return &it->iterator; +} + +/** + * Creates a new iterator over all matrix cells. + * + * Unlike eina_matrixsparse_iterator_new() this one will report all + * matrix cells, even those that are still empty (holes). These will + * be reported as dummy cells that contains no data. + * + * Be aware that iterating a big matrix (1000x1000) will call your + * function that number of times (1000000 times in that case) even if + * your matrix have no elements at all! + * + * The iterator data will be the cell reference, one may query current + * position with eina_matrixsparse_cell_position_get() and cell value + * with eina_matrixsparse_cell_data_get(). If cell is empty then the + * reference will be a dummy/placeholder, thus setting value with + * eina_matrixsparse_cell_data_set() will leave pointer unreferenced. + * + * @param m The Sparse Matrix reference, must @b not be @c NULL. + * @return A new iterator. + * + * @warning if the matrix structure changes then the iterator becomes + * invalid! That is, if you add or remove cells this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_matrixsparse_iterator_complete_new(const Eina_Matrixsparse *m) +{ + Eina_Matrixsparse_Iterator_Complete *it; + + it = calloc(1, sizeof(*it)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(it, EINA_MAGIC_MATRIXSPARSE_ITERATOR); + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->m = m; + it->idx.row = 0; + it->idx.col = 0; + it->ref.row = m->rows; + it->ref.col = m->rows ? m->rows->cols : NULL; + + it->dummy.row.next = it->dummy.row.prev = NULL; + it->dummy.row.cols = it->dummy.row.last_col = it->dummy.row.last_used = NULL; + it->dummy.row.parent = (Eina_Matrixsparse *)m; + EINA_MAGIC_SET(&it->dummy.row, EINA_MAGIC_MATRIXSPARSE_ROW); + + it->dummy.col.next = it->dummy.col.prev = NULL; + it->dummy.col.data = NULL; + it->dummy.col.parent = &it->dummy.row; + EINA_MAGIC_SET(&it->dummy.col, EINA_MAGIC_MATRIXSPARSE_CELL); + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT( + _eina_matrixsparse_iterator_complete_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _eina_matrixsparse_iterator_complete_get_container); + it->iterator.free = FUNC_ITERATOR_FREE( + _eina_matrixsparse_iterator_complete_free); + return &it->iterator; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_mempool.c b/tests/suite/ecore/src/lib/eina_mempool.c new file mode 100644 index 0000000000..b9062bef9a --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_mempool.c @@ -0,0 +1,399 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_hash.h" +#include "eina_module.h" +#include "eina_log.h" +#include "eina_main.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_mempool.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static Eina_Hash *_backends; +static Eina_Array *_modules; +static int _eina_mempool_log_dom = -1; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_mempool_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_mempool_log_dom, __VA_ARGS__) + + +static Eina_Mempool * +_new_va(const char *name, + const char *context, + const char *options, + va_list args) +{ + Eina_Mempool_Backend *be; + Eina_Mempool *mp; + + Eina_Error err = EINA_ERROR_NOT_MEMPOOL_MODULE; + + eina_error_set(0); + be = eina_hash_find(_backends, name); + if (!be) + goto on_error; + + err = EINA_ERROR_OUT_OF_MEMORY; + mp = calloc(1, sizeof(Eina_Mempool)); + if (!mp) + goto on_error; + + /* FIXME why backend is not a pointer? */ + mp->backend = *be; + mp->backend_data = mp->backend.init(context, options, args); + + return mp; + +on_error: + eina_error_set(err); + return NULL; +} + +/* Built-in backend's prototypes */ +#ifdef EINA_STATIC_BUILD_CHAINED_POOL +Eina_Bool chained_init(void); +void chained_shutdown(void); +#endif + +#ifdef EINA_STATIC_BUILD_PASS_THROUGH +Eina_Bool pass_through_init(void); +void pass_through_shutdown(void); +#endif + +#ifdef EINA_STATIC_BUILD_EMEMOA_UNKNOWN +Eina_Bool ememoa_unknown_init(void); +void ememoa_unknown_shutdown(void); +#endif + +#ifdef EINA_STATIC_BUILD_EMEMOA_FIXED +Eina_Bool ememoa_fixed_init(void); +void ememoa_fixed_shutdown(void); +#endif + +#ifdef EINA_STATIC_BUILD_FIXED_BITMAP +Eina_Bool fixed_bitmap_init(void); +void fixed_bitmap_shutdown(void); +#endif + +#ifdef EINA_STATIC_BUILD_BUDDY +Eina_Bool buddy_init(void); +void buddy_shutdown(void); +#endif + +#ifdef EINA_STATIC_BUILD_ONE_BIG +Eina_Bool one_big_init(void); +void one_big_shutdown(void); +#endif + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +EAPI Eina_Error EINA_ERROR_NOT_MEMPOOL_MODULE = 0; + +static const char EINA_ERROR_NOT_MEMPOOL_MODULE_STR[] = + "Not a memory pool module."; + +/** + * @endcond + */ + +EAPI Eina_Bool +eina_mempool_register(Eina_Mempool_Backend *be) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(be, 0); + DBG("be=%p, name=%p", be, be->name); + return eina_hash_add(_backends, be->name, be); +} + +EAPI void +eina_mempool_unregister(Eina_Mempool_Backend *be) +{ + EINA_SAFETY_ON_NULL_RETURN(be); + DBG("be=%p, name=%p", be, be->name); + eina_hash_del(_backends, be->name, be); +} + +Eina_Bool +eina_mempool_init(void) +{ + char *path; + + _eina_mempool_log_dom = eina_log_domain_register("eina_mempool", + EINA_LOG_COLOR_DEFAULT); + if (_eina_mempool_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_mempool"); + return 0; + } + + EINA_ERROR_NOT_MEMPOOL_MODULE = eina_error_msg_static_register( + EINA_ERROR_NOT_MEMPOOL_MODULE_STR); + _backends = eina_hash_string_superfast_new(NULL); + + /* dynamic backends */ + _modules = eina_module_arch_list_get(NULL, + PACKAGE_LIB_DIR "/eina/modules/mp", + MODULE_ARCH); + + path = eina_module_environment_path_get("HOME", "/.eina/mp/modules/mp"); + _modules = eina_module_arch_list_get(_modules, path, MODULE_ARCH); + if (path) + free(path); + + path = eina_module_environment_path_get("EINA_MODULES_MEMPOOL_DIR", + "/eina/modules/mp"); + _modules = eina_module_arch_list_get(_modules, path, MODULE_ARCH); + if (path) + free(path); + + path = eina_module_symbol_path_get((const void *)eina_init, + "/eina/modules/mp"); + _modules = eina_module_arch_list_get(_modules, path, MODULE_ARCH); + if (path) + free(path); + + if (!_modules) + { + ERR("no mempool modules able to be loaded."); + eina_hash_free(_backends); + goto mempool_init_error; + } + + eina_module_list_load(_modules); + + /* builtin backends */ +#ifdef EINA_STATIC_BUILD_CHAINED_POOL + chained_init(); +#endif +#ifdef EINA_STATIC_BUILD_PASS_THROUGH + pass_through_init(); +#endif +#ifdef EINA_STATIC_BUILD_EMEMOA_UNKNOWN + ememoa_unknown_init(); +#endif +#ifdef EINA_STATIC_BUILD_EMEMOA_FIXED + ememoa_fixed_init(); +#endif +#ifdef EINA_STATIC_BUILD_FIXED_BITMAP + fixed_bitmap_init(); +#endif +#ifdef EINA_STATIC_BUILD_BUDDY + buddy_init(); +#endif +#ifdef EINA_STATIC_BUILD_ONE_BIG + one_big_init(); +#endif + + return EINA_TRUE; + +mempool_init_error: + eina_log_domain_unregister(_eina_mempool_log_dom); + _eina_mempool_log_dom = -1; + + return EINA_FALSE; +} + +Eina_Bool +eina_mempool_shutdown(void) +{ + /* builtin backends */ +#ifdef EINA_STATIC_BUILD_CHAINED_POOL + chained_shutdown(); +#endif +#ifdef EINA_STATIC_BUILD_PASS_THROUGH + pass_through_shutdown(); +#endif +#ifdef EINA_STATIC_BUILD_EMEMOA_UNKNOWN + ememoa_unknown_shutdown(); +#endif +#ifdef EINA_STATIC_BUILD_EMEMOA_FIXED + ememoa_fixed_shutdown(); +#endif +#ifdef EINA_STATIC_BUILD_FIXED_BITMAP + fixed_bitmap_shutdown(); +#endif +#ifdef EINA_STATIC_BUILD_BUDDY + buddy_shutdown(); +#endif +#ifdef EINA_STATIC_BUILD_ONE_BIG + one_big_shutdown(); +#endif + /* dynamic backends */ + eina_module_list_free(_modules); + if (_modules) + eina_array_free(_modules); + + if (_backends) + eina_hash_free(_backends); + + eina_log_domain_unregister(_eina_mempool_log_dom); + _eina_mempool_log_dom = -1; + + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Memory_Pool_Group Memory Pool + * + * @brief These functions provide memory pool management. + * + * Several mempool are available: + * + * @li @c buddy: It uses the + * <a href="http://en.wikipedia.org/wiki/Buddy_memory_allocation">"buddy + * allocator" algorithm</a> but the Eina implementation differs in the + * sense that the chunk information is not stored on the chunk itself, + * but on another memory area. This is useful for cases where the + * memory to manage might be slower to access, or limited (like video + * memory). + * @li @c chained_pool: It is the default one. It allocates a big + * chunk of memory with malloc() and split the result in chunks of the + * requested size that are pushed inside a stack. When requested, it + * takes this pointer from the stack to give them to whoever wants + * them. + * @li @c ememoa_fixed and @c ememoa_unknown: experimental allocators + * which could be useful when a fixed amount of memory is needed. + * @li @c fixed_bitmap: It allocates with malloc) 32* the requested + * size and push the pool pointer in an rbtree. To find empty space in + * a pool, it will just search for the first bit set in an int (32 + * bits). Then, when a pointer is freed, it will do a search inside + * the rbtree. + * @li @c pass_through: it just call malloc() and free(). It may be + * faster on some computers than using our own allocators (like having + * a huge L2 cache, over 4MB). + * @li @c one_big: It call just one time malloc for the requested number + * of items. Usefull when you know in advance how many object of some + * type will live during the life of the mempool. + * + * @{ + */ + +EAPI Eina_Mempool * +eina_mempool_add(const char *name, + const char *context, + const char *options, + ...) +{ + Eina_Mempool *mp; + va_list args; + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL); + DBG("name=%s, context=%s, options=%s", + name, context ? context : "", options ? options : ""); + + va_start(args, options); + mp = _new_va(name, context, options, args); + va_end(args); + + DBG("name=%s, context=%s, options=%s, mp=%p", + name, context ? context : "", options ? options : "", mp); + + return mp; +} + +EAPI void eina_mempool_del(Eina_Mempool *mp) +{ + EINA_SAFETY_ON_NULL_RETURN(mp); + EINA_SAFETY_ON_NULL_RETURN(mp->backend.shutdown); + DBG("mp=%p", mp); + mp->backend.shutdown(mp->backend_data); + free(mp); +} + +EAPI void eina_mempool_gc(Eina_Mempool *mp) +{ + EINA_SAFETY_ON_NULL_RETURN(mp); + EINA_SAFETY_ON_NULL_RETURN(mp->backend.garbage_collect); + DBG("mp=%p", mp); + mp->backend.garbage_collect(mp->backend_data); +} + +EAPI void eina_mempool_statistics(Eina_Mempool *mp) +{ + EINA_SAFETY_ON_NULL_RETURN(mp); + EINA_SAFETY_ON_NULL_RETURN(mp->backend.statistics); + DBG("mp=%p", mp); + mp->backend.statistics(mp->backend_data); +} + +EAPI unsigned int +eina_mempool_alignof(unsigned int size) +{ + int align; + + if (size <= 2) + align = 2; + else if (size < 8) + align = 4; + else +#if __WORDSIZE == 32 + align = 8; + +#else + if (size < 16) + align = 8; + else + align = 16; +#endif + + return ((size / align) + 1) * align; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_module.c b/tests/suite/ecore/src/lib/eina_module.c new file mode 100644 index 0000000000..7ae4bdbd6b --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_module.c @@ -0,0 +1,762 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric BAIL + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_DLADDR +# define _GNU_SOURCE +#endif + +#ifdef HAVE_ALLOCA_H +# include <alloca.h> +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include <malloc.h> +# define alloca _alloca +#else +# include <stddef.h> +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <string.h> + +#ifndef _MSC_VER +# include <libgen.h> +#else +# include <Evil.h> +#endif + +#include <dlfcn.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_error.h" +#include "eina_file.h" +#include "eina_log.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_module.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static int EINA_MODULE_LOG_DOM = -1; +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(EINA_MODULE_LOG_DOM, __VA_ARGS__) + +#ifdef WRN +#undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(EINA_MODULE_LOG_DOM, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(EINA_MODULE_LOG_DOM, __VA_ARGS__) + +#define EINA_MODULE_SYMBOL_INIT "__eina_module_init" +#define EINA_MODULE_SYMBOL_SHUTDOWN "__eina_module_shutdown" + +struct _Eina_Module +{ + void *handle; + int ref; + const char file[]; +}; + +typedef struct _Dir_List_Get_Cb_Data +{ + Eina_Module_Cb cb; + void *data; + Eina_Array *array; +} Dir_List_Get_Cb_Data; + +typedef struct _Dir_List_Cb_Data +{ + Eina_Module_Cb cb; + void *data; +} Dir_List_Cb_Data; + +static Eina_Bool _dir_list_get_cb(Eina_Module *m, void *data) +{ + Dir_List_Get_Cb_Data *cb_data = data; + Eina_Bool ret = EINA_TRUE; + + if (cb_data->cb) + ret = cb_data->cb(m, cb_data->data); + + if (ret) + eina_array_push(cb_data->array, m); + + return ret; +} + +static void _dir_list_cb(const char *name, const char *path, void *data) +{ + Dir_List_Cb_Data *cb_data = data; + size_t length; + + length = strlen(name); + if (length < sizeof(SHARED_LIB_SUFFIX)) /* x.so */ + return; + + if (!strcmp(name + length - sizeof(SHARED_LIB_SUFFIX) + 1, + SHARED_LIB_SUFFIX)) + { + char *file; + Eina_Module *m; + + length = strlen(path) + strlen(name) + 2; + + file = alloca(sizeof (char) * length); + if (!file) + return; + + snprintf(file, length, "%s/%s", path, name); + m = eina_module_new(file); + if (!m) + { + return; /* call the user provided cb on this module */ + + } + + if (!cb_data->cb(m, cb_data->data)) + eina_module_free(m); + } +} + +static void _dir_arch_list_cb(const char *name, const char *path, void *data) +{ + Dir_List_Get_Cb_Data *cb_data = data; + Eina_Module *m; + char *file = NULL; + size_t length; + + length = strlen(path) + 1 + strlen(name) + 1 + + strlen((char *)(cb_data->data)) + 1 + sizeof("module") + + sizeof(SHARED_LIB_SUFFIX) + 1; + + file = alloca(length); + snprintf(file, length, "%s/%s/%s/module" SHARED_LIB_SUFFIX, + path, name, (char *)(cb_data->data)); + m = eina_module_new(file); + if (!m) + return; + + eina_array_push(cb_data->array, m); +} + +/** + * @endcond + */ + + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +static const char EINA_ERROR_WRONG_MODULE_STR[] = + "Wrong file format or no file module found"; +static const char EINA_ERROR_MODULE_INIT_FAILED_STR[] = + "Module initialisation function failed"; + +EAPI Eina_Error EINA_ERROR_WRONG_MODULE = 0; +EAPI Eina_Error EINA_ERROR_MODULE_INIT_FAILED = 0; + +/** + * @endcond + */ + +/** + * @internal + * @brief Initialize the module loader module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the module loader module of Eina. It is + * called by eina_init(). + * + * This function sets up the module module of Eina. It also registers + * the errors #EINA_ERROR_WRONG_MODULE and + * #EINA_ERROR_MODULE_INIT_FAILED. + * + * @see eina_init() + */ +Eina_Bool +eina_module_init(void) +{ + EINA_MODULE_LOG_DOM = eina_log_domain_register + ("eina_module", EINA_LOG_COLOR_DEFAULT); + if (EINA_MODULE_LOG_DOM < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_module"); + return EINA_FALSE; + } + +#define EEMR(n) n = eina_error_msg_static_register(n ## _STR) + EEMR(EINA_ERROR_WRONG_MODULE); + EEMR(EINA_ERROR_MODULE_INIT_FAILED); +#undef EEMR + + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the module loader module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the module loader module set up by + * eina_module_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_module_shutdown(void) +{ + /* TODO should we store every module when "new" is called and + * delete the list of modules here + */ + + eina_log_domain_unregister(EINA_MODULE_LOG_DOM); + EINA_MODULE_LOG_DOM = -1; + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Module_Group Module + * + * @brief These functions provide module management. + * + * @{ + */ + +/** + * @brief Return a new module. + * + * @param file The name of the file module to load. + * + * This function returns a new module. If @p file is @c NULL, the + * function returns @c NULL, otherwise, it allocates an Eina_Module, + * stores a duplicate string of @p file, sets its reference to @c 0 + * and its handle to @c NULL. + * + * When the new module is not needed anymore, use eina_module_free() + * to free the allocated memory. + * + * @see eina_module_load + */ +EAPI Eina_Module *eina_module_new(const char *file) +{ + Eina_Module *m; + size_t len; + + EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL); + /* TODO check that the file exists. Update doc too */ + + len = strlen(file); + EINA_SAFETY_ON_FALSE_RETURN_VAL(len > 0, NULL); + + m = malloc(sizeof(Eina_Module) + len + 1); + if (!m) + { + ERR("could not malloc(%lu)", + (unsigned long)(sizeof(Eina_Module) + len + 1)); + return NULL; + } + + memcpy((char *)m->file, file, len + 1); + m->ref = 0; + m->handle = NULL; + DBG("m=%p, file=%s", m, file); + + return m; +} + +/** + * @brief Delete a module. + * + * @param m The module to delete. + * @return EINA_TRUE on success, EINA_FALSE otherwise. + * + * This function calls eina_module_unload() if @p m has been previously + * loaded and frees the allocated memory. On success this function + * returns EINA_TRUE and EINA_FALSE otherwise. If @p m is @c NULL, the + * function returns immediately. + */ +EAPI Eina_Bool eina_module_free(Eina_Module *m) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE); + + DBG("m=%p, handle=%p, file=%s, refs=%d", m, m->handle, m->file, m->ref); + + if (m->handle) + if (eina_module_unload(m) == EINA_FALSE) + return EINA_FALSE; + + free(m); + return EINA_TRUE; +} + +/** + * @brief Load a module. + * + * @param m The module to load. + * @return EINA_TRUE on success, EINA_FALSE otherwise. + * + * This function load the shared file object passed in + * eina_module_new(). If it is a internal Eina module (like the + * mempools), it also initialize it. It the shared file object can not + * be loaded, the error #EINA_ERROR_WRONG_MODULE is set and + * #EINA_FALSE is returned. If it is a internal Eina module and the + * module can not be initialized, the error + * #EINA_ERROR_MODULE_INIT_FAILED is set and #EINA_FALSE is + * returned. If the module has already been loaded, it's refeence + * counter is increased by one and #EINA_TRUE is returned. If @p m is + * @c NULL, the function returns immediately #EINA_FALSE. + * + * When the symbols of the shared file objetcts are not needed + * anymore, call eina_module_unload() to unload the module. + */ +EAPI Eina_Bool eina_module_load(Eina_Module *m) +{ + void *dl_handle; + Eina_Module_Init *initcall; + + EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE); + + DBG("m=%p, handle=%p, file=%s, refs=%d", m, m->handle, m->file, m->ref); + + if (m->handle) + goto loaded; + + dl_handle = dlopen(m->file, RTLD_NOW); + if (!dl_handle) + { + WRN("could not dlopen(\"%s\", RTLD_NOW): %s", m->file, dlerror()); + eina_error_set(EINA_ERROR_WRONG_MODULE); + return EINA_FALSE; + } + + initcall = dlsym(dl_handle, EINA_MODULE_SYMBOL_INIT); + if ((!initcall) || (!(*initcall))) + goto ok; + + if ((*initcall)() == EINA_TRUE) + goto ok; + + WRN("could not find eina's entry symbol %s inside module %s", + EINA_MODULE_SYMBOL_INIT, m->file); + eina_error_set(EINA_ERROR_MODULE_INIT_FAILED); + dlclose(dl_handle); + return EINA_FALSE; +ok: + DBG("successfully loaded %s", m->file); + m->handle = dl_handle; +loaded: + m->ref++; + DBG("ref %d", m->ref); + + eina_error_set(0); + return EINA_TRUE; +} + +/** + * @brief Unload a module. + * + * @param m The module to load. + * @return EINA_TRUE on success, EINA_FALSE otherwise. + * + * This function unload the module @p m that has been previously + * loaded by eina_module_load(). If the reference counter of @p m is + * strictly greater than @c 1, #EINA_FALSE is returned. Otherwise, the + * shared object file is closed and if it is a internal Eina module, it + * is shutted down just before. In that case, #EINA_TRUE is + * returned. In all case, the reference counter is decreased. If @p m + * is @c NULL, the function returns immediately #EINA_FALSE. + */ +EAPI Eina_Bool eina_module_unload(Eina_Module *m) +{ + Eina_Module_Shutdown *shut; + EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE); + + DBG("m=%p, handle=%p, file=%s, refs=%d", m, m->handle, m->file, m->ref); + + m->ref--; + if (!m->ref) + { + shut = dlsym(m->handle, EINA_MODULE_SYMBOL_SHUTDOWN); + if ((shut) && (*shut)) + (*shut)(); + + dlclose(m->handle); + m->handle = NULL; + DBG("unloaded module %s", m->file); + return EINA_TRUE; + } + + return EINA_FALSE; +} + +/** + * @brief Retrive the data associated to a symbol. + * + * @param m The module. + * @param symbol The symbol. + * @return The data associated to the symbol, or @c NULL on failure. + * + * This function returns the data associated to @p symbol of @p m. @p + * m must have been loaded before with eina_module_load(). If @p m + * is @c NULL, or if it has not been correctly loaded before, the + * function returns immediately @c NULL. + */ +EAPI void *eina_module_symbol_get(const Eina_Module *m, const char *symbol) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(m, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(m->handle, NULL); + return dlsym(m->handle, symbol); +} + +/** + * @brief Return the file name associated to the module. + * + * @param m The module. + * @return The file name. + * + * This function returns the file name passed in eina_module_new(). If + * @p m is @c NULL, the function returns immediately @c NULL. The + * returned value must no be freed. + */ +EAPI const char *eina_module_file_get(const Eina_Module *m) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(m, NULL); + return m->file; +} + +/** + * @brief Return the path built from the location of a library and a + * given sub directory. + * + * @param symbol The symbol to search for. + * @param sub_dir The subdirectory to append. + * @return The built path on success, @c NULL otherwise. + * + * This function returns the path built by concatenating the path of + * the library containing the symbol @p symbol and @p sub_dir. @p sub_dir + * can be @c NULL. The returned path must be freed when not used + * anymore. If the symbol is not found, or dl_addr() is not supported, + * or allocation fails, this function returns @c NULL. + */ +EAPI char *eina_module_symbol_path_get(const void *symbol, const char *sub_dir) +{ +#ifdef HAVE_DLADDR + Dl_info eina_dl; + + EINA_SAFETY_ON_NULL_RETURN_VAL(symbol, NULL); + + if (dladdr(symbol, &eina_dl)) + { + char *pos = strrchr(eina_dl.dli_fname, '/'); + if (pos) + { + char *path; + int l0; + int l1; + int l2 = 0; + + l0 = strlen(eina_dl.dli_fname); + l1 = strlen(pos); + if (sub_dir && (*sub_dir != '\0')) + l2 = strlen(sub_dir); + + path = malloc(l0 - l1 + l2 + 1); + if (path) + { + memcpy(path, eina_dl.dli_fname, l0 - l1); + if (sub_dir && (*sub_dir != '\0')) + memcpy(path + l0 - l1, sub_dir, l2); + + path[l0 - l1 + l2] = '\0'; + return path; + } + } + } + +#endif /* ! HAVE_DLADDR */ + + return NULL; +} + +/** + * @brief Return the path built from the value of an environment varialbe and a + * given sub directory. + * + * @param env The environment variable to expand. + * @param sub_dir The subdirectory to append. + * @return The built path on success, @c NULL otherwise. + * + * This function returns the path built by concatenating the value of + * the environment variable named @p env and @p sub_dir. @p sub_dir + * can be @c NULL. The returned path must be freed when not used + * anymore. If the symbol is not found, or @p env does not exist, or + * allocation fails, this function returns @c NULL. + */ +EAPI char *eina_module_environment_path_get(const char *env, + const char *sub_dir) +{ + const char *env_dir; + + EINA_SAFETY_ON_NULL_RETURN_VAL(env, NULL); + + env_dir = getenv(env); + if (env_dir) + { + char *path; + size_t l1; + size_t l2 = 0; + + l1 = strlen(env_dir); + if (sub_dir && (*sub_dir != '\0')) + l2 = strlen(sub_dir); + + path = (char *)malloc(l1 + l2 + 1); + if (path) + { + memcpy(path, env_dir, l1); + if (sub_dir && (*sub_dir != '\0')) + memcpy(path + l1, sub_dir, l2); + + path[l1 + l2] = '\0'; + + return path; + } + } + + return NULL; +} + +/** + * @brief Get an array of modules found on the directory path matching an arch type. + * + * @param array The array that stores the list of the modules. + * @param path The directory's path to search for modules. + * @param arch The architecture string. + * + * This function adds to @p array the module names found in @p path + * which match the cpu architecture @p arch. If @p path or @p arch is + * @c NULL, the function returns immediately @p array. @p array can be + * @c NULL. In that case, it is created with 4 elements. + */ +EAPI Eina_Array *eina_module_arch_list_get(Eina_Array *array, + const char *path, + const char *arch) +{ + Dir_List_Get_Cb_Data list_get_cb_data; + + if ((!path) || (!arch)) + return array; + + list_get_cb_data.array = array ? array : eina_array_new(4); + list_get_cb_data.cb = NULL; + list_get_cb_data.data = (void *)arch; + + eina_file_dir_list(path, 0, &_dir_arch_list_cb, &list_get_cb_data); + + return list_get_cb_data.array; +} + +/** + * @brief Get a list of modules found on the directory path. + * + * @param array The array that stores the list of the modules. + * @param path The directory's path to search for modules. + * @param recursive Iterate recursively on the path. + * @param cb Callback function to call on each module. + * @param data Data passed to the callback function. + * + * This function adds to @p array the list of modules found in + * @p path. If @p recursive is #EINA_TRUE, then recursive search is + * done. The callback @p cb is called on each module found, and @p data + * is passed to @p cb. If @p path is @c NULL, the function returns + * immediately @p array. If the returned value of @p cb is 0, the + * module will not be added to the list, otherwise it will be added. + * @p array can be @c NULL. In that case, it is created with 4 + * elements. @p cb can be @c NULL. + */ +EAPI Eina_Array *eina_module_list_get(Eina_Array *array, + const char *path, + Eina_Bool recursive, + Eina_Module_Cb cb, + void *data) +{ + Dir_List_Get_Cb_Data list_get_cb_data; + Dir_List_Cb_Data list_cb_data; + + if (!path) + return array; + + list_get_cb_data.array = array ? array : eina_array_new(4); + list_get_cb_data.cb = cb; + list_get_cb_data.data = data; + + list_cb_data.cb = &_dir_list_get_cb; + list_cb_data.data = &list_get_cb_data; + + eina_file_dir_list(path, recursive, &_dir_list_cb, &list_cb_data); + + return list_get_cb_data.array; +} + +/** + * @brief Find an module in array. + * + * @param array The array to find the module. + * @param module The name of module to be searched. + * + * This function finds an @p module in @p array. + * If the element is found the function returns the module, else + * @c NULL is returned. + */ +EAPI Eina_Module * +eina_module_find(const Eina_Array *array, const char *module) +{ + unsigned int i; + Eina_Array_Iterator iterator; + Eina_Module *m; + + EINA_ARRAY_ITER_NEXT(array, i, m, iterator) + { + char *file_m; + char *tmp; + ssize_t len; + + /* basename() can modify its argument, so we first get a copie */ + /* do not use strdupa, as opensolaris does not have it */ + len = strlen(eina_module_file_get(m)); + tmp = alloca(len + 1); + memcpy(tmp, eina_module_file_get(m), len + 1); + file_m = basename(tmp); + len = strlen(file_m); + len -= sizeof(SHARED_LIB_SUFFIX) - 1; + if (len <= 0) + continue; + + if (!strncmp(module, file_m, len)) + return m;; + } + + return NULL; +} + +/** + * @brief Load every module on the list of modules. + * + * @param array The array of modules to load. + * + * This function calls eina_module_load() on each element found in + * @p array. If @p array is @c NULL, this function does nothing. + */ +EAPI void eina_module_list_load(Eina_Array *array) +{ + Eina_Array_Iterator iterator; + Eina_Module *m; + unsigned int i; + + EINA_SAFETY_ON_NULL_RETURN(array); + DBG("array %p, count %u", array, array->count); + EINA_ARRAY_ITER_NEXT(array, i, m, iterator) + eina_module_load(m); +} + +/** + * @brief Unload every module on the list of modules. + * + * @param array The array of modules to unload. + * + * This function calls eina_module_unload() on each element found in + * @p array. If @p array is @c NULL, this function does nothing. + */ +EAPI void eina_module_list_unload(Eina_Array *array) +{ + Eina_Array_Iterator iterator; + Eina_Module *m; + unsigned int i; + + EINA_SAFETY_ON_NULL_RETURN(array); + DBG("array %p, count %u", array, array->count); + EINA_ARRAY_ITER_NEXT(array, i, m, iterator) + eina_module_unload(m); +} + +/** + * @p Free every module on the list of modules. + * + * @param array The array of modules to free. + * + * This function calls eina_module_free() on each element found in + * @p array. If @p array is @c NULL, this function does nothing. + */ +EAPI void eina_module_list_free(Eina_Array *array) +{ + Eina_Array_Iterator iterator; + Eina_Module *m; + unsigned int i; + + EINA_SAFETY_ON_NULL_RETURN(array); + DBG("array %p, count %u", array, array->count); + EINA_ARRAY_ITER_NEXT(array, i, m, iterator) + eina_module_free(m); + + eina_array_flush(array); +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_private.h b/tests/suite/ecore/src/lib/eina_private.h new file mode 100644 index 0000000000..2f8ff8117d --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_private.h @@ -0,0 +1,135 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef EINA_PRIVATE_H_ +#define EINA_PRIVATE_H_ + +#include <stdarg.h> + +#if HAVE___ATTRIBUTE__ +# define __UNUSED__ __attribute__((unused)) +#else +# define __UNUSED__ +#endif + +#include "eina_magic.h" +#include "eina_iterator.h" +#include "eina_accessor.h" + +#ifndef MIN +# define MIN(x, y) (((x) > (y)) ? (y) : (x)) +#endif + +#ifndef MAX +# define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef ABS +# define ABS(x) ((x) < 0 ? -(x) : (x)) +#endif + +#ifndef CLAMP +# define CLAMP(x, min, \ + max) (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x))) +#endif + +#define READBUFSIZ 65536 + +#define EINA_LOG_COLOR_DEFAULT "\033[36m" + +/* eina magic types */ +#define EINA_MAGIC_SHARE 0x98761234 +#define EINA_MAGIC_SHARE_HEAD 0x98761235 +#define EINA_MAGIC_STRINGSHARE_NODE 0x98761254 +#define EINA_MAGIC_USTRINGSHARE_NODE 0x98761255 +#define EINA_MAGIC_BINSHARE_NODE 0x98761256 + +#define EINA_MAGIC_LIST 0x98761237 +#define EINA_MAGIC_LIST_ITERATOR 0x98761238 +#define EINA_MAGIC_LIST_ACCESSOR 0x98761239 +#define EINA_MAGIC_LIST_ACCOUNTING 0x9876123a + +#define EINA_MAGIC_ARRAY 0x9876123b +#define EINA_MAGIC_ARRAY_ITERATOR 0x9876123c +#define EINA_MAGIC_ARRAY_ACCESSOR 0x9876123d + +#define EINA_MAGIC_HASH 0x9876123e +#define EINA_MAGIC_HASH_ITERATOR 0x9876123f + +#define EINA_MAGIC_TILER 0x98761240 +#define EINA_MAGIC_TILER_ITERATOR 0x98761241 + +#define EINA_MAGIC_MATRIXSPARSE 0x98761242 +#define EINA_MAGIC_MATRIXSPARSE_ROW 0x98761243 +#define EINA_MAGIC_MATRIXSPARSE_CELL 0x98761244 +#define EINA_MAGIC_MATRIXSPARSE_ITERATOR 0x98761245 +#define EINA_MAGIC_MATRIXSPARSE_ROW_ITERATOR 0x98761246 +#define EINA_MAGIC_MATRIXSPARSE_ROW_ACCESSOR 0x98761247 +#define EINA_MAGIC_MATRIXSPARSE_CELL_ITERATOR 0x98761248 +#define EINA_MAGIC_MATRIXSPARSE_CELL_ACCESSOR 0x98761249 + +#define EINA_MAGIC_STRBUF 0x98761250 +#define EINA_MAGIC_USTRBUF 0x98761257 + +#define EINA_MAGIC_QUADTREE 0x98761251 +#define EINA_MAGIC_QUADTREE_ROOT 0x98761252 +#define EINA_MAGIC_QUADTREE_ITEM 0x98761253 + +/* undef the following, we want out version */ +#undef FREE +#define FREE(ptr) \ + do { \ + free(ptr); \ + ptr = NULL; \ + } while(0); + +#undef IF_FREE +#define IF_FREE(ptr) \ + do { \ + if (ptr) { \ + free(ptr); \ + ptr = NULL; \ + } \ + } while(0); + +#undef IF_FN_DEL +#define IF_FN_DEL(_fn, ptr) \ + do { \ + if (ptr) { \ + _fn(ptr); \ + ptr = NULL; \ + } \ + } while(0); + +#define MAGIC_FREE(ptr) \ + do { \ + if (ptr) { \ + EINA_MAGIC_SET(ptr, EINA_MAGIC_NONE); \ + FREE(ptr); \ + } \ + } while(0); + +#ifdef EFL_HAVE_THREADS +void eina_share_common_threads_init(void); +void eina_share_common_threads_shutdown(void); +void eina_log_threads_init(void); +void eina_log_threads_shutdown(void); +#endif + +#endif /* EINA_PRIVATE_H_ */ + diff --git a/tests/suite/ecore/src/lib/eina_quadtree.c b/tests/suite/ecore/src/lib/eina_quadtree.c new file mode 100644 index 0000000000..251bb96faa --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_quadtree.c @@ -0,0 +1,916 @@ +/* EINA - EFL data type library + * Copyright (C) 2010 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/** + * @page tutorial_quadtree_page QuadTree Tutorial + * + * to be written... + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> + +#include "eina_quadtree.h" +#include "eina_magic.h" +#include "eina_mempool.h" +#include "eina_list.h" +#include "eina_inlist.h" +#include "eina_trash.h" +#include "eina_log.h" +#include "eina_rectangle.h" + +#include "eina_private.h" + +typedef struct _Eina_QuadTree_Root Eina_QuadTree_Root; + +static const char EINA_MAGIC_QUADTREE_STR[] = "Eina QuadTree"; +static const char EINA_MAGIC_QUADTREE_ROOT_STR[] = "Eina QuadTree Root"; +static const char EINA_MAGIC_QUADTREE_ITEM_STR[] = "Eina QuadTree Item"; + +#define EINA_MAGIC_CHECK_QUADTREE(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE); \ + return __VA_ARGS__; \ + } \ + } while(0); + +#define EINA_MAGIC_CHECK_QUADTREE_ROOT(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE_ROOT)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE_ROOT); \ + return __VA_ARGS__; \ + } \ + } while(0); + +#define EINA_MAGIC_CHECK_QUADTREE_ITEM(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE_ITEM)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE_ITEM); \ + return __VA_ARGS__; \ + } \ + } while(0); + +struct _Eina_QuadTree +{ + EINA_MAGIC; + + Eina_QuadTree_Root *root; + + Eina_List *hidden; + + size_t root_count; + size_t items_count; + + Eina_Trash *items_trash; + Eina_Trash *root_trash; + + Eina_Inlist *change; + Eina_Inlist *cached; + Eina_Rectangle target; + + size_t index; + + struct + { + Eina_Quad_Callback v; + Eina_Quad_Callback h; + } func; + + struct + { + size_t w; + size_t h; + } geom; + + Eina_Bool resize : 1; + Eina_Bool lost : 1; +}; + +struct _Eina_QuadTree_Root +{ + EINA_MAGIC; + + Eina_QuadTree_Root *parent; + Eina_QuadTree_Root *left; + Eina_QuadTree_Root *right; + + Eina_List *both; + + Eina_Bool sorted : 1; +}; + +struct _Eina_QuadTree_Item +{ + EINA_MAGIC; + EINA_INLIST; + + Eina_QuadTree *quad; + Eina_QuadTree_Root *root; + + const void *object; + + size_t index; + + Eina_Bool change : 1; + Eina_Bool delete_me : 1; + Eina_Bool visible : 1; + Eina_Bool hidden : 1; +}; + +static int _eina_log_qd_dom = -1; +static Eina_Mempool *root_mp = NULL; +static Eina_Mempool *items_mp = NULL; + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_log_qd_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_log_qd_dom, __VA_ARGS__) + + +static int +_eina_quadtree_item_cmp(const void *a, const void *b) +{ + const Eina_QuadTree_Item *i = a; + const Eina_QuadTree_Item *j = b; + + return i->index - j->index; +} + +static Eina_QuadTree_Root * +eina_quadtree_root_free(Eina_QuadTree *q, Eina_QuadTree_Root *root) +{ + Eina_QuadTree_Item *item; + + if (!root) + return NULL; + + EINA_MAGIC_CHECK_QUADTREE_ROOT(root, NULL); + + EINA_LIST_FREE(root->both, item) + eina_mempool_free(items_mp, item); + + root->left = eina_quadtree_root_free(q, root->left); + root->right = eina_quadtree_root_free(q, root->right); + + EINA_MAGIC_SET(root, 0); + eina_mempool_free(root_mp, root); + + return NULL; +} + +static Eina_QuadTree_Root * +eina_quadtree_root_rebuild_pre(Eina_QuadTree *q, + Eina_Inlist **change, + Eina_QuadTree_Root *root) +{ + Eina_QuadTree_Item *item; + + if (!root) + return NULL; + + EINA_LIST_FREE(root->both, item) + { + if (item->visible) + *change = eina_inlist_append(*change, EINA_INLIST_GET(item)); + else if (!item->hidden) + { + q->hidden = eina_list_append(q->hidden, item); + item->hidden = EINA_TRUE; + item->root = NULL; + } + } + + root->left = eina_quadtree_root_rebuild_pre(q, change, root->left); + root->right = eina_quadtree_root_rebuild_pre(q, change, root->right); + + EINA_MAGIC_SET(root, 0); + if (q->root_count > 50) + eina_mempool_free(root_mp, root); + else + { + eina_trash_push(&q->root_trash, root); + q->root_count++; + } + + return NULL; +} + +static size_t +_eina_quadtree_split(Eina_Inlist *objects, + Eina_QuadTree_Root *root, + Eina_Inlist **left, + Eina_Inlist **right, + Eina_Quad_Callback func, + int border, + int middle) +{ + Eina_QuadTree_Item *object; + + middle /= 2; + + if (middle <= 4) + while (objects) + { + object = EINA_INLIST_CONTAINER_GET(objects, Eina_QuadTree_Item); + objects = objects->next; + + object->change = EINA_FALSE; + if (!object->visible) + { + if (!object->hidden) + { + object->hidden = EINA_TRUE; + object->quad->hidden = eina_list_append( + object->quad->hidden, + object); + } + + continue; + } + + if (object->hidden) + { + object->hidden = EINA_FALSE; + object->quad->hidden = eina_list_remove(object->quad->hidden, + object); + } + + if (!object->delete_me) + { + if (root->sorted) + root->both = eina_list_sorted_insert(root->both, + _eina_quadtree_item_cmp, + object); + else + root->both = eina_list_append(root->both, object); + + object->root = root; + } + else + eina_quadtree_del(object); + } + else + while (objects) + { + object = EINA_INLIST_CONTAINER_GET(objects, Eina_QuadTree_Item); + objects = objects->next; + + object->change = EINA_FALSE; + if (!object->visible) + { + if (!object->hidden) + { + object->hidden = EINA_TRUE; + object->quad->hidden = eina_list_append( + object->quad->hidden, + object); + } + + continue; + } + + if (object->hidden) + { + object->hidden = EINA_FALSE; + object->quad->hidden = eina_list_remove(object->quad->hidden, + object); + } + + if (!object->delete_me) + { + switch (func(object->object, border + middle)) + { + case EINA_QUAD_LEFT: + *left = eina_inlist_append(*left, EINA_INLIST_GET(object)); + break; + + case EINA_QUAD_RIGHT: + *right = + eina_inlist_append(*right, EINA_INLIST_GET(object)); + break; + + case EINA_QUAD_BOTH: + root->both = eina_list_append(root->both, object); + object->root = root; + break; + + default: + abort(); + } + } + else + eina_quadtree_del(object); + } + + return middle; +} + + +static Eina_QuadTree_Root * +_eina_quadtree_update(Eina_QuadTree *q, Eina_QuadTree_Root *parent, + Eina_QuadTree_Root *root, Eina_Inlist *objects, + Eina_Bool direction, Eina_Rectangle *size) +{ + Eina_Inlist *right = NULL; + Eina_Inlist *left = NULL; + size_t w2; + size_t h2; + + if (!objects) + return root; + + if (!root) + { + root = eina_trash_pop(&q->root_trash); + if (!root) + root = eina_mempool_malloc(root_mp, sizeof (Eina_QuadTree_Root)); + else + q->root_count--; + + if (!root) + /* FIXME: NOT GOOD TIMING, WE ARE GOING TO LEAK MORE MEMORY */ + return NULL; + + root->parent = parent; + root->both = NULL; + root->left = NULL; + root->right = NULL; + root->sorted = EINA_TRUE; + + EINA_MAGIC_SET(root, EINA_MAGIC_QUADTREE_ROOT); + } + + w2 = 0; + h2 = 0; + + if (direction) + w2 = _eina_quadtree_split(objects, root, + &left, &right, + q->func.h, size->x, size->w); + else + h2 = _eina_quadtree_split(objects, root, + &left, &right, + q->func.v, size->y, size->h); + + size->w -= w2; size->h -= h2; + root->left = _eina_quadtree_update(q, root, + root->left, left, + !direction, size); + size->x += w2; size->y += h2; + root->right = _eina_quadtree_update(q, root, + root->right, right, + !direction, size); + size->x -= w2; size->y -= h2; + size->w += w2; size->h += h2; + + return root; +} + +static Eina_Inlist * +_eina_quadtree_merge(Eina_Inlist *result, + Eina_List *both) +{ + Eina_QuadTree_Item *item; + Eina_QuadTree_Item *b; + Eina_Inlist *moving; + + if (!both) + return result; + + if (!result) + { + Eina_List *l; + + EINA_LIST_FOREACH(both, l, item) + if (item->visible) + result = eina_inlist_append(result, EINA_INLIST_GET(item)); + + return result; + } + + moving = result; + + item = EINA_INLIST_CONTAINER_GET(moving, Eina_QuadTree_Item); + b = eina_list_data_get(both); + + while (both && moving) + { + if (!b->visible) + { + both = eina_list_next(both); + b = eina_list_data_get(both); + continue; + } + + if (_eina_quadtree_item_cmp(item, b) < 0) + { + /* moving is still lower than item, so we can continue to the next one. */ + moving = moving->next; + item = EINA_INLIST_CONTAINER_GET(moving, Eina_QuadTree_Item); + } + else + { + /* we just get above the limit of both, so insert it */ + result = eina_inlist_prepend_relative(result, + EINA_INLIST_GET(b), + moving); + both = eina_list_next(both); + b = eina_list_data_get(both); + } + } + + item = EINA_INLIST_CONTAINER_GET(result->last, Eina_QuadTree_Item); + + while (both) + { + b = eina_list_data_get(both); + if (b->visible) + { + if (_eina_quadtree_item_cmp(item, b) < 0) + break; + + result = eina_inlist_prepend_relative(result, + EINA_INLIST_GET(b), + result->last); + } + + both = eina_list_next(both); + } + + while (both) + { + b = eina_list_data_get(both); + if (b->visible) + result = eina_inlist_append(result, EINA_INLIST_GET(b)); + + both = eina_list_next(both); + } + + return result; +} + +static Eina_Inlist * +_eina_quadtree_collide(Eina_Inlist *result, + Eina_QuadTree_Root *root, + Eina_Bool direction, Eina_Rectangle *size, + Eina_Rectangle *target) +{ + if (!root) + return result; + + if (!root->sorted) + { + root->both = eina_list_sort(root->both, -1, _eina_quadtree_item_cmp); + root->sorted = EINA_TRUE; + } + + result = _eina_quadtree_merge(result, root->both); + DBG("%p: %i in both for (%i, %i - %i, %i)", + root, eina_list_count(root->both), + size->x, size->y, size->w, size->h); + + if (direction) + { + int middle = size->w / 2; + + size->w -= middle; + if (eina_spans_intersect(size->x, size->w, target->x, target->w)) + result = _eina_quadtree_collide(result, root->left, + !direction, size, + target); + + size->x += middle; + if (eina_spans_intersect(size->x, size->w, target->x, target->w)) + result = _eina_quadtree_collide(result, root->right, + !direction, size, + target); + + size->x -= middle; + size->w += middle; + } + else + { + int middle = size->h / 2; + + size->h -= middle; + if (eina_spans_intersect(size->y, size->h, target->y, target->h)) + result = _eina_quadtree_collide(result, root->left, + !direction, size, + target); + + size->y += middle; + if (eina_spans_intersect(size->y, size->h, target->y, target->h)) + result = _eina_quadtree_collide(result, root->right, + !direction, size, + target); + + size->y -= middle; + size->h += middle; + } + + return result; +} + +static void +_eina_quadtree_remove(Eina_QuadTree_Item *object) +{ + if (!object->root) + return; + + object->root->both = eina_list_remove(object->root->both, object); + if (object->root->both) + goto end; + + if (object->root->left) + goto end; + + if (object->root->right) + goto end; + + /* The root is not useful anymore... */ + if (object->root->parent) + { + if (object->root->parent->left == object->root) + object->root->parent->left = NULL; + else + object->root->parent->right = NULL; + + object->root->parent = NULL; + } + else + object->quad->root = NULL; + + if (object->quad->root_count > 50) + eina_mempool_free(root_mp, object->root); + else + { + eina_trash_push(&object->quad->root_trash, object->root); + object->quad->root_count++; + } + +end: + object->root = NULL; +} + +EAPI Eina_QuadTree * +eina_quadtree_new(size_t w, size_t h, + Eina_Quad_Callback vertical, Eina_Quad_Callback horizontal) +{ + Eina_QuadTree *result; + + if (!vertical || !horizontal || h == 0 || w == 0) + return NULL; + + result = calloc(1, sizeof (Eina_QuadTree)); + if (!result) + return NULL; + + result->func.v = vertical; + result->func.h = horizontal; + + result->geom.w = w; + result->geom.h = h; + + result->change = NULL; + + result->lost = EINA_TRUE; + + EINA_MAGIC_SET(result, EINA_MAGIC_QUADTREE); + + return result; +} + +EAPI void +eina_quadtree_free(Eina_QuadTree *q) +{ + Eina_QuadTree_Item *item; + + if (!q) + return; + + EINA_MAGIC_CHECK_QUADTREE(q); + + while (q->change) + { + item = EINA_INLIST_CONTAINER_GET(q->change, Eina_QuadTree_Item); + q->change = q->change->next; + if (!item->hidden) + eina_mempool_free(items_mp, item); + } + + EINA_LIST_FREE(q->hidden, item) + eina_mempool_free(items_mp, item); + + eina_quadtree_root_free(q, q->root); + + while (q->items_trash) + { + item = eina_trash_pop(&q->items_trash); + eina_mempool_free(items_mp, item); + } + + while (q->root_trash) + { + Eina_QuadTree_Root *root; + + root = eina_trash_pop(&q->root_trash); + eina_mempool_free(root_mp, root); + } + + EINA_MAGIC_SET(q, 0); + free(q); +} + +EAPI Eina_QuadTree_Item * +eina_quadtree_add(Eina_QuadTree *q, const void *object) +{ + Eina_QuadTree_Item *result; + + EINA_MAGIC_CHECK_QUADTREE(q, NULL); + + if (!object) + return NULL; + + result = eina_trash_pop(&q->items_trash); + if (!result) + result = eina_mempool_malloc(items_mp, sizeof (Eina_QuadTree_Item)); + else + q->items_count--; + + if (!result) + return NULL; + + result->quad = q; + result->root = NULL; + result->object = object; + + result->index = q->index++; + + result->change = EINA_TRUE; + result->delete_me = EINA_FALSE; + result->visible = EINA_TRUE; + result->hidden = EINA_FALSE; + + EINA_MAGIC_SET(result, EINA_MAGIC_QUADTREE_ITEM); + + /* Insertion is delayed until we really need to use it */ + q->change = eina_inlist_append(q->change, EINA_INLIST_GET(result)); + + return result; +} + +EAPI Eina_Bool +eina_quadtree_del(Eina_QuadTree_Item *object) +{ + if (!object) + return EINA_FALSE; + + EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE); + + _eina_quadtree_remove(object); + + if (object->change) + { + /* This object is still in the update array, delaying it's removal !*/ + object->delete_me = EINA_TRUE; + object->visible = EINA_TRUE; + return EINA_TRUE; + } + + if (object->hidden) + { + object->quad->hidden = eina_list_remove(object->quad->hidden, object); + object->hidden = EINA_TRUE; + } + + /* This object is not anymore inside the tree, we can remove it now !*/ + EINA_MAGIC_SET(object, 0); + if (object->quad->items_count > 256) + eina_mempool_free(items_mp, object); + else + { + object->quad->items_count++; + eina_trash_push(&object->quad->items_trash, object); + } + + return EINA_TRUE; +} + +EAPI Eina_Bool +eina_quadtree_change(Eina_QuadTree_Item *object) +{ + EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE); + + if (object->delete_me || !object->visible) + return EINA_FALSE; + + if (object->quad->resize) + return EINA_TRUE; + + /* Delaying change until needed */ + if (!object->change) + object->quad->change = eina_inlist_append(object->quad->change, + EINA_INLIST_GET(object)); + + object->change = EINA_TRUE; + + _eina_quadtree_remove(object); + + return EINA_TRUE; +} + +EAPI Eina_Bool +eina_quadtree_hide(Eina_QuadTree_Item *object) +{ + EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE); + + object->visible = EINA_FALSE; + + return EINA_TRUE; +} + +EAPI Eina_Bool +eina_quadtree_show(Eina_QuadTree_Item *object) +{ + EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE); + + object->quad->lost = EINA_TRUE; + + if (object->visible) + return EINA_TRUE; + + object->visible = EINA_TRUE; + if (!object->change) + return eina_quadtree_change(object); + + return EINA_TRUE; +} + +EAPI Eina_Inlist * +eina_quadtree_collide(Eina_QuadTree *q, int x, int y, int w, int h) +{ + Eina_Rectangle canvas; + + EINA_MAGIC_CHECK_QUADTREE(q, NULL); + + /* Now we need the tree to be up to date, so it's time */ + if (q->resize) /* Full rebuild needed ! */ + { + DBG("resizing quadtree"); + q->root = eina_quadtree_root_rebuild_pre(q, &q->change, q->root); + q->resize = EINA_FALSE; + } + + EINA_RECTANGLE_SET(&canvas, 0, 0, q->geom.w, q->geom.h); + + if (q->change) + { + DBG("updating quadtree content"); + q->root = _eina_quadtree_update(q, NULL, q->root, q->change, + EINA_FALSE, &canvas); + q->change = NULL; + q->lost = EINA_TRUE; + } + + if (q->target.x != x + || q->target.y != y + || q->target.w != w + || q->target.h != h) + { + DBG("new target"); + EINA_RECTANGLE_SET(&q->target, x, y, w, h); + q->lost = EINA_TRUE; + } + + if (q->lost) + { + DBG("computing collide"); + q->cached = _eina_quadtree_collide(NULL, q->root, + EINA_FALSE, &canvas, + &q->target); + q->lost = EINA_FALSE; + } + + return q->cached; +} + +EAPI void * +eina_quadtree_object(Eina_Inlist *item) +{ + Eina_QuadTree_Item *qi; + + if (!item) + return NULL; + + qi = EINA_INLIST_CONTAINER_GET(item, Eina_QuadTree_Item); + if (!qi) + return NULL; + + EINA_MAGIC_CHECK_QUADTREE_ITEM(qi, NULL); + + if (!qi->visible) + return NULL; + + return (void *)qi->object; +} + +EAPI void +eina_quadtree_resize(Eina_QuadTree *q, size_t w, size_t h) +{ + EINA_MAGIC_CHECK_QUADTREE(q); + + if (q->geom.w == w + && q->geom.h == h) + return; + + q->resize = EINA_TRUE; + q->geom.w = w; + q->geom.h = h; +} + +EAPI void +eina_quadtree_cycle(Eina_QuadTree *q) +{ + EINA_MAGIC_CHECK_QUADTREE(q); + + q->index = 0; +} + +EAPI void +eina_quadtree_increase(Eina_QuadTree_Item *object) +{ + size_t tmp; + + tmp = object->quad->index++; + if (object->index == tmp) + return; + + object->index = tmp; + if (object->root) + object->root->sorted = EINA_FALSE; +} + +Eina_Bool +eina_quadtree_init(void) +{ + _eina_log_qd_dom = eina_log_domain_register("eina_quadtree", + EINA_LOG_COLOR_DEFAULT); + if (_eina_log_qd_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_quadtree"); + return EINA_FALSE; + } + +#define EMS(n) eina_magic_string_static_set(n, n ## _STR) + EMS(EINA_MAGIC_QUADTREE); + EMS(EINA_MAGIC_QUADTREE_ROOT); + EMS(EINA_MAGIC_QUADTREE_ITEM); +#undef EMS + + items_mp = eina_mempool_add("chained_mempool", "QuadTree Item", NULL, + sizeof (Eina_QuadTree_Item), 320); + root_mp = eina_mempool_add("chained_mempool", "QuadTree Root", NULL, + sizeof (Eina_QuadTree_Root), 32); + + return EINA_TRUE; +} + +Eina_Bool +eina_quadtree_shutdown(void) +{ + eina_log_domain_unregister(_eina_log_qd_dom); + _eina_log_qd_dom = -1; + return EINA_TRUE; +} + + + diff --git a/tests/suite/ecore/src/lib/eina_rbtree.c b/tests/suite/ecore/src/lib/eina_rbtree.c new file mode 100644 index 0000000000..1f03308426 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_rbtree.c @@ -0,0 +1,592 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_array.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_rbtree.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +#define EINA_RBTREE_ITERATOR_PREFIX_MASK 0x1 +#define EINA_RBTREE_ITERATOR_INFIX_MASK 0x2 +#define EINA_RBTREE_ITERATOR_POSTFIX_MASK 0x4 + +typedef struct _Eina_Iterator_Rbtree Eina_Iterator_Rbtree; +typedef struct _Eina_Iterator_Rbtree_List Eina_Iterator_Rbtree_List; + +struct _Eina_Iterator_Rbtree +{ + Eina_Iterator iterator; + + Eina_Array *stack; + + unsigned char mask; +}; + +struct _Eina_Iterator_Rbtree_List +{ + Eina_Rbtree *tree; + + Eina_Rbtree_Direction dir : 1; + Eina_Bool up : 1; +}; + +static Eina_Iterator_Rbtree_List * +_eina_rbtree_iterator_list_new(const Eina_Rbtree *tree) +{ + Eina_Iterator_Rbtree_List *new; + + eina_error_set(0); + new = malloc(sizeof (Eina_Iterator_Rbtree_List)); + if (!new) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + new->tree = (Eina_Rbtree *)tree; + new->dir = EINA_RBTREE_RIGHT; + new->up = EINA_FALSE; + + return new; +} + +static Eina_Rbtree * +_eina_rbtree_iterator_get_content(Eina_Iterator_Rbtree *it) +{ + if (eina_array_count_get(it->stack) <= 0) + return NULL; + + return eina_array_data_get(it->stack, 0); +} + +static void +_eina_rbtree_iterator_free(Eina_Iterator_Rbtree *it) +{ + Eina_Iterator_Rbtree_List *item; + Eina_Array_Iterator et; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(it->stack, i, item, et) + free(item); + + eina_array_free(it->stack); + free(it); +} + +static Eina_Bool +_eina_rbtree_iterator_next(Eina_Iterator_Rbtree *it, void **data) +{ + Eina_Iterator_Rbtree_List *last; + Eina_Iterator_Rbtree_List *new; + Eina_Rbtree *tree; + + if (eina_array_count_get(it->stack) <= 0) + return EINA_FALSE; + + last = eina_array_data_get(it->stack, eina_array_count_get(it->stack) - 1); + tree = last->tree; + + if (!last->tree || last->up == EINA_TRUE) + { + last = eina_array_pop(it->stack); + while (last->dir == EINA_RBTREE_LEFT + || !last->tree) + { + if (tree) + if ((it->mask & EINA_RBTREE_ITERATOR_POSTFIX_MASK) == + EINA_RBTREE_ITERATOR_POSTFIX_MASK) + { + free(last); + + if (eina_array_count_get(it->stack) > 0) + { + last = eina_array_data_get(it->stack, + eina_array_count_get( + it-> + stack) + - 1); + last->up = EINA_TRUE; + } + + goto onfix; + } + + free(last); + + last = eina_array_pop(it->stack); + if (!last) + return EINA_FALSE; + + tree = last->tree; + } + + last->dir = EINA_RBTREE_LEFT; + last->up = EINA_FALSE; + + eina_array_push(it->stack, last); + + if ((it->mask & EINA_RBTREE_ITERATOR_INFIX_MASK) == + EINA_RBTREE_ITERATOR_INFIX_MASK) + goto onfix; + } + + new = _eina_rbtree_iterator_list_new(last->tree->son[last->dir]); + if (!new) + return EINA_FALSE; + + eina_array_push(it->stack, new); + + if (last->dir == EINA_RBTREE_RIGHT) + if ((it->mask & EINA_RBTREE_ITERATOR_PREFIX_MASK) == + EINA_RBTREE_ITERATOR_PREFIX_MASK) + goto onfix; + + return _eina_rbtree_iterator_next(it, data); + +onfix: + *data = tree; + return EINA_TRUE; +} + +static Eina_Iterator * +_eina_rbtree_iterator_build(const Eina_Rbtree *root, unsigned char mask) +{ + Eina_Iterator_Rbtree_List *first; + Eina_Iterator_Rbtree *it; + + eina_error_set(0); + it = calloc(1, sizeof (Eina_Iterator_Rbtree)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + it->stack = eina_array_new(8); + if (!it->stack) + goto on_error2; + + first = _eina_rbtree_iterator_list_new(root); + if (!first) + goto on_error; + + eina_array_push(it->stack, first); + + it->mask = mask; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_rbtree_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _eina_rbtree_iterator_get_content); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_rbtree_iterator_free); + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + return &it->iterator; + +on_error: + eina_array_free(it->stack); +on_error2: + free(it); + + return NULL; +} + +/* + * Thanks to Julienne Walker public domain tutorial. + * http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx + */ + +static void +_eina_rbtree_node_init(Eina_Rbtree *node) +{ + if (!node) + return; + + node->son[0] = NULL; + node->son[1] = NULL; + + node->color = EINA_RBTREE_RED; +} + +static inline Eina_Bool +_eina_rbtree_is_red(Eina_Rbtree *node) +{ + return !!node && node->color == EINA_RBTREE_RED; +} + +static inline Eina_Rbtree * +_eina_rbtree_inline_single_rotation(Eina_Rbtree *node, + Eina_Rbtree_Direction dir) +{ + Eina_Rbtree *save = node->son[!dir]; + + node->son[!dir] = save->son[dir]; + save->son[dir] = node; + + node->color = EINA_RBTREE_RED; + save->color = EINA_RBTREE_BLACK; + + return save; +} + +static inline Eina_Rbtree * +_eina_rbtree_inline_double_rotation(Eina_Rbtree *node, + Eina_Rbtree_Direction dir) +{ + node->son[!dir] = _eina_rbtree_inline_single_rotation(node->son[!dir], !dir); + return _eina_rbtree_inline_single_rotation(node, dir); +} + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Rbtree_Group Red-Black tree + * + * @brief These functions provide Red-Black trees management. + * + * @{ + */ + +EAPI Eina_Rbtree * +eina_rbtree_inline_insert(Eina_Rbtree *root, + Eina_Rbtree *node, + Eina_Rbtree_Cmp_Node_Cb cmp, + const void *data) +{ + Eina_Rbtree head; + Eina_Rbtree *g, *t; /* Grandparent & parent */ + Eina_Rbtree *p, *q; /* Iterator & parent */ + /* WARNING: + Compiler is not able to understand the underlying algorithm and don't know that + first top node is always black, so it will never use last before running the loop + one time. + */ + Eina_Rbtree_Direction dir, last; + + EINA_SAFETY_ON_NULL_RETURN_VAL(node, root); + EINA_SAFETY_ON_NULL_RETURN_VAL( cmp, root); + + if (!node) + return root; + + _eina_rbtree_node_init(node); + + if (!root) + { + root = node; + goto end_add; + } + + memset(&head, 0, sizeof (Eina_Rbtree)); + last = dir = EINA_RBTREE_LEFT; + + /* Set up helpers */ + t = &head; + g = p = NULL; + q = t->son[1] = root; + + /* Search down the tree */ + for (;; ) + { + if (!q) + /* Insert new node at the bottom */ + p->son[dir] = q = node; + else if (_eina_rbtree_is_red(q->son[0]) + && _eina_rbtree_is_red(q->son[1])) + { + /* Color flip */ + q->color = EINA_RBTREE_RED; + q->son[0]->color = EINA_RBTREE_BLACK; + q->son[1]->color = EINA_RBTREE_BLACK; + } + + /* Fix red violation */ + if (_eina_rbtree_is_red(q) && _eina_rbtree_is_red(p)) + { + Eina_Rbtree_Direction dir2; + + dir2 = (t->son[1] == g) ? EINA_RBTREE_RIGHT : EINA_RBTREE_LEFT; + + if (q == p->son[last]) + t->son[dir2] = _eina_rbtree_inline_single_rotation(g, !last); + else + t->son[dir2] = _eina_rbtree_inline_double_rotation(g, !last); + } + + /* Stop if found */ + if (q == node) + break; + + last = dir; + dir = cmp(q, node, (void *)data); + + /* Update helpers */ + if ( g ) + t = g; + + g = p, p = q; + q = q->son[dir]; + } + + root = head.son[1]; + +end_add: + /* Make root black */ + root->color = EINA_RBTREE_BLACK; + + return root; +} + +EAPI Eina_Rbtree * +eina_rbtree_inline_remove(Eina_Rbtree *root, + Eina_Rbtree *node, + Eina_Rbtree_Cmp_Node_Cb cmp, + const void *data) +{ + Eina_Rbtree head; + Eina_Rbtree *q, *p; + Eina_Rbtree *f = NULL; + Eina_Rbtree_Direction dir; + + EINA_SAFETY_ON_NULL_RETURN_VAL(node, root); + EINA_SAFETY_ON_NULL_RETURN_VAL( cmp, root); + + if (!root || !node) + return root; + + memset(&head, 0, sizeof(Eina_Rbtree)); + + dir = EINA_RBTREE_RIGHT; + q = &head; + p = NULL; + q->son[EINA_RBTREE_RIGHT] = root; + + /* Search and push a red down */ + while (q->son[dir]) + { + Eina_Rbtree_Direction last = dir; + Eina_Rbtree *g; + + /* Update helpers */ + g = p; p = q; + q = q->son[dir]; + dir = cmp(q, node, (void *)data); + + /* Save parent node found */ + if (q == node) + f = p; + + /* Push the red node down */ + if (!_eina_rbtree_is_red(q) + && !_eina_rbtree_is_red(q->son[dir])) + { + if (_eina_rbtree_is_red(q->son[!dir])) + q = p->son[last] = _eina_rbtree_inline_single_rotation(q, dir); + else if (!_eina_rbtree_is_red(q->son[!dir])) + { + Eina_Rbtree *s = p->son[!last]; + + if (s) + { + if (!_eina_rbtree_is_red(s->son[EINA_RBTREE_LEFT]) + && !_eina_rbtree_is_red(s->son[EINA_RBTREE_RIGHT])) + { +/* Color flip */ + p->color = EINA_RBTREE_BLACK; + p->son[EINA_RBTREE_LEFT]->color = EINA_RBTREE_RED; + p->son[EINA_RBTREE_RIGHT]->color = EINA_RBTREE_RED; + } + else + { + Eina_Rbtree_Direction dir2; + + dir2 = g->son[1] == + p ? EINA_RBTREE_RIGHT : EINA_RBTREE_LEFT; + + if (_eina_rbtree_is_red(s->son[last])) + { + g->son[dir2] = + _eina_rbtree_inline_double_rotation(p, last); + if (f == g) + { + p = g->son[dir2]->son[last]; + f = g->son[dir2]; + } + } + else if (_eina_rbtree_is_red(s->son[!last])) + { + g->son[dir2] = + _eina_rbtree_inline_single_rotation(p, last); + if (f == g) + { + p = g->son[dir2]->son[last]; + f = g->son[dir2]; + } + } + +/* Ensure correct coloring */ + q->color = g->son[dir2]->color = EINA_RBTREE_RED; + g->son[dir2]->son[EINA_RBTREE_LEFT]->color = + EINA_RBTREE_BLACK; + g->son[dir2]->son[EINA_RBTREE_RIGHT]->color = + EINA_RBTREE_BLACK; + } + } + } + } + } + + /* Replace and remove if found */ + if (f) + { + /* 'q' should take the place of 'node' parent */ + f->son[f->son[1] == node] = q; + + /* Switch the link from the parent to q's son */ + p->son[p->son[1] == q] = q->son[!q->son[0]]; + + /* Put q at the place of node */ + q->son[0] = node->son[0]; + q->son[1] = node->son[1]; + q->color = node->color; + + /* Reset node link */ + node->son[0] = NULL; + node->son[1] = NULL; + } + + root = head.son[1]; + if (root) + root->color = EINA_RBTREE_BLACK; + + return root; +} + +/** + * @brief Returned a new prefix iterator associated to a rbtree. + * + * @param root The root of rbtree. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * root. It will iterate the tree using prefix walk. If @p root is @c + * NULL, this function still returns a valid iterator that will always + * return false on eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the rbtree structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_rbtree_iterator_prefix(const Eina_Rbtree *root) +{ + return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_PREFIX_MASK); +} + +/** + * @brief Returned a new prefix iterator associated to a rbtree. + * + * @param root The root of rbtree. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * root. It will iterate the tree using infix walk. If @p root is @c + * NULL, this function still returns a valid iterator that will always + * return false on eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the rbtree structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_rbtree_iterator_infix(const Eina_Rbtree *root) +{ + return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_INFIX_MASK); +} + +/** + * @brief Returned a new prefix iterator associated to a rbtree. + * + * @param root The root of rbtree. + * @return A new iterator. + * + * This function returns a newly allocated iterator associated to @p + * root. It will iterate the tree using postfix walk. If @p root is @c + * NULL, this function still returns a valid iterator that will always + * return false on eina_iterator_next(), thus keeping API sane. + * + * If the memory can not be allocated, NULL is returned and + * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is + * returned. + * + * @warning if the rbtree structure changes then the iterator becomes + * invalid! That is, if you add or remove nodes this iterator + * behavior is undefined and your program may crash! + */ +EAPI Eina_Iterator * +eina_rbtree_iterator_postfix(const Eina_Rbtree *root) +{ + return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_POSTFIX_MASK); +} + +EAPI void +eina_rbtree_delete(Eina_Rbtree *root, Eina_Rbtree_Free_Cb func, void *data) +{ + if (!root) + return; + + EINA_SAFETY_ON_NULL_RETURN(func); + + eina_rbtree_delete(root->son[0], func, data); + eina_rbtree_delete(root->son[1], func, data); + func(root, data); +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_rectangle.c b/tests/suite/ecore/src/lib/eina_rectangle.c new file mode 100644 index 0000000000..237ca8b461 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_rectangle.c @@ -0,0 +1,693 @@ +/* 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#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), 1024); + 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), 256); + 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 * +*============================================================================*/ + +/** + * @addtogroup Eina_Rectangle_Group Rectangle + * + * @brief These functions provide rectangle management. + * + * @{ + */ + +/** + * @brief Create a new rectangle. + * + * @param x The X coordinate of the top left corner of the rectangle. + * @param y The Y coordinate of the top left corner of the rectangle. + * @param w The width of the rectangle. + * @param h The height of the rectangle. + * @return The new rectangle on success, @ NULL otherwise. + * + * This function creates a rectangle which top left corner has the + * coordinates (@p x, @p y), with height @p w and height @p h and adds + * it to the rectangles pool. No check is done on @p w and @p h. This + * function returns a new rectangle on success, @c NULL otherwhise. + */ +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; +} + +/** + * @brief Free the given rectangle. + * + * @param rect The rectangle to free. + * + * This function removes @p rect from the rectangles pool. + */ +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++; + } +} + +/** + * @brief Add a rectangle in a new pool. + * + * @param w The width of the rectangle. + * @param h The height of the rectangle. + * @return A newly allocated pool on success, @c NULL otherwise. + * + * This function adds the rectangle of size (@p width, @p height) to a + * new pool. If the pool can not be created, @c NULL is + * returned. Otherwise the newly allocated pool is returned. + */ +EAPI Eina_Rectangle_Pool * +eina_rectangle_pool_new(int w, int h) +{ + Eina_Rectangle_Pool *new; + + 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; +} + +/** + * @brief Free the given pool. + * + * @param pool The pool to free. + * + * This function frees the allocated data of @p pool. If @p pool is + * @c NULL, ths function returned immediately. + */ +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); +} + +/** + * @brief Return the number of rectangles in the given pool. + * + * @param pool The pool. + * @return The number of rectangles in the pool. + * + * This function returns the number of rectangles in @p pool. + */ +EAPI int +eina_rectangle_pool_count(Eina_Rectangle_Pool *pool) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(pool, 0); + return pool->references; +} + +/** + * @brief Request a rectangle of given size in the given pool. + * + * @param pool The pool. + * @param w The width of the rectangle to request. + * @param h The height of the rectangle to request. + * @return The requested rectangle on success, @c NULL otherwise. + * + * This function retrieve from @p pool the rectangle of width @p w and + * height @p h. If @p pool is @c NULL, or @p w or @p h are non-positive, + * the function returns @c NULL. If @p w or @p h are greater than the + * pool size, the function returns @c NULL. On success, the function + * returns the rectangle which matches the size (@p w, @p h). + * Otherwise it returns @c NULL. + */ +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; +} + +/** + * @brief Remove the given rectangle from the pool. + * + * @param rect The rectangle to remove from the pool. + * + * This function removes @p rect from the pool. If @p rect is + * @c NULL, the function returns immediately. Otherwise it remoes @p + * rect from the pool. + */ +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); + } +} + +/** + * @brief Return the pool of the given rectangle. + * + * @param rect The rectangle. + * @return The pool of the given rectangle. + * + * This function returns the pool in which @p rect is. If @p rect is + * @c NULL, @c NULL is returned. + */ +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; +} + +/** + * @brief Set the data to the given pool. + * + * @param pool The pool. + * @param data The data to set. + * + * This function sets @p data to @p pool. If @p pool is @c NULL, this + * function does nothing. + */ +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; +} + +/** + * @brief Get the data from the given pool. + * + * @param pool The pool. + * @return The returned data. + * + * This function gets the data from @p pool set by + * eina_rectangle_pool_data_set(). If @p pool is @c NULL, this + * function returns @c NULL. + */ +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; +} + +/** + * @brief Return the width and height of the given pool. + * + * @param pool The pool. + * @param w The returned width. + * @param h The returned height. + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + * + * This function returns the width and height of @p pool and store + * them in respectively @p w and @p h if they are not @c NULL. If + * @p pool is @c NULL, #EINA_FALSE is returned. Otherwise #EINA_TRUE is + * returned. + */ +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; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_safety_checks.c b/tests/suite/ecore/src/lib/eina_safety_checks.c new file mode 100644 index 0000000000..09aa2983b5 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_safety_checks.c @@ -0,0 +1,114 @@ +/* EINA - EFL data type library + * Copyright (C) 2008 Gustavo Sverzut Barbieri + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "eina_private.h" +#include "eina_error.h" +#include "eina_log.h" +#include "eina_safety_checks.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Shut down the safety checks module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the error module set up by + * eina_safety_checks_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_safety_checks_shutdown(void) +{ + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Safety_Checks_Group Safety Checks + * + * Safety checks are a set of macros to check for parameters or values + * that should never happen, it is similar in concept to assert(), but + * will log and return instead of abort() your program. + * + * Since these cases should never happen, one may wantto keep safety + * checks enabled during tests but disable then during deploy, not + * doing any checks at all. This is a common requirement for embedded + * systems. Whenever to check or not should be set during compile time + * by using @c --disable-safety-checks or @c --enable-safety-checks + * options to @c configure script. + * + * Whenever these macros capture an error, EINA_LOG_ERR() will be + * called and @c eina_error set to @c EINA_ERROR_SAFETY_FAILED and can + * be checked with eina_error_get() after call. + * + * @see EINA_SAFETY_ON_NULL_RETURN(), EINA_SAFETY_ON_NULL_RETURN_VAL() + * and other macros. + * + * @{ + */ + +/** + * @cond LOCAL + */ + +EAPI Eina_Error EINA_ERROR_SAFETY_FAILED = 0; + +static const char EINA_ERROR_SAFETY_FAILED_STR[] = "Safety check failed."; + +/** + * @endcond + */ + +/** + * @internal + * @brief Initialize the safety checks module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the safety checks module of Eina. It is + * called by eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_safety_checks_init(void) +{ + EINA_ERROR_SAFETY_FAILED = eina_error_msg_static_register( + EINA_ERROR_SAFETY_FAILED_STR); + return EINA_TRUE; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_sched.c b/tests/suite/ecore/src/lib/eina_sched.c new file mode 100644 index 0000000000..dbb3da663c --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_sched.c @@ -0,0 +1,95 @@ +/* EINA - EFL data type library + * Copyright (C) 2010 ProFUSION embedded systems + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef EFL_HAVE_POSIX_THREADS +# include <pthread.h> +# ifdef __linux__ +# include <sched.h> +# include <sys/time.h> +# include <sys/resource.h> +# include <errno.h> +# endif +#endif + +#include "eina_sched.h" +#include "eina_log.h" + +#define RTNICENESS 5 +#define NICENESS 5 + +/** + * @brief Lower priority of current thread. + * + * It's used by worker threads so they use up background cpu and do not stall + * the main thread If current thread is running with real-time priority, we + * decrease our priority by @c RTNICENESS. This is done in a portable way. + * + * Otherwise (we are running with SCHED_OTHER policy) there's no portable way to + * set the nice level on current thread. In Linux, it does work and it's the + * only one that is implemented as of now. In this case the nice level is + * incremented on this thread by @c NICENESS. + */ +EAPI void +eina_sched_prio_drop(void) +{ +#ifdef EFL_HAVE_POSIX_THREADS + struct sched_param param; + int pol, prio, ret; + pthread_t pthread_id; + + pthread_id = pthread_self(); + ret = pthread_getschedparam(pthread_id, &pol, ¶m); + if (ret) + { + EINA_LOG_ERR("Unable to query sched parameters"); + return; + } + + if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO)) + { + prio = sched_get_priority_max(pol); + param.sched_priority += RTNICENESS; + if (prio > 0 && param.sched_priority > prio) + param.sched_priority = prio; + + pthread_setschedparam(pthread_id, pol, ¶m); + } +#ifdef __linux__ + else + { + errno = 0; + prio = getpriority(PRIO_PROCESS, 0); + if (errno == 0) + { + prio += NICENESS; + if (prio > 19) + prio = 19; + + setpriority(PRIO_PROCESS, 0, prio); + } + } +#endif +#else + EINA_LOG_ERR("Eina does not have support for threads enabled" + "or it doesn't support setting scheduler priorities"); +#endif +} diff --git a/tests/suite/ecore/src/lib/eina_share_common.c b/tests/suite/ecore/src/lib/eina_share_common.c new file mode 100644 index 0000000000..2302843eaa --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_share_common.c @@ -0,0 +1,974 @@ +/* EINA - EFL data type library + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2010 + * Carsten Haitzler, + * Jorge Luis Zapata Muga, + * Cedric Bail, + * Gustavo Sverzut Barbieri + * Tom Hacohen + * Brett Nash + * + * 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 <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (C) 2008 Peter Wehrfritz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software and its Copyright notices. In addition publicly + * documented acknowledgment must be given that this software has been used if no + * source code of this software is made available publicly. This includes + * acknowledgments in either Copyright notices, Manuals, Publicity and Marketing + * documents or any documentation provided with any product containing this + * software. This License does not apply to any software that links to the + * libraries provided by this software (statically or dynamically), but only to + * the software provided. + * + * Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice + * and it's intent. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stddef.h> + +#ifdef EFL_HAVE_POSIX_THREADS +# include <pthread.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_hash.h" +#include "eina_rbtree.h" +#include "eina_error.h" +#include "eina_log.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_share_common.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +#define EINA_SHARE_COMMON_BUCKETS 256 +#define EINA_SHARE_COMMON_MASK 0xFF + +static const char EINA_MAGIC_SHARE_STR[] = "Eina Share"; +static const char EINA_MAGIC_SHARE_HEAD_STR[] = "Eina Share Head"; + + +#define EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(d, unlock, ...) \ + do { \ + if (!EINA_MAGIC_CHECK((d), EINA_MAGIC_SHARE_HEAD)) \ + { \ + EINA_MAGIC_FAIL((d), EINA_MAGIC_SHARE_HEAD); \ + unlock; \ + return __VA_ARGS__; \ + } \ + } while (0) + +#define EINA_MAGIC_CHECK_SHARE_COMMON_NODE(d, _node_magic, unlock) \ + do { \ + if (!EINA_MAGIC_CHECK((d), _node_magic)) \ + { \ + unlock; \ + EINA_MAGIC_FAIL((d), _node_magic); \ + } \ + } while (0) + +#ifdef EINA_SHARE_USAGE +typedef struct _Eina_Share_Common_Population Eina_Share_Common_Population; +#endif + +typedef struct _Eina_Share_Common Eina_Share_Common; +typedef struct _Eina_Share_Common_Node Eina_Share_Common_Node; +typedef struct _Eina_Share_Common_Head Eina_Share_Common_Head; + +int _eina_share_common_log_dom = -1; + +struct _Eina_Share +{ + Eina_Share_Common *share; + Eina_Magic node_magic; +#ifdef EINA_SHARE_COMMON_USAGE + Eina_Share_Common_Population population; + int max_node_population; +#endif +}; + +struct _Eina_Share_Common +{ + Eina_Share_Common_Head *buckets[EINA_SHARE_COMMON_BUCKETS]; + + EINA_MAGIC +}; + +struct _Eina_Share_Common_Node +{ + Eina_Share_Common_Node *next; + + EINA_MAGIC + + unsigned int length; + unsigned int references; + char str[]; +}; + +struct _Eina_Share_Common_Head +{ + EINA_RBTREE; + EINA_MAGIC + + int hash; + +#ifdef EINA_SHARE_COMMON_USAGE + int population; +#endif + + Eina_Share_Common_Node *head; + Eina_Share_Common_Node builtin_node; +}; + +#ifdef EFL_HAVE_THREADS +Eina_Bool _share_common_threads_activated = EINA_FALSE; + +# ifdef EFL_HAVE_POSIX_THREADS +static pthread_mutex_t _mutex_big = PTHREAD_MUTEX_INITIALIZER; +# define SHARE_COMMON_LOCK_BIG() if(_share_common_threads_activated) \ + pthread_mutex_lock(&_mutex_big) +# define SHARE_COMMON_UNLOCK_BIG() if(_share_common_threads_activated) \ + pthread_mutex_unlock(&_mutex_big) +# else /* EFL_HAVE_WIN32_THREADS */ +static HANDLE _mutex_big = NULL; +# define SHARE_COMMON_LOCK_BIG() if(_share_common_threads_activated) \ + WaitForSingleObject(_mutex_big, INFINITE) +# define SHARE_COMMON_UNLOCK_BIG() if(_share_common_threads_activated) \ + ReleaseMutex(_mutex_big) + +# endif /* EFL_HAVE_WIN32_THREADS */ +#else /* EFL_HAVE_THREADS */ +# define SHARE_COMMON_LOCK_BIG() do {} while (0) +# define SHARE_COMMON_UNLOCK_BIG() do {} while (0) +#endif + +#ifdef EINA_SHARE_COMMON_USAGE +struct _Eina_Share_Common_Population +{ + int count; + int max; +}; + +static Eina_Share_Common_Population population = { 0, 0 }; + +static Eina_Share_Common_Population population_group[4] = +{ + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } +}; + +static void +_eina_share_common_population_init(Eina_Share *share) +{ + unsigned int i; + + for (i = 0; + i < sizeof (share->population_group) / + sizeof (share->population_group[0]); + ++i) + { + share->population_group[i].count = 0; + share->population_group[i].max = 0; + } +} + +static void +_eina_share_common_population_shutdown(Eina_Share *share) +{ + unsigned int i; + + share->max_node_population = 0; + share->population.count = 0; + share->population.max = 0; + + for (i = 0; + i < sizeof (share->population_group) / + sizeof (share->population_group[0]); + ++i) + { + share->population_group[i].count = 0; + share->population_group[i].max = 0; + } +} + +static void +_eina_share_common_population_stats(Eina_Share *share) +{ + unsigned int i; + + fprintf(stderr, "eina share_common statistic:\n"); + fprintf(stderr, + " * maximum shared strings : %i\n", + share->population.max); + fprintf(stderr, + " * maximum shared strings per node : %i\n", + share->max_node_population); + + for (i = 0; + i < sizeof (share->population_group) / + sizeof (share->population_group[0]); + ++i) + fprintf(stderr, + "DDD: %i strings of length %i, max strings: %i\n", + share->population_group[i].count, + i, + share->population_group[i].max); +} + +void +eina_share_common_population_add(Eina_Share *share, int slen) +{ + SHARE_COMMON_LOCK_BIG(); + + share->population.count++; + if (share->population.count > share->population.max) + share->population.max = share->population.count; + + if (slen < 4) + { + share->population_group[slen].count++; + if (share->population_group[slen].count > + share->population_group[slen].max) + share->population_group[slen].max = + share->population_group[slen].count; + } + + SHARE_COMMON_UNLOCK_BIG(); +} + +void +eina_share_common_population_del(Eina_Share *share, int slen) +{ + SHARE_COMMON_LOCK_BIG(); + + share->population.count--; + if (slen < 4) + share->population_group[slen].count--; + + SHARE_COMMON_UNLOCK_BIG(); +} + +static void +_eina_share_common_population_head_init(Eina_Share *share, + Eina_Share_Common_Head *head) +{ + head->population = 1; +} + +static void +_eina_share_common_population_head_add(Eina_Share *share, + Eina_Share_Common_Head *head) +{ + head->population++; + if (head->population > share->max_node_population) + share->max_node_population = head->population; +} + +static void +_eina_share_common_population_head_del(Eina_Share *share, + Eina_Share_Common_Head *head) +{ + head->population--; +} + +#else /* EINA_SHARE_COMMON_USAGE undefined */ + +static void _eina_share_common_population_init(__UNUSED__ Eina_Share *share) { +} +static void _eina_share_common_population_shutdown(__UNUSED__ Eina_Share *share) +{ +} +static void _eina_share_common_population_stats(__UNUSED__ Eina_Share *share) { +} +void eina_share_common_population_add(__UNUSED__ Eina_Share *share, + __UNUSED__ int slen) { +} +void eina_share_common_population_del(__UNUSED__ Eina_Share *share, + __UNUSED__ int slen) { +} +static void _eina_share_common_population_head_init( + __UNUSED__ Eina_Share *share, + __UNUSED__ Eina_Share_Common_Head *head) { +} +static void _eina_share_common_population_head_add( + __UNUSED__ Eina_Share *share, + __UNUSED__ + Eina_Share_Common_Head *head) { +} +static void _eina_share_common_population_head_del( + __UNUSED__ Eina_Share *share, + __UNUSED__ + Eina_Share_Common_Head *head) { +} +#endif + +static int +_eina_share_common_cmp(const Eina_Share_Common_Head *ed, + const int *hash, + __UNUSED__ int length, + __UNUSED__ void *data) +{ + EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, , 0); + + return ed->hash - *hash; +} + +static Eina_Rbtree_Direction +_eina_share_common_node(const Eina_Share_Common_Head *left, + const Eina_Share_Common_Head *right, + __UNUSED__ void *data) +{ + EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(left, , 0); + EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(right, , 0); + + if (left->hash - right->hash < 0) + return EINA_RBTREE_LEFT; + + return EINA_RBTREE_RIGHT; +} + +static void +_eina_share_common_head_free(Eina_Share_Common_Head *ed, __UNUSED__ void *data) +{ + EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, ); + + while (ed->head) + { + Eina_Share_Common_Node *el = ed->head; + + ed->head = ed->head->next; + if (el != &ed->builtin_node) + MAGIC_FREE(el); + } + MAGIC_FREE(ed); +} + +static void +_eina_share_common_node_init(Eina_Share_Common_Node *node, + const char *str, + int slen, + unsigned int null_size, + Eina_Magic node_magic) +{ + EINA_MAGIC_SET(node, node_magic); + node->references = 1; + node->length = slen; + memcpy(node->str, str, slen); + memset(node->str + slen, 0, null_size); /* Nullify the null */ + + (void) node_magic; /* When magic are disable, node_magic is unused, this remove a warning. */ +} + +static Eina_Share_Common_Head * +_eina_share_common_head_alloc(int slen) +{ + Eina_Share_Common_Head *head; + const size_t head_size = offsetof(Eina_Share_Common_Head, builtin_node.str); + + head = malloc(head_size + slen); + if (!head) + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + + return head; +} + +static const char * +_eina_share_common_add_head(Eina_Share *share, + Eina_Share_Common_Head **p_bucket, + int hash, + const char *str, + unsigned int slen, + unsigned int null_size) +{ + Eina_Rbtree **p_tree = (Eina_Rbtree **)p_bucket; + Eina_Share_Common_Head *head; + + head = _eina_share_common_head_alloc(slen + null_size); + if (!head) + return NULL; + + EINA_MAGIC_SET(head, EINA_MAGIC_SHARE_HEAD); + head->hash = hash; + head->head = &head->builtin_node; + _eina_share_common_node_init(head->head, + str, + slen, + null_size, + share->node_magic); + head->head->next = NULL; + + _eina_share_common_population_head_init(share, head); + + *p_tree = eina_rbtree_inline_insert + (*p_tree, EINA_RBTREE_GET(head), + EINA_RBTREE_CMP_NODE_CB(_eina_share_common_node), NULL); + + return head->head->str; +} + +static void +_eina_share_common_del_head(Eina_Share_Common_Head **p_bucket, + Eina_Share_Common_Head *head) +{ + Eina_Rbtree **p_tree = (Eina_Rbtree **)p_bucket; + + *p_tree = eina_rbtree_inline_remove + (*p_tree, EINA_RBTREE_GET(head), + EINA_RBTREE_CMP_NODE_CB(_eina_share_common_node), NULL); + + MAGIC_FREE(head); +} + + +static inline Eina_Bool +_eina_share_common_node_eq(const Eina_Share_Common_Node *node, + const char *str, + unsigned int slen) +{ + return ((node->length == slen) && + (memcmp(node->str, str, slen) == 0)); +} + +static Eina_Share_Common_Node * +_eina_share_common_head_find(Eina_Share_Common_Head *head, + const char *str, + unsigned int slen) +{ + Eina_Share_Common_Node *node, *prev; + + node = head->head; + if (_eina_share_common_node_eq(node, str, slen)) + return node; + + prev = node; + node = node->next; + for (; node; prev = node, node = node->next) + if (_eina_share_common_node_eq(node, str, slen)) + { + /* promote node, make hot items be at the beginning */ + prev->next = node->next; + node->next = head->head; + head->head = node; + return node; + } + + return NULL; +} + +static Eina_Bool +_eina_share_common_head_remove_node(Eina_Share_Common_Head *head, + const Eina_Share_Common_Node *node) +{ + Eina_Share_Common_Node *cur, *prev; + + if (head->head == node) + { + head->head = node->next; + return 1; + } + + prev = head->head; + cur = head->head->next; + for (; cur; prev = cur, cur = cur->next) + if (cur == node) + { + prev->next = cur->next; + return 1; + } + + return 0; +} + +static Eina_Share_Common_Head * +_eina_share_common_find_hash(Eina_Share_Common_Head *bucket, int hash) +{ + return (Eina_Share_Common_Head *)eina_rbtree_inline_lookup + (EINA_RBTREE_GET(bucket), &hash, 0, + EINA_RBTREE_CMP_KEY_CB(_eina_share_common_cmp), NULL); +} + +static Eina_Share_Common_Node * +_eina_share_common_node_alloc(unsigned int slen, unsigned int null_size) +{ + Eina_Share_Common_Node *node; + const size_t node_size = offsetof(Eina_Share_Common_Node, str); + + node = malloc(node_size + slen + null_size); + if (!node) + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + + return node; +} + +static Eina_Share_Common_Node * +_eina_share_common_node_from_str(const char *str, Eina_Magic node_magic) +{ + Eina_Share_Common_Node *node; + const size_t offset = offsetof(Eina_Share_Common_Node, str); + + node = (Eina_Share_Common_Node *)(str - offset); + EINA_MAGIC_CHECK_SHARE_COMMON_NODE(node, node_magic, ); + return node; + + (void) node_magic; /* When magic are disable, node_magic is unused, this remove a warning. */ +} + +static Eina_Bool +eina_iterator_array_check(const Eina_Rbtree *rbtree __UNUSED__, + Eina_Share_Common_Head *head, + struct dumpinfo *fdata) +{ + Eina_Share_Common_Node *node; + + SHARE_COMMON_LOCK_BIG(); + + fdata->used += sizeof(Eina_Share_Common_Head); + for (node = head->head; node; node = node->next) + { + printf("DDD: %5i %5i ", node->length, node->references); + printf("'%.*s'\n", node->length, ((char *)node) + sizeof(Eina_Share_Common_Node)); + fdata->used += sizeof(Eina_Share_Common_Node); + fdata->used += node->length; + fdata->saved += (node->references - 1) * node->length; + fdata->dups += node->references - 1; + fdata->unique++; + } + + SHARE_COMMON_UNLOCK_BIG(); + + return EINA_TRUE; +} + +/** + * @endcond + */ + + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the share_common module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the share_common module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_share_common_init(Eina_Share **_share, + Eina_Magic node_magic, + const char *node_magic_STR) +{ + Eina_Share *share; + share = *_share = calloc(sizeof(Eina_Share), 1); + if (!share) + return EINA_FALSE; + + if (_eina_share_common_log_dom < 0) /*Only register if not already */ + _eina_share_common_log_dom = eina_log_domain_register( + "eina_share", + EINA_LOG_COLOR_DEFAULT); + + if (_eina_share_common_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_share_common"); + return EINA_FALSE; + } + + share->share = calloc(1, sizeof(Eina_Share_Common)); + if (!share->share) + { + if (_eina_share_common_log_dom > 0) + { + eina_log_domain_unregister(_eina_share_common_log_dom); + _eina_share_common_log_dom = -1; + } + + return EINA_FALSE; + } + + share->node_magic = node_magic; +#define EMS(n) eina_magic_string_static_set(n, n ## _STR) + EMS(EINA_MAGIC_SHARE); + EMS(EINA_MAGIC_SHARE_HEAD); + EMS(node_magic); +#undef EMS + EINA_MAGIC_SET(share->share, EINA_MAGIC_SHARE); + + _eina_share_common_population_init(share); + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the share_common module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the share_common module set up by + * eina_share_common_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_share_common_shutdown(Eina_Share **_share) +{ + unsigned int i; + Eina_Share *share = *_share; + + SHARE_COMMON_LOCK_BIG(); + + _eina_share_common_population_stats(share); + + /* remove any string still in the table */ + for (i = 0; i < EINA_SHARE_COMMON_BUCKETS; i++) + { + eina_rbtree_delete(EINA_RBTREE_GET( + share->share->buckets[i]), + EINA_RBTREE_FREE_CB( + _eina_share_common_head_free), NULL); + share->share->buckets[i] = NULL; + } + MAGIC_FREE(share->share); + + _eina_share_common_population_shutdown(share); + if (_eina_share_common_log_dom > 0) /* Only free if necessary */ + { + eina_log_domain_unregister(_eina_share_common_log_dom); + _eina_share_common_log_dom = -1; + } + + SHARE_COMMON_UNLOCK_BIG(); + + free(*_share); + *_share = NULL; + return EINA_TRUE; +} + +#ifdef EFL_HAVE_THREADS + +/** + * @internal + * @brief Activate the share_common mutexes. + * + * This function activate the mutexes in the eina share_common module. It is called by + * eina_threads_init(). + * + * @see eina_threads_init() + */ +void +eina_share_common_threads_init(void) +{ + _share_common_threads_activated = EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the share_common mutexes. + * + * This function shuts down the mutexes in the share_common module. + * It is called by eina_threads_shutdown(). + * + * @see eina_threads_shutdown() + */ +void +eina_share_common_threads_shutdown(void) +{ + _share_common_threads_activated = EINA_FALSE; +} + +#endif + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +const char * +eina_share_common_add_length(Eina_Share *share, + const char *str, + unsigned int slen, + unsigned int null_size) +{ + Eina_Share_Common_Head **p_bucket, *ed; + Eina_Share_Common_Node *el; + int hash_num, hash; + + if (!str) + return NULL; + + eina_share_common_population_add(share, slen); + + if (slen <= 0) + return NULL; + + hash = eina_hash_superfast(str, slen); + hash_num = hash & 0xFF; + hash = (hash >> 8) & EINA_SHARE_COMMON_MASK; + + SHARE_COMMON_LOCK_BIG(); + p_bucket = share->share->buckets + hash_num; + + ed = _eina_share_common_find_hash(*p_bucket, hash); + if (!ed) + { + const char *s = _eina_share_common_add_head(share, + p_bucket, + hash, + str, + slen, + null_size); + SHARE_COMMON_UNLOCK_BIG(); + return s; + } + + EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, SHARE_COMMON_UNLOCK_BIG(), NULL); + + el = _eina_share_common_head_find(ed, str, slen); + if (el) + { + EINA_MAGIC_CHECK_SHARE_COMMON_NODE(el, + share->node_magic, + SHARE_COMMON_UNLOCK_BIG()); + el->references++; + SHARE_COMMON_UNLOCK_BIG(); + return el->str; + } + + el = _eina_share_common_node_alloc(slen, null_size); + if (!el) + { + SHARE_COMMON_UNLOCK_BIG(); + return NULL; + } + + _eina_share_common_node_init(el, str, slen, null_size, share->node_magic); + el->next = ed->head; + ed->head = el; + _eina_share_common_population_head_add(share, ed); + + SHARE_COMMON_UNLOCK_BIG(); + + return el->str; +} + +const char * +eina_share_common_ref(Eina_Share *share, const char *str) +{ + Eina_Share_Common_Node *node; + + if (!str) + return NULL; + + SHARE_COMMON_LOCK_BIG(); + node = _eina_share_common_node_from_str(str, share->node_magic); + node->references++; + DBG("str=%p refs=%u", str, node->references); + + SHARE_COMMON_UNLOCK_BIG(); + + eina_share_common_population_add(share, node->length); + + return str; +} + + +void +eina_share_common_del(Eina_Share *share, const char *str) +{ + unsigned int slen; + Eina_Share_Common_Head *ed; + Eina_Share_Common_Head **p_bucket; + Eina_Share_Common_Node *node; + int hash_num, hash; + + if (!str) + return; + + SHARE_COMMON_LOCK_BIG(); + + node = _eina_share_common_node_from_str(str, share->node_magic); + slen = node->length; + eina_share_common_population_del(share, slen); + if (node->references > 1) + { + node->references--; + DBG("str=%p refs=%u", str, node->references); + SHARE_COMMON_UNLOCK_BIG(); + return; + } + + DBG("str=%p refs=0, delete.", str); + node->references = 0; + + hash = eina_hash_superfast(str, slen); + hash_num = hash & 0xFF; + hash = (hash >> 8) & EINA_SHARE_COMMON_MASK; + + p_bucket = share->share->buckets + hash_num; + ed = _eina_share_common_find_hash(*p_bucket, hash); + if (!ed) + goto on_error; + + EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, SHARE_COMMON_UNLOCK_BIG()); + + if (!_eina_share_common_head_remove_node(ed, node)) + goto on_error; + + if (node != &ed->builtin_node) + MAGIC_FREE(node); + + if (!ed->head) + _eina_share_common_del_head(p_bucket, ed); + else + _eina_share_common_population_head_del(share, ed); + + SHARE_COMMON_UNLOCK_BIG(); + + return; + +on_error: + SHARE_COMMON_UNLOCK_BIG(); + /* possible segfault happened before here, but... */ + CRITICAL("EEEK trying to del non-shared share_common \"%s\"", str); +} + +int +eina_share_common_length(__UNUSED__ Eina_Share *share, const char *str) +{ + const Eina_Share_Common_Node *node; + + if (!str) + return -1; + + node = _eina_share_common_node_from_str(str, share->node_magic); + return node->length; +} + +void +eina_share_common_dump(Eina_Share *share, void (*additional_dump)( + struct dumpinfo *), int used) +{ + Eina_Iterator *it; + unsigned int i; + struct dumpinfo di; + + if (!share) + return; + + di.used = used; + di.saved = 0; + di.dups = 0; + di.unique = 0; + printf("DDD: len ref string\n"); + printf("DDD:-------------------\n"); + + SHARE_COMMON_LOCK_BIG(); + for (i = 0; i < EINA_SHARE_COMMON_BUCKETS; i++) + { + if (!share->share->buckets[i]) + { + continue; // printf("DDD: BUCKET # %i (HEAD=%i, NODE=%i)\n", i, + + } + +// sizeof(Eina_Share_Common_Head), sizeof(Eina_Share_Common_Node)); + it = eina_rbtree_iterator_prefix( + (Eina_Rbtree *)share->share->buckets[i]); + eina_iterator_foreach(it, EINA_EACH_CB(eina_iterator_array_check), &di); + eina_iterator_free(it); + } + if (additional_dump) + additional_dump(&di); + +#ifdef EINA_SHARE_COMMON_USAGE + /* One character strings are not counted in the hash. */ + di.saved += share->population_group[0].count * sizeof(char); + di.saved += share->population_group[1].count * sizeof(char) * 2; +#endif + printf("DDD:-------------------\n"); + printf("DDD: usage (bytes) = %i, saved = %i (%3.0f%%)\n", + di.used, di.saved, di.used ? (di.saved * 100.0 / di.used) : 0.0); + printf("DDD: unique: %d, duplicates: %d (%3.0f%%)\n", + di.unique, di.dups, di.unique ? (di.dups * 100.0 / di.unique) : 0.0); + +#ifdef EINA_SHARE_COMMON_USAGE + printf("DDD: Allocated strings: %i\n", share->population.count); + printf("DDD: Max allocated strings: %i\n", share->population.max); + + for (i = 0; + i < sizeof (share->population_group) / + sizeof (share->population_group[0]); + ++i) + fprintf(stderr, + "DDD: %i strings of length %i, max strings: %i\n", + share->population_group[i].count, + i, + share->population_group[i].max); +#endif + + SHARE_COMMON_UNLOCK_BIG(); +} + +/** + * @endcond + */ diff --git a/tests/suite/ecore/src/lib/eina_share_common.h b/tests/suite/ecore/src/lib/eina_share_common.h new file mode 100644 index 0000000000..002c65276c --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_share_common.h @@ -0,0 +1,103 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, Jorge Luis Zapata Muga, Cedric Bail + * + * 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 <http://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (C) 2008 Peter Wehrfritz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of the Software and its Copyright notices. In addition publicly + * documented acknowledgment must be given that this software has been used if no + * source code of this software is made available publicly. This includes + * acknowledgments in either Copyright notices, Manuals, Publicity and Marketing + * documents or any documentation provided with any product containing this + * software. This License does not apply to any software that links to the + * libraries provided by this software (statically or dynamically), but only to + * the software provided. + * + * Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice + * and it's intent. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef EINA_SHARE_COMMON_H_ +#define EINA_SHARE_COMMON_H_ + +#include "eina_types.h" +#include "eina_magic.h" + +typedef struct _Eina_Share Eina_Share; + +struct dumpinfo +{ + int used, saved, dups, unique; +}; + +Eina_Bool eina_share_common_init(Eina_Share **share, + Eina_Magic node_magic, + const char *node_magic_STR); +Eina_Bool eina_share_common_shutdown(Eina_Share **share); +const char *eina_share_common_add_length(Eina_Share *share, + const char *str, + unsigned int slen, + unsigned int null_size) +EINA_WARN_UNUSED_RESULT; +const char *eina_share_common_ref(Eina_Share *share, const char *str); +void eina_share_common_del(Eina_Share *share, const char *str); +int eina_share_common_length(Eina_Share *share, + const char *str) EINA_CONST +EINA_WARN_UNUSED_RESULT; +void eina_share_common_dump(Eina_Share *share, void (*additional_dump)( + struct dumpinfo *), int used); + + +/* Population functions */ +void eina_share_common_population_add(Eina_Share *share, int slen); +void eina_share_common_population_del(Eina_Share *share, int slen); + +/* Share logging */ +#ifdef CRITICAL +#undef CRITICAL +#endif +#define CRITICAL(...) EINA_LOG_DOM_CRIT(_eina_share_common_log_dom, __VA_ARGS__) + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_share_common_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_share_common_log_dom, __VA_ARGS__) +extern int _eina_share_common_log_dom; + +#endif /* EINA_STRINGSHARE_H_ */ diff --git a/tests/suite/ecore/src/lib/eina_str.c b/tests/suite/ecore/src/lib/eina_str.c new file mode 100644 index 0000000000..bd9badbc58 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_str.c @@ -0,0 +1,639 @@ +/* Leave the OpenBSD version below so we can track upstream fixes */ +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <ctype.h> + +#ifdef HAVE_ICONV +# include <errno.h> +# include <iconv.h> +#endif + +#include "eina_private.h" +#include "eina_str.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +/* + * Internal helper function used by eina_str_has_suffix() and + * eina_str_has_extension() + */ +static inline Eina_Bool +eina_str_has_suffix_helper(const char *str, + const char *suffix, + int (*cmp)(const char *, const char *)) +{ + size_t str_len; + size_t suffix_len; + + str_len = strlen(str); + suffix_len = eina_strlen_bounded(suffix, str_len); + if (suffix_len == (size_t)-1) + return EINA_FALSE; + + return cmp(str + str_len - suffix_len, suffix) == 0; +} + +static inline char ** +eina_str_split_full_helper(const char *str, + const char *delim, + int max_tokens, + unsigned int *elements) +{ + char *s, **str_array; + const char *src; + size_t len, dlen; + unsigned int tokens; + + dlen = strlen(delim); + if (dlen == 0) + { + if (elements) + *elements = 0; + + return NULL; + } + + tokens = 0; + src = str; + /* count tokens and check strlen(str) */ + while (*src != '\0') + { + const char *d = delim, *d_end = d + dlen; + const char *tmp = src; + for (; (d < d_end) && (*tmp != '\0'); d++, tmp++) + { + if (EINA_LIKELY(*d != *tmp)) + break; + } + if (EINA_UNLIKELY(d == d_end)) + { + src = tmp; + tokens++; + } + else + src++; + } + len = src - str; + + if ((max_tokens > 0) && (tokens > (unsigned int)max_tokens)) + tokens = max_tokens; + + str_array = malloc(sizeof(char *) * (tokens + 2)); + if (!str_array) + { + if (elements) + *elements = 0; + + return NULL; + } + + s = malloc(len + 1); + if (!s) + { + free(str_array); + if (elements) + *elements = 0; + + return NULL; + } + + /* copy tokens and string */ + tokens = 0; + str_array[0] = s; + src = str; + while (*src != '\0') + { + const char *d = delim, *d_end = d + dlen; + const char *tmp = src; + for (; (d < d_end) && (*tmp != '\0'); d++, tmp++) + { + if (EINA_LIKELY(*d != *tmp)) + break; + } + if (EINA_UNLIKELY(d == d_end)) + { + src = tmp; + *s = '\0'; + s += dlen; + tokens++; + str_array[tokens] = s; + } + else + { + *s = *src; + s++; + src++; + } + } + *s = '\0'; + str_array[tokens + 1] = NULL; + if (elements) + *elements = (tokens + 1); + + return str_array; +} + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_String_Group String + * + * @brief These functions provide useful C string management. + * + * @{ + */ + + +/** + * @brief Copy a c-string to another. + * + * @param dst The destination string. + * @param src The source string. + * @param siz The size of the destination string. + * @return The length of the source string. + * + * This function copies up to @p siz - 1 characters from the + * NUL-terminated string @p src to @p dst, NUL-terminating the result + * (unless @p siz is equal to 0). The returned value is the length of + * @p src. If the returned value is greater than @p siz, truncation + * occurred. + */ +EAPI size_t +eina_strlcpy(char *dst, const char *src, size_t siz) +{ +#ifdef HAVE_STRLCPY + return strlcpy(dst, src, siz); +#else + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) + while (--n != 0) + { + if ((*d++ = *s++) == '\0') + break; + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +#endif +} + +/** + * @brief Append a c-string. + * + * @param dst The destination string. + * @param src The source string. + * @param siz The size of the destination string. + * @return The length of the source string plus MIN(siz, strlen(initial dst)) + * + * This function appends @p src to @p dst of size @p siz (unlike + * strncat, @p siz is the full size of @p dst, not space left). At + * most @p siz - 1 characters will be copied. Always NUL terminates + * (unless @p siz <= strlen(dst)). This function returns strlen(src) + + * MIN(siz, strlen(initial dst)). If the returned value is greater or + * equal than @p siz, truncation occurred. + */ +EAPI size_t +eina_strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + + while (*s != '\0') { + if (n != 1) + { + *d++ = *s; + n--; + } + + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +/** + * @brief Check if the given string has the given prefix. + * + * @param str The string to work with. + * @param prefix The prefix to check for. + * @return #EINA_TRUE if the string has the given prefix, #EINA_FALSE otherwise. + * + * This function returns #EINA_TRUE if @p str has the prefix + * @p prefix, #EINA_FALSE otherwise. If the length of @p prefix is + * greater than @p str, #EINA_FALSE is returned. + */ +EAPI Eina_Bool +eina_str_has_prefix(const char *str, const char *prefix) +{ + size_t str_len; + size_t prefix_len; + + str_len = strlen(str); + prefix_len = eina_strlen_bounded(prefix, str_len); + if (prefix_len == (size_t)-1) + return EINA_FALSE; + + return (strncmp(str, prefix, prefix_len) == 0); +} + +/** + * @brief Check if the given string has the given suffix. + * + * @param str The string to work with. + * @param suffix The suffix to check for. + * @return #EINA_TRUE if the string has the given suffix, #EINA_FALSE otherwise. + * + * This function returns #EINA_TRUE if @p str has the suffix + * @p suffix, #EINA_FALSE otherwise. If the length of @p suffix is + * greater than @p str, #EINA_FALSE is returned. + */ +/** + * @param str the string to work with + * @param suffix the suffix to check for + * @return true if str has the given suffix + * @brief checks if the string has the given suffix + */ +EAPI Eina_Bool +eina_str_has_suffix(const char *str, const char *suffix) +{ + return eina_str_has_suffix_helper(str, suffix, strcmp); +} + +/** + * @brief Check if the given string has the given suffix. + * + * @param str The string to work with. + * @param ext The extension to check for. + * @return #EINA_TRUE if the string has the given extension, #EINA_FALSE otherwise. + * + * This function does the same like eina_str_has_suffix(), but with a + * case insensitive compare. + */ +EAPI Eina_Bool +eina_str_has_extension(const char *str, const char *ext) +{ + return eina_str_has_suffix_helper(str, ext, strcasecmp); +} + +/** + * @brief Split a string using a delimiter and returns number of elements. + * + * @param str The string to split. + * @param delim The string which specifies the places at which to split the string. + * @param max_tokens The maximum number of strings to split string into. + * @param elements Where to return the number of elements in returned + * array (not counting the terminating @c NULL). May be @c NULL. + * @return A newly-allocated NULL-terminated array of strings or NULL if it + * fails to allocate the array. + * + * This functin splits @p str into a maximum of @p max_tokens pieces, + * using the given delimiter @p delim. @p delim is not included in any + * of the resulting strings, unless @p max_tokens is reached. If + * @p max_tokens is less than @c 1, the string is splitted completely. If + * @p max_tokens is reached, the last string in the returned string + * array contains the remainder of string. The returned value is a + * newly allocated NULL-terminated array of strings or NULL if it fails to + * allocate the array. To free it, free the first element of the array and the + * array itself. + * + * @see eina_str_split() + */ +EAPI char ** +eina_str_split_full(const char *str, + const char *delim, + int max_tokens, + unsigned int *elements) +{ + return eina_str_split_full_helper(str, delim, max_tokens, elements); +} + + +/** + * @brief Split a string using a delimiter. + * + * @param str The string to split. + * @param delim The string which specifies the places at which to split the string. + * @param max_tokens The maximum number of strings to split string into. + * @return A newly-allocated NULL-terminated array of strings or NULL if it + * fails to allocate the array. + * + * This functin splits @p str into a maximum of @p max_tokens pieces, + * using the given delimiter @p delim. @p delim is not included in any + * of the resulting strings, unless @p max_tokens is reached. If + * @p max_tokens is less than @c 1, the string is splitted completely. If + * @p max_tokens is reached, the last string in the returned string + * array contains the remainder of string. The returned value is a + * newly allocated NULL-terminated array of strings or NULL if it fails to + * allocate the array. To free it, free the first element of the array and the + * array itself. + */ +EAPI char ** +eina_str_split(const char *str, const char *delim, int max_tokens) +{ + return eina_str_split_full_helper(str, delim, max_tokens, NULL); +} + +/** + * @brief Join two strings of known length. + * + * @param dst The buffer to store the result. + * @param size Size (in byte) of the buffer. + * @param sep The separator character to use. + * @param a First string to use, before @p sep. + * @param a_len length of @p a. + * @param b Second string to use, after @p sep. + * @param b_len length of @p b. + * @return The number of characters printed. + * + * This function joins the strings @p a and @p b (in that order) and + * separate them with @p sep. The result is stored in the buffer + * @p dst and at most @p size - 1 characters will be written and the + * string is NULL-terminated. @p a_len is the length of @p a (not + * including '\\0') and @p b_len is the length of @p b (not including + * '\\0'). This function returns the number of characters printed (not + * including the trailing '\\0' used to end output to strings). Just + * like snprintf(), it will not write more than @p size bytes, thus a + * returned value of @p size or more means that the output was + * truncated. + * + * @see eina_str_join() + * @see eina_str_join_static() + */ +EAPI size_t +eina_str_join_len(char *dst, + size_t size, + char sep, + const char *a, + size_t a_len, + const char *b, + size_t b_len) +{ + size_t ret = a_len + b_len + 1; + size_t off; + + if (size < 1) + return ret; + + if (size <= a_len) + { + memcpy(dst, a, size - 1); + dst[size - 1] = '\0'; + return ret; + } + + memcpy(dst, a, a_len); + off = a_len; + + if (size <= off + 1) + { + dst[size - 1] = '\0'; + return ret; + } + + dst[off] = sep; + off++; + + if (size <= off + b_len + 1) + { + memcpy(dst + off, b, size - off - 1); + dst[size - 1] = '\0'; + return ret; + } + + memcpy(dst + off, b, b_len); + dst[off + b_len] = '\0'; + return ret; +} + +/** + * @brief Use iconv to convert a text string from one encoding to another + * + * @param enc_from encoding to convert from + * @param enc_to encoding to convert to + * @param text text to convert + * + */ +#ifdef HAVE_ICONV +EAPI char * +eina_str_convert(const char *enc_from, const char *enc_to, const char *text) +{ + iconv_t ic; + char *new_txt, *inp, *outp; + size_t inb, outb, outlen, tob, outalloc; + + if (!text) + return NULL; + + ic = iconv_open(enc_to, enc_from); + if (ic == (iconv_t)(-1)) + return NULL; + + new_txt = malloc(64); + inb = strlen(text); + outb = 64; + inp = (char *)text; + outp = new_txt; + outalloc = 64; + outlen = 0; + + for (;; ) + { + size_t count; + + tob = outb; + count = iconv(ic, &inp, &inb, &outp, &outb); + outlen += tob - outb; + if (count == (size_t)(-1)) + { + if (errno == E2BIG) + { + new_txt = realloc(new_txt, outalloc + 64); + outp = new_txt + outlen; + outalloc += 64; + outb += 64; + } + else if (errno == EILSEQ) + { + if (new_txt) + free(new_txt); + + new_txt = NULL; + break; + } + else if (errno == EINVAL) + { + if (new_txt) + free(new_txt); + + new_txt = NULL; + break; + } + else + { + if (new_txt) + free(new_txt); + + new_txt = NULL; + break; + } + } + + if (inb == 0) + { + if (outalloc == outlen) + new_txt = realloc(new_txt, outalloc + 1); + + new_txt[outlen] = 0; + break; + } + } + iconv_close(ic); + return new_txt; +} +#else +EAPI char * +eina_str_convert(const char *enc_from __UNUSED__, + const char *enc_to __UNUSED__, + const char *text __UNUSED__) +{ + return NULL; +} +#endif + +/** + * @brief Put a \ before and Space( ), \ or ' in a string. + * + * @param str the string to escape + * + * A newly allocated string is returned. + */ +EAPI char * +eina_str_escape(const char *str) +{ + char *s2, *d; + const char *s; + + s2 = malloc((strlen(str) * 2) + 1); + if (!s2) + return NULL; + + for (s = str, d = s2; *s != 0; s++, d++) + { + if ((*s == ' ') || (*s == '\\') || (*s == '\'')) + { + *d = '\\'; + d++; + } + + *d = *s; + } + *d = 0; + return s2; +} + +/** + * @brief Lowercase all the characters in range [A-Z] in the given string. + * + * @param str the string to lowercase + * + * This modifies the original string, changing all characters in [A-Z] to lowercase. + */ +EAPI void +eina_str_tolower(char **str) +{ + char *p; + if ((!str) || (!(*str))) + return; + + for (p = *str; (*p); p++) + *p = tolower((unsigned char )(*p)); +} + +/** + * @brief Uppercase all the characters in range [a-z] in the given string. + * + * @param str the string to uppercase + * + * This modifies the original string, changing all characters in [a-z] to uppercase. + */ +EAPI void +eina_str_toupper(char **str) +{ + char *p; + if ((!str) || (!(*str))) + return; + + for (p = *str; (*p); p++) + *p = toupper((unsigned char)(*p)); +} + + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_strbuf.c b/tests/suite/ecore/src/lib/eina_strbuf.c new file mode 100644 index 0000000000..3968af56ae --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_strbuf.c @@ -0,0 +1,180 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define _GNU_SOURCE + +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_private.h" +#include "eina_str.h" +#include "eina_strbuf_common.h" +#include "eina_unicode.h" + +/*============================================================================* + * Local * + *============================================================================*/ + +/** + * @cond LOCAL + */ + +#ifdef _STRBUF_DATA_TYPE +# undef _STRBUF_DATA_TYPE +#endif + +#ifdef _STRBUF_CSIZE +# undef _STRBUF_CSIZE +#endif + +#ifdef _STRBUF_STRUCT_NAME +# undef _STRBUF_STRUCT_NAME +#endif + +#ifdef _STRBUF_STRLEN_FUNC +# undef _STRBUF_STRLEN_FUNC +#endif + +#ifdef _STRBUF_STRESCAPE_FUNC +# undef _STRBUF_STRESCAPE_FUNC +#endif + +#ifdef _STRBUF_MAGIC +# undef _STRBUF_MAGIC +#endif + +#ifdef _STRBUF_MAGIC_STR +# undef _STRBUF_MAGIC_STR +#endif + +#ifdef _FUNC_EXPAND +# undef _FUNC_EXPAND +#endif + + +#define _STRBUF_DATA_TYPE char +#define _STRBUF_CSIZE sizeof(_STRBUF_DATA_TYPE) +#define _STRBUF_STRUCT_NAME Eina_Strbuf +#define _STRBUF_STRLEN_FUNC(x) strlen(x) +#define _STRBUF_STRESCAPE_FUNC(x) eina_str_escape(x) +#define _STRBUF_MAGIC EINA_MAGIC_STRBUF +#define _STRBUF_MAGIC_STR __STRBUF_MAGIC_STR +static const char __STRBUF_MAGIC_STR[] = "Eina Strbuf"; + +#define _FUNC_EXPAND(y) eina_strbuf_ ## y + +/** + * @endcond + */ + + +/*============================================================================* + * Global * + *============================================================================*/ + + +/*============================================================================* + * API * + *============================================================================*/ + + +/** + * @addtogroup Eina_String_Buffer_Group String Buffer + * + * @brief These functions provide string buffers management. + * + * The String Buffer data type is designed to be a mutable string, + * allowing to append, prepend or insert a string to a buffer. + * + * @{ + */ + +EAPI Eina_Bool +eina_strbuf_append_printf(Eina_Strbuf *buf, const char *fmt, ...) +{ + va_list args; + char *str; + size_t len; + Eina_Bool ret; + + va_start(args, fmt); + len = vasprintf(&str, fmt, args); + va_end(args); + + if (len <= 0 || !str) + return EINA_FALSE; + + ret = eina_strbuf_append_length(buf, str, len); + free(str); + return ret; +} + +EAPI Eina_Bool +eina_strbuf_append_vprintf(Eina_Strbuf *buf, const char *fmt, va_list args) +{ + char *str; + size_t len; + Eina_Bool ret; + + len = vasprintf(&str, fmt, args); + + if (len <= 0 || !str) + return EINA_FALSE; + + ret = eina_strbuf_append_length(buf, str, len); + free(str); + return ret; +} + +EAPI Eina_Bool +eina_strbuf_insert_printf(Eina_Strbuf *buf, const char *fmt, size_t pos, ...) +{ + va_list args; + char *str; + size_t len; + Eina_Bool ret; + + va_start(args, pos); + len = vasprintf(&str, fmt, args); + va_end(args); + + if (len <= 0 || !str) + return EINA_FALSE; + + ret = eina_strbuf_insert(buf, str, pos); + free(str); + return ret; +} + +EAPI Eina_Bool +eina_strbuf_insert_vprintf(Eina_Strbuf *buf, + const char *fmt, + size_t pos, + va_list args) +{ + char *str; + size_t len; + Eina_Bool ret; + + len = vasprintf(&str, fmt, args); + + if (len <= 0 || !str) + return EINA_FALSE; + + ret = eina_strbuf_insert(buf, str, pos); + free(str); + return ret; +} + +/* Unicode */ + +#include "eina_strbuf_template_c.x" + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_strbuf_common.c b/tests/suite/ecore/src/lib/eina_strbuf_common.c new file mode 100644 index 0000000000..c96e70189d --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_strbuf_common.c @@ -0,0 +1,862 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 +# include <Evil.h> +#endif + +#include "eina_private.h" +#include "eina_str.h" +#include "eina_magic.h" +#include "eina_error.h" +#include "eina_safety_checks.h" +#include "eina_strbuf.h" +#include "eina_strbuf_common.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/** + * @cond LOCAL + */ + +#define EINA_STRBUF_INIT_SIZE 32 +#define EINA_STRBUF_INIT_STEP 32 +#define EINA_STRBUF_MAX_STEP 4096 + +/** + * @endcond + */ + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the strbuf module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the strbuf module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_strbuf_common_init(void) +{ + return EINA_TRUE; +} + +/** + * @internal + * @brief Shut down the strbuf module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the strbuf module set up by + * eina_strbuf_common_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_strbuf_common_shutdown(void) +{ + return EINA_TRUE; +} + +/** + * @internal + * + * init the buffer + * @param buf the buffer to init + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +static Eina_Bool +_eina_strbuf_common_init(size_t csize, Eina_Strbuf *buf) +{ + buf->len = 0; + buf->size = EINA_STRBUF_INIT_SIZE; + buf->step = EINA_STRBUF_INIT_STEP; + + eina_error_set(0); + buf->buf = calloc(csize, buf->size); + if (EINA_UNLIKELY(!buf->buf)) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +/** + * @internal + * + * resize the buffer + * @param buf the buffer to resize + * @param size the minimum size of the buffer + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +static inline Eina_Bool +_eina_strbuf_common_resize(size_t csize, Eina_Strbuf *buf, size_t size) +{ + size_t new_size, new_step, delta; + void *buffer; + + size += 1; // Add extra space for '\0' + + if (size == buf->size) + /* nothing to do */ + return EINA_TRUE; + else if (size > buf->size) + delta = size - buf->size; + else + delta = buf->size - size; + + /* check if should keep the same step (just used while growing) */ + if ((delta <= buf->step) && (size > buf->size)) + new_step = buf->step; + else + { + new_step = (((delta / EINA_STRBUF_INIT_STEP) + 1) + * EINA_STRBUF_INIT_STEP); + + if (new_step > EINA_STRBUF_MAX_STEP) + new_step = EINA_STRBUF_MAX_STEP; + } + + new_size = (((size / new_step) + 1) * new_step); + + /* reallocate the buffer to the new size */ + buffer = realloc(buf->buf, new_size * csize); + if (EINA_UNLIKELY(!buffer)) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return EINA_FALSE; + } + + buf->buf = buffer; + buf->size = new_size; + buf->step = new_step; + eina_error_set(0); + return EINA_TRUE; +} + +/** + * @internal + * + * If required, enlarge the buffer to fit the new size. + * + * @param buf the buffer to resize + * @param size the minimum size of the buffer + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +Eina_Bool +_eina_strbuf_common_grow(size_t csize, Eina_Strbuf *buf, size_t size) +{ + if ((size + 1) < buf->size) + return EINA_TRUE; + + return _eina_strbuf_common_resize(csize, buf, size); +} + +/** + * @internal + * + * insert string of known length at random within existing strbuf limits. + * + * @param buf the buffer to resize, must be valid. + * @param str the string to copy, must be valid (!NULL and smaller than @a len) + * @param len the amount of bytes in @a str to copy, must be valid. + * @param pos the position inside buffer to insert, must be valid (smaller + * than eina_strbuf_common_length_get()) + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +static inline Eina_Bool +_eina_strbuf_common_insert_length(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len, + size_t pos) +{ + if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len))) + return EINA_FALSE; + + /* move the existing text */ + memmove(buf->buf + ((len + pos) * csize), buf->buf + (pos * csize), + (buf->len - pos) * csize); + + /* and now insert the given string */ + memcpy(buf->buf + (pos * csize), str, len * csize); + + buf->len += len; + memset(buf->buf + (buf->len * csize), 0, csize); + return EINA_TRUE; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @brief Create a new string buffer. + * + * @return Newly allocated string buffer instance. + * + * This function creates a new string buffer. On error, @c NULL is + * returned and Eina error is set to #EINA_ERROR_OUT_OF_MEMORY. To + * free the resources, use eina_strbuf_common_free(). + * + * @see eina_strbuf_common_free() + * @see eina_strbuf_common_append() + * @see eina_strbuf_common_string_get() + */ +Eina_Strbuf * +eina_strbuf_common_new(size_t csize) +{ + Eina_Strbuf *buf; + + eina_error_set(0); + buf = malloc(sizeof(Eina_Strbuf)); + if (EINA_UNLIKELY(!buf)) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + if (EINA_UNLIKELY(!_eina_strbuf_common_init(csize, buf))) + { + eina_strbuf_common_free(buf); + return NULL; + } + + return buf; +} + +/** + * @brief Free a string buffer. + * + * @param buf The string buffer to free. + * + * This function frees the memory of @p buf. @p buf must have been + * created by eina_strbuf_common_new(). + */ +void +eina_strbuf_common_free(Eina_Strbuf *buf) +{ + free(buf->buf); + free(buf); +} + +/** + * @brief Reset a string buffer. + * + * @param buf The string buffer to reset. + * + * This function reset @p buf: the buffer len is set to 0, and the + * string is set to '\\0'. No memory is free'd. + */ +void +eina_strbuf_common_reset(size_t csize, Eina_Strbuf *buf) +{ + buf->len = 0; + buf->step = EINA_STRBUF_INIT_STEP; + + memset(buf->buf, 0, csize); +} + +/** + * @brief Append a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends @p str to @p buf. It computes the length of + * @p str, so is slightly slower than eina_strbuf_common_append_length(). If + * the length is known beforehand, consider using that variant. If + * @p buf can't append it, #EINA_FALSE is returned, otherwise + * #EINA_TRUE is returned. + * + * @see eina_strbuf_common_append() + * @see eina_strbuf_common_append_length() + */ +Eina_Bool +eina_strbuf_common_append(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len) +{ + + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + + if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len))) + return EINA_FALSE; + + memcpy(buf->buf + (buf->len * csize), str, (len + 1) * csize); + buf->len += len; + return EINA_TRUE; +} + +/** + * @brief Append a string to a buffer, reallocating as necessary, + * limited by the given length. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @param maxlen The maximum number of characters to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends at most @p maxlen characters of @p str to + * @p buf. It can't appends more than the length of @p str. It + * computes the length of @p str, so is slightly slower than + * eina_strbuf_common_append_length(). If the length is known beforehand, + * consider using that variant (@p maxlen should then be checked so + * that it is greater than the size of @p str). If @p str can not be + * appended, #EINA_FALSE is returned, otherwise, #EINA_TRUE is + * returned. + * + * @see eina_strbuf_common_append() + * @see eina_strbuf_common_append_length() + */ +Eina_Bool +eina_strbuf_common_append_n(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len, + size_t maxlen) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + + if (len > maxlen) + len = maxlen; + + if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len))) + return EINA_FALSE; + + memcpy(buf->buf + (buf->len * csize), str, len * csize); + buf->len += len; + memset(buf->buf + (buf->len * csize), 0, csize); + return EINA_TRUE; +} + +/** + * @brief Append a string of exact length to a buffer, reallocating as necessary. + * + * @param buf The string buffer to append to. + * @param str The string to append. + * @param length The exact length to use. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function appends @p str to @p buf. @p str must be of size at + * most @p length. It is slightly faster than eina_strbuf_common_append() as + * it does not compute the size of @p str. It is useful when dealing + * with strings of known size, such as eina_strngshare. If @p buf + * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + * + * @see eina_stringshare_length() + * @see eina_strbuf_common_append() + * @see eina_strbuf_common_append_n() + */ +Eina_Bool +eina_strbuf_common_append_length(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t length) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + + if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + length))) + return EINA_FALSE; + + memcpy(buf->buf + (buf->len * csize), str, length * csize); + buf->len += length; + memset(buf->buf + (buf->len * csize), 0, csize); + return EINA_TRUE; +} + +/** + * @brief Insert a string to a buffer, reallocating as necessary. + * + * @param buf The string buffer to insert. + * @param str The string to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str to @p buf at position @p pos. It + * computes the length of @p str, so is slightly slower than + * eina_strbuf_common_insert_length(). If the length is known beforehand, + * consider using that variant. If @p buf can't insert it, #EINA_FALSE + * is returned, otherwise #EINA_TRUE is returned. + */ +Eina_Bool +eina_strbuf_common_insert(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len, + size_t pos) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + + if (pos >= buf->len) + return eina_strbuf_common_append(csize, buf, str, len); + + return _eina_strbuf_common_insert_length(csize, buf, str, len, pos); +} + +/** + * @brief Insert a string to a buffer, reallocating as necessary. Limited by maxlen. + * + * @param buf The string buffer to insert to. + * @param str The string to insert. + * @param maxlen The maximum number of chars to insert. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str ot @p buf at position @p pos, with at + * most @p maxlen bytes. The number of inserted characters can not be + * greater than the length of @p str. It computes the length of + * @p str, so is slightly slower than eina_strbuf_common_insert_length(). If the + * length is known beforehand, consider using that variant (@p maxlen + * should then be checked so that it is greater than the size of + * @p str). If @p str can not be inserted, #EINA_FALSE is returned, + * otherwise, #EINA_TRUE is returned. + */ +Eina_Bool +eina_strbuf_common_insert_n(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len, + size_t maxlen, + size_t pos) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + + if (pos >= buf->len) + return eina_strbuf_common_append_n(csize, buf, str, len, maxlen); + + if (len > maxlen) + len = maxlen; + + return _eina_strbuf_common_insert_length(csize, buf, str, len, pos); +} + +/** + * @brief Insert a string of exact length to a buffer, reallocating as necessary. + * + * @param buf The string buffer to insert to. + * @param str The string to insert. + * @param length The exact length to use. + * @param pos The position to insert the string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p str to @p buf. @p str must be of size at + * most @p length. It is slightly faster than eina_strbuf_common_insert() as + * it does not compute the size of @p str. It is useful when dealing + * with strings of known size, such as eina_strngshare. If @p buf + * can't insert it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + * + * @see eina_stringshare_length() + * @see eina_strbuf_common_insert() + * @see eina_strbuf_common_insert_n() + */ +Eina_Bool +eina_strbuf_common_insert_length(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t length, + size_t pos) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + + if (pos >= buf->len) + return eina_strbuf_common_append_length(csize, buf, str, length); + + return _eina_strbuf_common_insert_length(csize, buf, str, length, pos); +} + +/** + * @brief Append a character to a string buffer, reallocating as + * necessary. + * + * @param buf The string buffer to append to. + * @param c The char to append. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p c to @p buf. If it can not insert it, + * #EINA_FALSE is returned, otherwise #EINA_TRUE is returned. + */ +Eina_Bool +eina_strbuf_common_append_char(size_t csize, Eina_Strbuf *buf, const void *c) +{ + + if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + 1))) + return EINA_FALSE; + + memcpy(buf->buf + ((buf->len)++ *csize), c, csize); + memset(buf->buf + (buf->len * csize), 0, csize); + return EINA_TRUE; +} + +/** + * @brief Insert a character to a string buffer, reallocating as + * necessary. + * + * @param buf The string buffer to insert to. + * @param c The char to insert. + * @param pos The position to insert the char. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function inserts @p c to @p buf at position @p pos. If @p buf + * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is + * returned. + */ +Eina_Bool +eina_strbuf_common_insert_char(size_t csize, + Eina_Strbuf *buf, + const void *c, + size_t pos) +{ + + if (pos >= buf->len) + return eina_strbuf_common_append_char(csize, buf, c); + + return _eina_strbuf_common_insert_length(csize, buf, c, 1, pos); +} + +/** + * @brief Remove a slice of the given string buffer. + * + * @param buf The string buffer to remove a slice. + * @param start The initial (inclusive) slice position to start + * removing, in bytes. + * @param end The final (non-inclusive) slice position to finish + * removing, in bytes. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function removes a slice of @p buf, starting at @p start + * (inclusive) and ending at @p end (non-inclusive). Both values are + * in bytes. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise. + */ +Eina_Bool +eina_strbuf_common_remove(size_t csize, + Eina_Strbuf *buf, + size_t start, + size_t end) +{ + size_t remove_len, tail_len; + + if (end >= buf->len) + end = buf->len; + + if (end <= start) + return EINA_TRUE; + + remove_len = end - start; + if (remove_len == buf->len) + { + free(buf->buf); + return _eina_strbuf_common_init(csize, buf); + } + + tail_len = buf->len - end + 1; /* includes '\0' */ + memmove(buf->buf + (start * csize), + buf->buf + (end * csize), + tail_len * csize); + buf->len -= remove_len; + return _eina_strbuf_common_resize(csize, buf, buf->len); +} + +/** + * @brief Retrieve a pointer to the contents of a string buffer + * + * @param buf The string buffer. + * @return The current string in the string buffer. + * + * This function returns the string contained in @p buf. The returned + * value must not be modified and will no longer be valid if @p buf is + * modified. In other words, any eina_strbuf_common_append() or similar will + * make that pointer invalid. + * + * @see eina_strbuf_common_string_steal() + */ +const void * +eina_strbuf_common_string_get(const Eina_Strbuf *buf) +{ + return buf->buf; +} + +/** + * @brief Steal the contents of a string buffer. + * + * @param buf The string buffer to steal. + * @return The current string in the string buffer. + * + * This function returns the string contained in @p buf. @p buf is + * then initialized and does not own the returned string anymore. The + * caller must release the memory of the returned string by calling + * free(). + * + * @see eina_strbuf_common_string_get() + */ +void * +eina_strbuf_common_string_steal(size_t csize, Eina_Strbuf *buf) +{ + void *ret; + + ret = buf->buf; + // TODO: Check return value and do something clever + _eina_strbuf_common_init(csize, buf); + return ret; +} + +/** + * @brief Free the contents of a string buffer but not the buffer. + * + * @param buf The string buffer to free the string of. + * + * This function frees the string contained in @p buf without freeing + * @p buf. + */ +void +eina_strbuf_common_string_free(size_t csize, Eina_Strbuf *buf) +{ + free(buf->buf); + _eina_strbuf_common_init(csize, buf); +} + +/** + * @brief Retrieve the length of the string buffer content. + * + * @param buf The string buffer. + * @return The current length of the string, in bytes. + * + * This function returns the length of @p buf. + */ +size_t +eina_strbuf_common_length_get(const Eina_Strbuf *buf) +{ + return buf->len; +} + +/** + * @addtogroup Eina_String_Buffer_Group String Buffer + * + * @brief These functions provide string buffers management. + * + * The String Buffer data type is designed to be a mutable string, + * allowing to append, prepend or insert a string to a buffer. + * + * @{ + */ + + +/** + * @cond LOCAL + */ + +/*FIXME: Implementing them here is a hack! */ + +#ifdef _STRBUF_CSIZE +# undef _STRBUF_CSIZE +#endif + +#ifdef _STRBUF_MAGIC +# undef _STRBUF_MAGIC +#endif + +#ifdef _STRBUF_MAGIC_STR +# undef _STRBUF_MAGIC_STR +#endif + +#define _STRBUF_CSIZE 1 +#define _STRBUF_MAGIC EINA_MAGIC_STRBUF +#define _STRBUF_MAGIC_STR __STRBUF_STR_MAGIC_STR +static const char __STRBUF_STR_MAGIC_STR[] = "Eina Strbuf"; + + +/** + * @endcond + */ + + +/** + * @brief Replace the n-th string with an other string. + * + * @param buf The string buffer to work with. + * @param str The string to replace. + * @param with The replaceing string. + * @param n The number of the fitting string. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function replaces the n-th occurrence of @p str in @p buf with + * @p with. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise. + */ +EAPI Eina_Bool +eina_strbuf_replace(Eina_Strbuf *buf, + const char *str, + const char *with, + unsigned int n) +{ + size_t len1, len2; + char *spos; + size_t pos; + + EINA_SAFETY_ON_NULL_RETURN_VAL( str, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(with, EINA_FALSE); + EINA_MAGIC_CHECK_STRBUF(buf, 0); + + if (n == 0) + return EINA_FALSE; + + spos = buf->buf; + while (n--) + { + spos = strstr(spos, str); + if (!spos || *spos == '\0') + return EINA_FALSE; + + if (n) + spos++; + } + + pos = spos - (const char *)buf->buf; + len1 = strlen(str); + len2 = strlen(with); + if (len1 != len2) + { + /* resize the buffer if necessary */ + if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf, + buf->len - len1 + len2))) + { + return EINA_FALSE; /* move the existing text */ + + } + + memmove(buf->buf + pos + len2, buf->buf + pos + len1, + buf->len - pos - len1); + } + + /* and now insert the given string */ + memcpy(buf->buf + pos, with, len2); + buf->len += len2 - len1; + memset((char *)buf->buf + buf->len, 0, 1); + + return EINA_TRUE; +} + +/** + * @brief Replace all strings with an other string. + + * @param buf the string buffer to work with. + * @param str The string to replace. + * @param with The replaceing string. + * @return How often the string was replaced. + * + * This function replaces all the occurrences of @p str in @ buf with + * the string @p with. This function returns the number of times @p str + * has been replaced. On failure, it returns 0. + */ +EAPI int +eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, const char *with) +{ + size_t len1, len2, len; + char *tmp_buf = NULL; + char *spos; + size_t pos, start; + size_t pos_tmp, start_tmp; + int n = 0; + + EINA_SAFETY_ON_NULL_RETURN_VAL( str, 0); + EINA_SAFETY_ON_NULL_RETURN_VAL(with, 0); + EINA_MAGIC_CHECK_STRBUF(buf, 0); + + spos = strstr(buf->buf, str); + if (!spos || *spos == '\0') + return 0; + + len1 = strlen(str); + len2 = strlen(with); + + /* if the size of the two string is equal, it is fairly easy to replace them + * we don't need to resize the buffer or doing other calculations */ + if (len1 == len2) + { + while (spos) + { + memcpy(spos, with, len2); + spos = strstr(spos + len2, str); + n++; + } + return n; + } + + pos = pos_tmp = spos - (const char *)buf->buf; + tmp_buf = buf->buf; + buf->buf = malloc(buf->size); + if (EINA_UNLIKELY(!buf->buf)) + { + buf->buf = tmp_buf; + return 0; + } + + start = start_tmp = 0; + len = buf->len; + + while (spos) + { + n++; + len = (len + len2) - len1; + /* resize the buffer if necessary */ + if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf, len))) + { + /* we have to stop replacing here, because we haven't enough + * memory to go on */ + len = (len + len1) - len2; + break; + } + + /* copy the untouched text */ + memcpy(buf->buf + start, tmp_buf + start_tmp, pos - start); + /* copy the new string */ + memcpy(buf->buf + pos, with, len2); + + /* calculate the next positions */ + start_tmp = pos_tmp + len1; + start = pos + len2; + spos = strstr(tmp_buf + start_tmp, str); + /* this calculations don't make sense if spos == NULL, but the + * calculated values won't be used, because the loop will stop + * then */ + pos_tmp = spos - tmp_buf; + pos = start + pos_tmp - start_tmp; + } + /* and now copy the rest of the text */ + memcpy(buf->buf + start, tmp_buf + start_tmp, len - start); + buf->len = len; + memset((char *)buf->buf + buf->len, 0, 1); + + free(tmp_buf); + + return n; +} + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_strbuf_common.h b/tests/suite/ecore/src/lib/eina_strbuf_common.h new file mode 100644 index 0000000000..3713f2c48a --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_strbuf_common.h @@ -0,0 +1,112 @@ +#ifndef EINA_STRBUF_COMMON_H +#define EINA_STRBUF_COMMON_H + +#include <stdlib.h> + +#include "eina_private.h" +#include "eina_magic.h" +#include "eina_strbuf.h" + +struct _Eina_Strbuf +{ + void *buf; + size_t len; + size_t size; + size_t step; + + EINA_MAGIC +}; + +#define EINA_MAGIC_CHECK_STRBUF(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK((d), _STRBUF_MAGIC)) \ + { \ + EINA_MAGIC_FAIL((d), _STRBUF_MAGIC); \ + return __VA_ARGS__; \ + } \ + } while (0) + +Eina_Bool +eina_strbuf_common_init(void); + +Eina_Bool +eina_strbuf_common_shutdown(void); +Eina_Strbuf * +eina_strbuf_common_new(size_t csize); +void +eina_strbuf_common_free(Eina_Strbuf *buf); +void +eina_strbuf_common_reset(size_t csize, Eina_Strbuf *buf); +Eina_Bool +eina_strbuf_common_append(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len); +Eina_Bool +eina_strbuf_common_append_escaped(size_t csize, + Eina_Strbuf *buf, + const void *str); +Eina_Bool +eina_strbuf_common_append_n(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len, + size_t maxlen); +Eina_Bool +eina_strbuf_common_append_length(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t length); +Eina_Bool +eina_strbuf_common_insert(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len, + size_t pos); +Eina_Bool +eina_strbuf_common_insert_escaped(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len, + size_t pos); +Eina_Bool +eina_strbuf_common_insert_n(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t len, + size_t maxlen, + size_t pos); +Eina_Bool +eina_strbuf_common_insert_length(size_t csize, + Eina_Strbuf *buf, + const void *str, + size_t length, + size_t pos); +Eina_Bool +eina_strbuf_common_append_char(size_t csize, Eina_Strbuf *buf, const void *c); +Eina_Bool +eina_strbuf_common_insert_char(size_t csize, + Eina_Strbuf *buf, + const void *c, + size_t pos); +Eina_Bool +eina_strbuf_common_remove(size_t csize, + Eina_Strbuf *buf, + size_t start, + size_t end); +const void * +eina_strbuf_common_string_get(const Eina_Strbuf *buf); +void * +eina_strbuf_common_string_steal(size_t csize, Eina_Strbuf *buf); +void +eina_strbuf_common_string_free(size_t csize, Eina_Strbuf *buf); +size_t +eina_strbuf_common_length_get(const Eina_Strbuf *buf); + +Eina_Bool +_eina_strbuf_common_grow(size_t csize, Eina_Strbuf *buf, size_t size); +/** + * @} + */ + +#endif diff --git a/tests/suite/ecore/src/lib/eina_strbuf_template_c.x b/tests/suite/ecore/src/lib/eina_strbuf_template_c.x new file mode 100644 index 0000000000..2f3aeae4b4 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_strbuf_template_c.x @@ -0,0 +1,216 @@ +/* + * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 + */ + +/* This file should be included from files implementing strbuf. The including file + * should define the following macros: + * _STRBUF_DATA_TYPE + * _STRBUF_CSIZE + * _STRBUF_STRUCT_NAME + * _STRBUF_STRLEN_FUNC(x) + * _STRBUF_STRESCAPE_FUNC(x) + * _STRBUF_STRSTR_FUNC(x, y) + * _STRBUF_MAGIC + * _STRBUF_MAGIC_STR + * See how it's done in eina_ustrbuf.c and eina_strbuf.c. This just makes things + * a lot easier since those are essentially the same just with different sizes. + */ + +/*============================================================================* + * Local * + *============================================================================*/ + + +/*============================================================================* + * Global * + *============================================================================*/ + +/** + * @internal + * @brief Initialize the strbuf module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the strbuf module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +_FUNC_EXPAND(init)(void) +{ + eina_magic_string_static_set(_STRBUF_MAGIC, _STRBUF_MAGIC_STR); + return eina_strbuf_common_init(); +} + +/** + * @internal + * @brief Shut down the strbuf module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the strbuf module set up by + * eina_ustrbuf_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +_FUNC_EXPAND(shutdown)(void) +{ + return eina_strbuf_common_shutdown(); +} + +/*============================================================================* + * API * + *============================================================================*/ + +EAPI _STRBUF_STRUCT_NAME * +_FUNC_EXPAND(new)(void) +{ + _STRBUF_STRUCT_NAME *buf = eina_strbuf_common_new(_STRBUF_CSIZE); + EINA_MAGIC_SET(buf, _STRBUF_MAGIC); + return buf; +} + +EAPI void +_FUNC_EXPAND(free)(_STRBUF_STRUCT_NAME *buf) +{ + EINA_MAGIC_CHECK_STRBUF(buf); + EINA_MAGIC_SET(buf, EINA_MAGIC_NONE); + eina_strbuf_common_free(buf); +} + +EAPI void +_FUNC_EXPAND(reset)(_STRBUF_STRUCT_NAME *buf) +{ + EINA_MAGIC_CHECK_STRBUF(buf); + eina_strbuf_common_reset(_STRBUF_CSIZE, buf); +} + +EAPI Eina_Bool +_FUNC_EXPAND(append)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_append(_STRBUF_CSIZE, buf, (const void *) str, _STRBUF_STRLEN_FUNC(str)); +} + +EAPI Eina_Bool +_FUNC_EXPAND(append_escaped)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str) +{ + _STRBUF_DATA_TYPE *esc; + Eina_Bool ret; + + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + esc = _STRBUF_STRESCAPE_FUNC(str); + if (!esc) { + return _FUNC_EXPAND(append)(buf, str); + } + ret = _FUNC_EXPAND(append)(buf, esc); + if (esc) + free(esc); + + return ret; +} + +EAPI Eina_Bool +_FUNC_EXPAND(append_n)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t maxlen) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_append_n(_STRBUF_CSIZE, buf, (const void *) str, _STRBUF_STRLEN_FUNC(str), maxlen); +} + +EAPI Eina_Bool +_FUNC_EXPAND(append_length)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t length) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_append_length(_STRBUF_CSIZE, buf, (const void *) str, length); +} + +EAPI Eina_Bool +_FUNC_EXPAND(insert)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t pos) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_insert(_STRBUF_CSIZE, buf, (const void *) str, _STRBUF_STRLEN_FUNC(str), pos); +} + +EAPI Eina_Bool +_FUNC_EXPAND(insert_escaped)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t pos) +{ + _STRBUF_DATA_TYPE *esc; + Eina_Bool ret; + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + + esc = _STRBUF_STRESCAPE_FUNC(str); + if (!esc) { + return _FUNC_EXPAND(insert)(buf, str, pos); + } + ret = _FUNC_EXPAND(insert)(buf, esc, pos); + if (esc) + free(esc); + + return ret; +} + +EAPI Eina_Bool +_FUNC_EXPAND(insert_n)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t maxlen, size_t pos) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_insert_n(_STRBUF_CSIZE, buf, (const void *) str, _STRBUF_STRLEN_FUNC(str), maxlen, pos); +} + +EAPI Eina_Bool +_FUNC_EXPAND(insert_length)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t length, size_t pos) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_insert_length(_STRBUF_CSIZE, buf, (const void *) str, length, pos); +} + +EAPI Eina_Bool +_FUNC_EXPAND(append_char)(_STRBUF_STRUCT_NAME *buf, _STRBUF_DATA_TYPE c) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_append_char(_STRBUF_CSIZE, buf, (const void *) &c); +} + +EAPI Eina_Bool +_FUNC_EXPAND(insert_char)(_STRBUF_STRUCT_NAME *buf, _STRBUF_DATA_TYPE c, size_t pos) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_insert_char(_STRBUF_CSIZE, buf, (const void *) &c, pos); +} + +EAPI Eina_Bool +_FUNC_EXPAND(remove)(_STRBUF_STRUCT_NAME *buf, size_t start, size_t end) +{ + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + return eina_strbuf_common_remove(_STRBUF_CSIZE, buf, start, end); +} + +EAPI const _STRBUF_DATA_TYPE * +_FUNC_EXPAND(string_get)(const _STRBUF_STRUCT_NAME *buf) +{ + EINA_MAGIC_CHECK_STRBUF(buf, NULL); + return (const _STRBUF_DATA_TYPE *) eina_strbuf_common_string_get(buf); +} + +EAPI _STRBUF_DATA_TYPE * +_FUNC_EXPAND(string_steal)(_STRBUF_STRUCT_NAME *buf) +{ + EINA_MAGIC_CHECK_STRBUF(buf, NULL); + return (_STRBUF_DATA_TYPE *) eina_strbuf_common_string_steal(_STRBUF_CSIZE, buf); +} + + +EAPI void +_FUNC_EXPAND(string_free)(_STRBUF_STRUCT_NAME *buf) +{ + EINA_MAGIC_CHECK_STRBUF(buf); + eina_strbuf_common_string_free(_STRBUF_CSIZE, buf); +} + +EAPI size_t +_FUNC_EXPAND(length_get)(const _STRBUF_STRUCT_NAME *buf) +{ + EINA_MAGIC_CHECK_STRBUF(buf, 0); + return eina_strbuf_common_length_get(buf); +} diff --git a/tests/suite/ecore/src/lib/eina_stringshare.c b/tests/suite/ecore/src/lib/eina_stringshare.c new file mode 100644 index 0000000000..86b461725d --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_stringshare.c @@ -0,0 +1,956 @@ +/* EINA - EFL data type library + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2010 + * Carsten Haitzler, + * Jorge Luis Zapata Muga, + * Cedric Bail, + * Gustavo Sverzut Barbieri + * Tom Hacohen + * Brett Nash + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/** + * @page tutorial_stringshare_page Stringshare Tutorial + * + * to be written... + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define _GNU_SOURCE + +#ifdef HAVE_ALLOCA_H +# include <alloca.h> +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include <malloc.h> +# define alloca _alloca +#else +# include <stddef.h> +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef EFL_HAVE_POSIX_THREADS +# include <pthread.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_hash.h" +#include "eina_rbtree.h" +#include "eina_error.h" +#include "eina_log.h" +#include "eina_stringshare.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_share_common.h" + +/* The actual share */ +static Eina_Share *stringshare_share; +static const char EINA_MAGIC_STRINGSHARE_NODE_STR[] = "Eina Stringshare Node"; + +#ifdef EFL_HAVE_THREADS +extern Eina_Bool _share_common_threads_activated; + +# ifdef EFL_HAVE_POSIX_THREADS +static pthread_mutex_t _mutex_small = PTHREAD_MUTEX_INITIALIZER; +# define STRINGSHARE_LOCK_SMALL() if(_share_common_threads_activated) \ + pthread_mutex_lock(&_mutex_small) +# define STRINGSHARE_UNLOCK_SMALL() if(_share_common_threads_activated) \ + pthread_mutex_unlock(&_mutex_small) +# else /* EFL_HAVE_WIN32_THREADS */ +static HANDLE _mutex_small = NULL; +# define STRINGSHARE_LOCK_SMALL() if(_share_common_threads_activated) \ + WaitForSingleObject(_mutex_small, INFINITE) +# define STRINGSHARE_UNLOCK_SMALL() if(_share_common_threads_activated) \ + ReleaseMutex(_mutex_small) + +# endif /* EFL_HAVE_WIN32_THREADS */ +#else /* EFL_HAVE_THREADS */ +# define STRINGSHARE_LOCK_SMALL() do {} while (0) +# define STRINGSHARE_UNLOCK_SMALL() do {} while (0) +#endif + +/* Stringshare optimizations */ +static const unsigned char _eina_stringshare_single[512] = { + 0,0,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0, + 16,0,17,0,18,0,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0, + 31,0,32,0,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44,0,45,0, + 46,0,47,0,48,0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,60,0, + 61,0,62,0,63,0,64,0,65,0,66,0,67,0,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,0, + 76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0, + 91,0,92,0,93,0,94,0,95,0,96,0,97,0,98,0,99,0,100,0,101,0,102,0,103,0,104,0, + 105,0, + 106,0,107,0,108,0,109,0,110,0,111,0,112,0,113,0,114,0,115,0,116,0,117,0,118, + 0,119,0,120,0, + 121,0,122,0,123,0,124,0,125,0,126,0,127,0,128,0,129,0,130,0,131,0,132,0,133, + 0,134,0,135,0, + 136,0,137,0,138,0,139,0,140,0,141,0,142,0,143,0,144,0,145,0,146,0,147,0,148, + 0,149,0,150,0, + 151,0,152,0,153,0,154,0,155,0,156,0,157,0,158,0,159,0,160,0,161,0,162,0,163, + 0,164,0,165,0, + 166,0,167,0,168,0,169,0,170,0,171,0,172,0,173,0,174,0,175,0,176,0,177,0,178, + 0,179,0,180,0, + 181,0,182,0,183,0,184,0,185,0,186,0,187,0,188,0,189,0,190,0,191,0,192,0,193, + 0,194,0,195,0, + 196,0,197,0,198,0,199,0,200,0,201,0,202,0,203,0,204,0,205,0,206,0,207,0,208, + 0,209,0,210,0, + 211,0,212,0,213,0,214,0,215,0,216,0,217,0,218,0,219,0,220,0,221,0,222,0,223, + 0,224,0,225,0, + 226,0,227,0,228,0,229,0,230,0,231,0,232,0,233,0,234,0,235,0,236,0,237,0,238, + 0,239,0,240,0, + 241,0,242,0,243,0,244,0,245,0,246,0,247,0,248,0,249,0,250,0,251,0,252,0,253, + 0,254,0,255,0 +}; + +typedef struct _Eina_Stringshare_Small Eina_Stringshare_Small; +typedef struct _Eina_Stringshare_Small_Bucket Eina_Stringshare_Small_Bucket; + +struct _Eina_Stringshare_Small_Bucket +{ + /* separate arrays for faster lookups */ + const char **strings; + unsigned char *lengths; + unsigned short *references; + int count; + int size; +}; + +struct _Eina_Stringshare_Small +{ + Eina_Stringshare_Small_Bucket *buckets[256]; +}; + +#define EINA_STRINGSHARE_SMALL_BUCKET_STEP 8 +static Eina_Stringshare_Small _eina_small_share; + +static inline int +_eina_stringshare_small_cmp(const Eina_Stringshare_Small_Bucket *bucket, + int i, + const char *pstr, + unsigned char plength) +{ + /* pstr and plength are from second char and on, since the first is + * always the same. + * + * First string being always the same, size being between 2 and 3 + * characters (there is a check for special case length==1 and then + * small stringshare is applied to strings < 4), we just need to + * compare 2 characters of both strings. + */ + const unsigned char cur_plength = bucket->lengths[i] - 1; + const char *cur_pstr; + + if (cur_plength > plength) + return 1; + else if (cur_plength < plength) + return -1; + + cur_pstr = bucket->strings[i] + 1; + + if (cur_pstr[0] > pstr[0]) + return 1; + else if (cur_pstr[0] < pstr[0]) + return -1; + + if (plength == 1) + return 0; + + if (cur_pstr[1] > pstr[1]) + return 1; + else if (cur_pstr[1] < pstr[1]) + return -1; + + return 0; +} + +static const char * +_eina_stringshare_small_bucket_find(const Eina_Stringshare_Small_Bucket *bucket, + const char *str, + unsigned char length, + int *idx) +{ + const char *pstr = str + 1; /* skip first letter, it's always the same */ + unsigned char plength = length - 1; + int i, low, high; + + if (bucket->count == 0) + { + *idx = 0; + return NULL; + } + + low = 0; + high = bucket->count; + + while (low < high) + { + int r; + + i = (low + high - 1) / 2; + + r = _eina_stringshare_small_cmp(bucket, i, pstr, plength); + if (r > 0) + high = i; + else if (r < 0) + low = i + 1; + else + { + *idx = i; + return bucket->strings[i]; + } + } + + *idx = low; + return NULL; +} + +static Eina_Bool +_eina_stringshare_small_bucket_resize(Eina_Stringshare_Small_Bucket *bucket, + int size) +{ + void *tmp; + + tmp = realloc((void *)bucket->strings, size * sizeof(bucket->strings[0])); + if (!tmp) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return 0; + } + + bucket->strings = tmp; + + tmp = realloc(bucket->lengths, size * sizeof(bucket->lengths[0])); + if (!tmp) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return 0; + } + + bucket->lengths = tmp; + + tmp = realloc(bucket->references, size * sizeof(bucket->references[0])); + if (!tmp) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return 0; + } + + bucket->references = tmp; + + bucket->size = size; + return 1; +} + +static const char * +_eina_stringshare_small_bucket_insert_at( + Eina_Stringshare_Small_Bucket **p_bucket, + const char *str, + unsigned char length, + int idx) +{ + Eina_Stringshare_Small_Bucket *bucket = *p_bucket; + int todo, off; + char *snew; + + if (!bucket) + { + *p_bucket = bucket = calloc(1, sizeof(*bucket)); + if (!bucket) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + } + + if (bucket->count + 1 >= bucket->size) + { + int size = bucket->size + EINA_STRINGSHARE_SMALL_BUCKET_STEP; + if (!_eina_stringshare_small_bucket_resize(bucket, size)) + return NULL; + } + + snew = malloc(length + 1); + if (!snew) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + memcpy(snew, str, length); + snew[length] = '\0'; + + off = idx + 1; + todo = bucket->count - idx; + if (todo > 0) + { + memmove((void *)(bucket->strings + off), bucket->strings + idx, + todo * sizeof(bucket->strings[0])); + memmove(bucket->lengths + off, bucket->lengths + idx, + todo * sizeof(bucket->lengths[0])); + memmove(bucket->references + off, bucket->references + idx, + todo * sizeof(bucket->references[0])); + } + + bucket->strings[idx] = snew; + bucket->lengths[idx] = length; + bucket->references[idx] = 1; + bucket->count++; + + return snew; +} + +static void +_eina_stringshare_small_bucket_remove_at( + Eina_Stringshare_Small_Bucket **p_bucket, + int idx) +{ + Eina_Stringshare_Small_Bucket *bucket = *p_bucket; + int todo, off; + + if (bucket->references[idx] > 1) + { + bucket->references[idx]--; + return; + } + + free((char *)bucket->strings[idx]); + + if (bucket->count == 1) + { + free((void *)bucket->strings); + free(bucket->lengths); + free(bucket->references); + free(bucket); + *p_bucket = NULL; + return; + } + + bucket->count--; + if (idx == bucket->count) + goto end; + + off = idx + 1; + todo = bucket->count - idx; + + memmove((void *)(bucket->strings + idx), bucket->strings + off, + todo * sizeof(bucket->strings[0])); + memmove(bucket->lengths + idx, bucket->lengths + off, + todo * sizeof(bucket->lengths[0])); + memmove(bucket->references + idx, bucket->references + off, + todo * sizeof(bucket->references[0])); + +end: + if (bucket->count + EINA_STRINGSHARE_SMALL_BUCKET_STEP < bucket->size) + { + int size = bucket->size - EINA_STRINGSHARE_SMALL_BUCKET_STEP; + _eina_stringshare_small_bucket_resize(bucket, size); + } +} + +static const char * +_eina_stringshare_small_add(const char *str, unsigned char length) +{ + Eina_Stringshare_Small_Bucket **bucket; + int i; + + bucket = _eina_small_share.buckets + (unsigned char)str[0]; + if (!*bucket) + i = 0; + else + { + const char *ret; + ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i); + if (ret) + { + (*bucket)->references[i]++; + return ret; + } + } + + return _eina_stringshare_small_bucket_insert_at(bucket, str, length, i); +} + +static void +_eina_stringshare_small_del(const char *str, unsigned char length) +{ + Eina_Stringshare_Small_Bucket **bucket; + const char *ret; + int i; + + bucket = _eina_small_share.buckets + (unsigned char)str[0]; + if (!*bucket) + goto error; + + ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i); + if (!ret) + goto error; + + _eina_stringshare_small_bucket_remove_at(bucket, i); + return; + +error: + CRITICAL("EEEK trying to del non-shared stringshare \"%s\"", str); +} + +static void +_eina_stringshare_small_init(void) +{ + memset(&_eina_small_share, 0, sizeof(_eina_small_share)); +} + +static void +_eina_stringshare_small_shutdown(void) +{ + Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end; + + p_bucket = _eina_small_share.buckets; + p_bucket_end = p_bucket + 256; + + for (; p_bucket < p_bucket_end; p_bucket++) + { + Eina_Stringshare_Small_Bucket *bucket = *p_bucket; + char **s, **s_end; + + if (!bucket) + continue; + + s = (char **)bucket->strings; + s_end = s + bucket->count; + for (; s < s_end; s++) + free(*s); + + free((void *)bucket->strings); + free(bucket->lengths); + free(bucket->references); + free(bucket); + *p_bucket = NULL; + } +} + +static void +_eina_stringshare_small_bucket_dump(Eina_Stringshare_Small_Bucket *bucket, + struct dumpinfo *di) +{ + const char **s = bucket->strings; + unsigned char *l = bucket->lengths; + unsigned short *r = bucket->references; + int i; + + di->used += sizeof(*bucket); + di->used += bucket->count * sizeof(*s); + di->used += bucket->count * sizeof(*l); + di->used += bucket->count * sizeof(*r); + di->unique += bucket->count; + + for (i = 0; i < bucket->count; i++, s++, l++, r++) + { + int dups; +#ifdef _WIN32 + printf("DDD: %5hu %5hu '%s'\n", *l, *r, *s); +#else + printf("DDD: %5hhu %5hu '%s'\n", *l, *r, *s); +#endif + + dups = (*r - 1); + + di->used += *l; + di->saved += *l * dups; + di->dups += dups; + } +} + +static void +_eina_stringshare_small_dump(struct dumpinfo *di) +{ + Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end; + + p_bucket = _eina_small_share.buckets; + p_bucket_end = p_bucket + 256; + + for (; p_bucket < p_bucket_end; p_bucket++) + { + Eina_Stringshare_Small_Bucket *bucket = *p_bucket; + + if (!bucket) + continue; + + _eina_stringshare_small_bucket_dump(bucket, di); + } +} + + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the share_common module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the share_common module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_stringshare_init(void) +{ + Eina_Bool ret; + ret = eina_share_common_init(&stringshare_share, + EINA_MAGIC_STRINGSHARE_NODE, + EINA_MAGIC_STRINGSHARE_NODE_STR); + if (ret) + _eina_stringshare_small_init(); + + return ret; +} + +/** + * @internal + * @brief Shut down the share_common module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the share_common module set up by + * eina_share_common_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_stringshare_shutdown(void) +{ + Eina_Bool ret; + _eina_stringshare_small_shutdown(); + ret = eina_share_common_shutdown(&stringshare_share); + return ret; +} + +/*============================================================================* +* API * +*============================================================================*/ + +/** + * @addtogroup Eina_Stringshare_Group Stringshare + * + * These functions allow you to store one copy of a string, and use it + * throughout your program. + * + * This is a method to reduce the number of duplicated strings kept in + * memory. It's pretty common for the same strings to be dynamically + * allocated repeatedly between applications and libraries, especially in + * circumstances where you could have multiple copies of a structure that + * allocates the string. So rather than duplicating and freeing these + * strings, you request a read-only pointer to an existing string and + * only incur the overhead of a hash lookup. + * + * It sounds like micro-optimizing, but profiling has shown this can have + * a significant impact as you scale the number of copies up. It improves + * string creation/destruction speed, reduces memory use and decreases + * memory fragmentation, so a win all-around. + * + * For more information, you can look at the @ref tutorial_stringshare_page. + * + * @{ + */ + +/** + * @brief Note that the given string has lost an instance. + * + * @param str string The given string. + * + * This function decreases the reference counter associated to @p str + * if it exists. If that counter reaches 0, the memory associated to + * @p str is freed. If @p str is NULL, the function returns + * immediately. + * + * Note that if the given pointer is not shared or NULL, bad things + * will happen, likely a segmentation fault. + */ +EAPI void +eina_stringshare_del(const char *str) +{ + int slen; + DBG("str=%p (%s)", str, str ? str : ""); + if (!str) + return; + + /* special cases */ + if (str[0] == '\0') + slen = 0; + else if (str[1] == '\0') + slen = 1; + else if (str[2] == '\0') + slen = 2; + else if (str[3] == '\0') + slen = 3; + else + slen = 4; /* handled later */ + + if (slen < 2) + return; + else if (slen < 4) + { + eina_share_common_population_del(stringshare_share, slen); + STRINGSHARE_LOCK_SMALL(); + _eina_stringshare_small_del(str, slen); + STRINGSHARE_UNLOCK_SMALL(); + return; + } + + eina_share_common_del(stringshare_share, str); +} + +/** + * @brief Retrieve an instance of a string for use in a program. + * + * @param str The string to retrieve an instance of. + * @param slen The string size (<= strlen(str)). + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This function retrieves an instance of @p str. If @p str is + * @c NULL, then @c NULL is returned. If @p str is already stored, it + * is just returned and its reference counter is increased. Otherwise + * it is added to the strings to be searched and a duplicated string + * of @p str is returned. + * + * This function does not check string size, but uses the + * exact given size. This can be used to share_common part of a larger + * buffer or substring. + * + * @see eina_share_common_add() + */ +EAPI const char * +eina_stringshare_add_length(const char *str, unsigned int slen) +{ + DBG("str=%p (%.*s), slen=%u", str, slen, str ? str : "", slen); + + if (slen <= 0) + return ""; + else if (slen == 1) + return (const char *)_eina_stringshare_single + ((*str) << 1); + else if (slen < 4) + { + const char *s; + + STRINGSHARE_LOCK_SMALL(); + s = _eina_stringshare_small_add(str, slen); + STRINGSHARE_UNLOCK_SMALL(); + return s; + } + + return eina_share_common_add_length(stringshare_share, str, slen * + sizeof(char), sizeof(char)); +} + +/** + * @brief Retrieve an instance of a string for use in a program. + * + * @param str The NULL terminated string to retrieve an instance of. + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This function retrieves an instance of @p str. If @p str is + * @c NULL, then @c NULL is returned. If @p str is already stored, it + * is just returned and its reference counter is increased. Otherwise + * it is added to the strings to be searched and a duplicated string + * of @p str is returned. + * + * The string @p str must be NULL terminated ('@\0') and its full + * length will be used. To use part of the string or non-null + * terminated, use eina_stringshare_add_length() instead. + * + * @see eina_stringshare_add_length() + */ +EAPI const char * +eina_stringshare_add(const char *str) +{ + int slen; + if (!str) + return NULL; + + if (str[0] == '\0') + slen = 0; + else if (str[1] == '\0') + slen = 1; + else if (str[2] == '\0') + slen = 2; + else if (str[3] == '\0') + slen = 3; + else + slen = 3 + (int)strlen(str + 3); + + return eina_stringshare_add_length(str, slen); +} + +/** + * @brief Retrieve an instance of a string for use in a program + * from a format string. + * + * @param fmt The NULL terminated format string to retrieve an instance of. + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This function retrieves an instance of @p fmt. If @p fmt is + * @c NULL, then @c NULL is returned. If @p fmt is already stored, it + * is just returned and its reference counter is increased. Otherwise + * it is added to the strings to be searched and a duplicated string + * is returned. + * + * The format string @p fmt must be NULL terminated ('@\0') and its full + * length will be used. To use part of the format string or non-null + * terminated, use eina_stringshare_nprintf() instead. + * + * @see eina_stringshare_nprintf() + */ +EAPI const char * +eina_stringshare_printf(const char *fmt, ...) +{ + va_list args; + char *tmp; + const char *ret; + int len; + + if (!fmt) + return NULL; + + va_start(args, fmt); + len = vasprintf(&tmp, fmt, args); + va_end(args); + + if (len < 1) + return NULL; + + ret = eina_stringshare_add_length(tmp, len); + free(tmp); + + return ret; +} + +/** + * @brief Retrieve an instance of a string for use in a program + * from a format string. + * + * @param fmt The NULL terminated format string to retrieve an instance of. + * @param args The va_args for @p fmt + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This function retrieves an instance of @p fmt with @p args. If @p fmt is + * @c NULL, then @c NULL is returned. If @p fmt with @p args is already stored, it + * is just returned and its reference counter is increased. Otherwise + * it is added to the strings to be searched and a duplicated string + * is returned. + * + * The format string @p fmt must be NULL terminated ('@\0') and its full + * length will be used. To use part of the format string or non-null + * terminated, use eina_stringshare_nprintf() instead. + * + * @see eina_stringshare_nprintf() + */ +EAPI const char * +eina_stringshare_vprintf(const char *fmt, va_list args) +{ + char *tmp; + const char *ret; + int len; + + if (!fmt) + return NULL; + + len = vasprintf(&tmp, fmt, args); + + if (len < 1) + return NULL; + + ret = eina_stringshare_add_length(tmp, len); + free(tmp); + + return ret; +} + +/** + * @brief Retrieve an instance of a string for use in a program + * from a format string with size limitation. + * @param len The length of the format string to use + * @param fmt The format string to retrieve an instance of. + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This function retrieves an instance of @p fmt limited by @p len. If @p fmt is + * @c NULL or @p len is < 1, then @c NULL is returned. If the resulting string + * is already stored, it is returned and its reference counter is increased. Otherwise + * it is added to the strings to be searched and a duplicated string + * is returned. + * + * @p len length of the format string will be used. To use the + * entire format string, use eina_stringshare_printf() instead. + * + * @see eina_stringshare_printf() + */ +EAPI const char * +eina_stringshare_nprintf(unsigned int len, const char *fmt, ...) +{ + va_list args; + char *tmp; + int size; + + if (!fmt) + return NULL; + + if (len < 1) + return NULL; + + tmp = alloca(sizeof(char) * len + 1); + + va_start(args, fmt); + size = vsnprintf(tmp, len, fmt, args); + va_end(args); + + if (size < 1) + return NULL; + + return eina_stringshare_add_length(tmp, len); +} + +/** + * Increment references of the given shared string. + * + * @param str The shared string. + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This is similar to eina_share_common_add(), but it's faster since it will + * avoid lookups if possible, but on the down side it requires the parameter + * to be shared before, in other words, it must be the return of a previous + * eina_share_common_add(). + * + * There is no unref since this is the work of eina_share_common_del(). + */ +EAPI const char * +eina_stringshare_ref(const char *str) +{ + int slen; + DBG("str=%p (%s)", str, str ? str : ""); + + if (!str) + return eina_share_common_ref(stringshare_share, str); + + /* special cases */ + if (str[0] == '\0') + slen = 0; + else if (str[1] == '\0') + slen = 1; + else if (str[2] == '\0') + slen = 2; + else if (str[3] == '\0') + slen = 3; + else + slen = 3 + (int)strlen(str + 3); + + if (slen < 2) + { + eina_share_common_population_add(stringshare_share, slen); + + return str; + } + else if (slen < 4) + { + const char *s; + eina_share_common_population_add(stringshare_share, slen); + + STRINGSHARE_LOCK_SMALL(); + s = _eina_stringshare_small_add(str, slen); + STRINGSHARE_UNLOCK_SMALL(); + + return s; + } + + return eina_share_common_ref(stringshare_share, str); +} + +/** + * @brief Note that the given string @b must be shared. + * + * @param str the shared string to know the length. It is safe to + * give NULL, in that case -1 is returned. + * + * This function is a cheap way to known the length of a shared + * string. Note that if the given pointer is not shared, bad + * things will happen, likely a segmentation fault. If in doubt, try + * strlen(). + */ +EAPI int +eina_stringshare_strlen(const char *str) +{ + int len; + /* special cases */ + if (str[0] == '\0') + return 0; + + if (str[1] == '\0') + return 1; + + if (str[2] == '\0') + return 2; + + if (str[3] == '\0') + return 3; + + len = eina_share_common_length(stringshare_share, (const char *)str); + len = (len > 0) ? len / (int)sizeof(char) : -1; + return len; +} + +/** + * @brief Dump the contents of the share_common. + * + * This function dumps all strings in the share_common to stdout with a + * DDD: prefix per line and a memory usage summary. + */ +EAPI void +eina_stringshare_dump(void) +{ + eina_share_common_dump(stringshare_share, + _eina_stringshare_small_dump, + sizeof(_eina_stringshare_single)); +} + +/** + * @} + */ + diff --git a/tests/suite/ecore/src/lib/eina_tiler.c b/tests/suite/ecore/src/lib/eina_tiler.c new file mode 100644 index 0000000000..2d263b7a15 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_tiler.c @@ -0,0 +1,1295 @@ +/* EINA - EFL data type library + * Copyright (C) 2007-2008 Gustavo Sverzut Barbieri, Jorge Luis Zapata Muga + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/* TODO + * it is possible to have more than one tiler algorithm, but for now the + * version Gustavo did is hardcoded here + * http://blog.gustavobarbieri.com.br/2007/06/03/evas-now-using-rectangle-split-and-merge/ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_tiler.h" +#include "eina_error.h" + +/*============================================================================* +* Local * +*============================================================================*/ + +/* The splitter data types */ +typedef struct list_node list_node_t; +typedef struct list list_t; +typedef struct rect rect_t; +typedef struct rect_node rect_node_t; + +struct list_node +{ + struct list_node *next; +}; + +struct list +{ + struct list_node *head; + struct list_node *tail; +}; + +struct rect +{ + short right; + short bottom; + short left; + short top; + short width; + short height; + int area; +}; + +struct rect_node +{ + struct list_node _lst; + struct rect rect; +}; + +typedef struct splitter +{ + Eina_Bool need_merge; + list_t rects; +} splitter_t; + +typedef struct list_node_pool +{ + list_node_t *node; + int len; + int max; +} list_node_pool_t; + + +static const list_node_t list_node_zeroed = { NULL }; +static const list_t list_zeroed = { NULL, NULL }; +static list_node_pool_t list_node_pool = { NULL, 0, 1024 }; + + +typedef struct _Eina_Iterator_Tiler +{ + Eina_Iterator iterator; + const Eina_Tiler *tiler; + list_node_t *curr; + EINA_MAGIC +} Eina_Iterator_Tiler; + +struct _Eina_Tiler +{ + struct + { + int w, h; + } tile; + Eina_Rectangle area; + EINA_MAGIC + splitter_t splitter; +}; + +#define EINA_MAGIC_CHECK_TILER(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_TILER)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_TILER); \ + return __VA_ARGS__; \ + } \ + } while(0) + + +#define EINA_MAGIC_CHECK_TILER_ITERATOR(d, ...) \ + do { \ + if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_TILER_ITERATOR)) \ + { \ + EINA_MAGIC_FAIL(d, EINA_MAGIC_TILER_ITERATOR); \ + return __VA_ARGS__; \ + } \ + } while(0) + +/* The Splitter algorithm */ +static inline void rect_init(rect_t *r, int x, int y, int w, int h) +{ + r->area = w * h; + + r->left = x; + r->top = y; + + r->right = x + w; + r->bottom = y + h; + + r->width = w; + r->height = h; +} + +static inline list_node_t * +rect_list_node_pool_get(void) +{ + if (list_node_pool.node) + { + list_node_t *node; + + node = list_node_pool.node; + list_node_pool.node = node->next; + list_node_pool.len--; + + return node; + } + else + return malloc(sizeof(rect_node_t)); +} + + +static inline void rect_list_concat(list_t *rects, list_t *other) +{ + if (!other->head) + return; + + if (rects->tail) + { + rects->tail->next = other->head; + rects->tail = other->tail; + } + else + { + rects->head = other->head; + rects->tail = other->tail; + } + + *other = list_zeroed; +} + +static inline void rect_list_append_node(list_t *rects, list_node_t *node) +{ + if (rects->tail) + { + rects->tail->next = node; + rects->tail = node; + } + else + { + rects->head = node; + rects->tail = node; + } +} + +static inline void rect_list_append(list_t *rects, const rect_t r) +{ + rect_node_t *rect_node; + + rect_node = (rect_node_t *)rect_list_node_pool_get(); + rect_node->rect = r; + rect_node->_lst = list_node_zeroed; + + rect_list_append_node(rects, (list_node_t *)rect_node); +} + +static inline void rect_list_append_xywh(list_t *rects, + int x, + int y, + int w, + int h) +{ + rect_t r; + + rect_init(&r, x, y, w, h); + rect_list_append(rects, r); +} + +static inline void _calc_intra_rect_area(const rect_t a, const rect_t b, + int *width, int *height) +{ + int max_left, min_right, max_top, min_bottom; + + if (a.left < b.left) + max_left = b.left; + else + max_left = a.left; + + if (a.right < b.right) + min_right = a.right; + else + min_right = b.right; + + *width = min_right - max_left; + + if (a.top < b.top) + max_top = b.top; + else + max_top = a.top; + + if (a.bottom < b.bottom) + min_bottom = a.bottom; + else + min_bottom = b.bottom; + + *height = min_bottom - max_top; +} + +static inline void _split_strict(list_t *dirty, const rect_t current, rect_t r) +{ + int h_1, h_2, w_1, w_2; + + h_1 = current.top - r.top; + h_2 = r.bottom - current.bottom; + w_1 = current.left - r.left; + w_2 = r.right - current.right; + + if (h_1 > 0) + { + /* .--.r (b) .---.r2 + * | | | | + * .-------.cur (a) .---.r '---' + * | | | | -> | | + + * | `--' | `---' + * `-------' + */ + rect_list_append_xywh(dirty, r.left, r.top, r.width, h_1); + r.height -= h_1; + r.top = current.top; + } + + if (h_2 > 0) + { + /* .-------.cur (a) + * | .---. | .---.r + * | | | | -> | | + * `-------' `---' + .---.r2 + * | | | | + * `---'r (b) `---' + */ + rect_list_append_xywh(dirty, r.left, current.bottom, r.width, + h_2); + r.height -= h_2; + } + + if (w_1 > 0) + /* (b) r .----.cur (a) + * .--|-. | .--.r2 .-.r + * | | | | -> | | + | | + * `--|-' | `--' `-' + * `----' + */ + rect_list_append_xywh(dirty, r.left, r.top, w_1, r.height); /* not necessary to keep these, r (b) will be destroyed */ + + /* r.width -= w_1; */ + /* r.left = current.left; */ + + if (w_2 > 0) + /* .----.cur (a) + * | | + * | .-|--.r (b) .-.r .--.r2 + * | | | | -> | | + | | + * | `-|--' `-' `--' + * `----' + */ + rect_list_append_xywh(dirty, current.right, r.top, w_2, + r.height); /* not necessary to keep this, r (b) will be destroyed */ + + /* r.width -= w_2; */ +} + +static inline void _calc_intra_outer_rect_area(const rect_t a, const rect_t b, + rect_t *intra, rect_t *outer) +{ + int min_left, max_left, min_right, max_right; + int min_top, max_top, min_bottom, max_bottom; + + if (a.left < b.left) + { + max_left = b.left; + min_left = a.left; + } + else + { + max_left = a.left; + min_left = b.left; + } + + if (a.right < b.right) + { + min_right = a.right; + max_right = b.right; + } + else + { + min_right = b.right; + max_right = a.right; + } + + intra->left = max_left; + intra->right = min_right; + intra->width = min_right - max_left; + + outer->left = min_left; + outer->right = max_right; + outer->width = max_right - min_left; + + if (a.top < b.top) + { + max_top = b.top; + min_top = a.top; + } + else + { + max_top = a.top; + min_top = b.top; + } + + if (a.bottom < b.bottom) + { + min_bottom = a.bottom; + max_bottom = b.bottom; + } + else + { + min_bottom = b.bottom; + max_bottom = a.bottom; + } + + intra->top = max_top; + intra->bottom = min_bottom; + intra->height = min_bottom - max_top; + if ((intra->width > 0) && (intra->height > 0)) + intra->area = intra->width * intra->height; + else + intra->area = 0; + + outer->top = min_top; + outer->bottom = max_bottom; + outer->height = max_bottom - min_top; + outer->area = outer->width * outer->height; +} + +enum +{ + SPLIT_FUZZY_ACTION_NONE, + SPLIT_FUZZY_ACTION_SPLIT, + SPLIT_FUZZY_ACTION_MERGE +}; + +static inline int _split_fuzzy(list_t *dirty, const rect_t a, rect_t *b) +{ + int h_1, h_2, w_1, w_2, action; + + h_1 = a.top - b->top; + h_2 = b->bottom - a.bottom; + w_1 = a.left - b->left; + w_2 = b->right - a.right; + + action = SPLIT_FUZZY_ACTION_NONE; + + if (h_1 > 0) + { + /* .--.r (b) .---.r2 + * | | | | + * .-------.cur (a) .---.r '---' + * | | | | -> | | + + * | `--' | `---' + * `-------' + */ + rect_list_append_xywh(dirty, b->left, b->top, b->width, h_1); + b->height -= h_1; + b->top = a.top; + action = SPLIT_FUZZY_ACTION_SPLIT; + } + + if (h_2 > 0) + { + /* .-------.cur (a) + * | .---. | .---.r + * | | | | -> | | + * `-------' `---' + .---.r2 + * | | | | + * `---'r (b) `---' + */ + rect_list_append_xywh(dirty, b->left, a.bottom, b->width, h_2); + b->height -= h_2; + action = SPLIT_FUZZY_ACTION_SPLIT; + } + + if (((w_1 > 0) || (w_2 > 0)) && (a.height == b->height)) + return SPLIT_FUZZY_ACTION_MERGE; + + if (w_1 > 0) + { + /* (b) r .----.cur (a) + * .--|-. | .--.r2 .-.r + * | | | | -> | | + | | + * `--|-' | `--' `-' + * `----' + */ + rect_list_append_xywh(dirty, b->left, b->top, w_1, b->height); + /* not necessary to keep these, r (b) will be destroyed */ + /* b->width -= w_1; */ + /* b->left = a.left; */ + action = SPLIT_FUZZY_ACTION_SPLIT; + } + + if (w_2 > 0) + { + /* .----.cur (a) + * | | + * | .-|--.r (b) .-.r .--.r2 + * | | | | -> | | + | | + * | `-|--' `-' `--' + * `----' + */ + rect_list_append_xywh(dirty, a.right, b->top, w_2, b->height); + /* not necessary to keep these, r (b) will be destroyed */ + /* b->width -= w_2; */ + action = SPLIT_FUZZY_ACTION_SPLIT; + } + + return action; +} + +#if 0 +static void rect_list_node_pool_set_max(int max) +{ + int diff; + + diff = list_node_pool.len - max; + for (; diff > 0 && list_node_pool.node != NULL; diff--) + { + list_node_t *node; + + node = list_node_pool.node; + list_node_pool.node = node->next; + list_node_pool.len--; + + free(node); + } + + list_node_pool.max = max; +} +#endif + +static void rect_list_node_pool_flush(void) +{ + while (list_node_pool.node) + { + list_node_t *node; + + node = list_node_pool.node; + list_node_pool.node = node->next; + list_node_pool.len--; + + free(node); + } +} + + + +static inline void rect_list_node_pool_put(list_node_t *node) +{ + if (list_node_pool.len < list_node_pool.max) + { + node->next = list_node_pool.node; + list_node_pool.node = node; + list_node_pool.len++; + } + else + free(node); +} + +#if 0 +static void rect_print(const rect_t r) +{ + printf("<rect(%d, %d, %d, %d)>", r.left, r.top, r.width, r.height); +} + +static void rect_list_print(const list_t rects) +{ + list_node_t *node; + int len; + + len = 0; + for (node = rects.head; node != NULL; node = node->next) + len++; + + printf("["); + for (node = rects.head; node != NULL; node = node->next) + { + rect_print(((rect_node_t *)node)->rect); + if (node->next) + { + putchar(','); + if (len < 4) + putchar(' '); + else + { + putchar('\n'); + putchar(' '); + } + } + } + printf("]\n"); +} +#endif + +static inline list_node_t * +rect_list_unlink_next(list_t *rects, list_node_t *parent_node) +{ + list_node_t *node; + + if (parent_node) + { + node = parent_node->next; + parent_node->next = node->next; + } + else + { + node = rects->head; + rects->head = node->next; + } + + if (rects->tail == node) + rects->tail = parent_node; + + *node = list_node_zeroed; + return node; +} + +static inline void rect_list_del_next(list_t *rects, list_node_t *parent_node) +{ + list_node_t *node; + + node = rect_list_unlink_next(rects, parent_node); + rect_list_node_pool_put(node); +} + +static void rect_list_clear(list_t *rects) +{ + list_node_t *node; + + node = rects->head; + while (node) + { + list_node_t *aux; + + aux = node->next; + rect_list_node_pool_put(node); + node = aux; + } + *rects = list_zeroed; +} + +static void rect_list_del_split_strict(list_t *rects, const rect_t del_r) +{ + list_t modified = list_zeroed; + list_node_t *cur_node, *prev_node; + + prev_node = NULL; + cur_node = rects->head; + while (cur_node) + { + int intra_width, intra_height; + rect_t current; + + current = ((rect_node_t *)cur_node)->rect; + + _calc_intra_rect_area(del_r, current, &intra_width, + &intra_height); + if ((intra_width <= 0) || (intra_height <= 0)) + { + /* .---.current .---.del_r + * | | | | + * `---+---.del_r `---+---.current + * | | | | + * `---' `---' + * no intersection, nothing to do + */ + prev_node = cur_node; + cur_node = cur_node->next; + } + else if ((intra_width == current.width) && (intra_height + == current.height)) + { + /* .-------.del_r + * | .---. | + * | | | | + * | `---'current + * `-------' + * current is contained, remove from rects + */ + cur_node = cur_node->next; + rect_list_del_next(rects, prev_node); + } + else + { + _split_strict(&modified, del_r, current); + cur_node = cur_node->next; + rect_list_del_next(rects, prev_node); + } + } + + rect_list_concat(rects, &modified); +} + +#if 0 +static void rect_list_add_split_strict(list_t *rects, list_node_t *node) +{ + list_t dirty = list_zeroed; + list_t new_dirty = list_zeroed; + list_node_t *cur_node; + + if (!rects->head) + { + rect_list_append_node(rects, node); + return; + } + + rect_list_append_node(&dirty, node); + + cur_node = rects->head; + while (dirty.head) + { + rect_t current; + + if (!cur_node) + { + rect_list_concat(rects, &dirty); + break; + } + + current = ((rect_node_t *)cur_node)->rect; + + while (dirty.head) + { + int intra_width, intra_height; + rect_t r; + + r = ((rect_node_t *)dirty.head)->rect; + _calc_intra_rect_area(r, current, &intra_width, + &intra_height); + if ((intra_width == r.width) && (intra_height + == r.height)) + /* .-------.cur + * | .---.r| + * | | | | + * | `---' | + * `-------' + */ + rect_list_del_next(&dirty, NULL); + else if ((intra_width <= 0) || (intra_height <= 0)) + { + /* .---.cur .---.r + * | | | | + * `---+---.r `---+---.cur + * | | | | + * `---' `---' + */ + list_node_t *tmp; + tmp = rect_list_unlink_next(&dirty, NULL); + rect_list_append_node(&new_dirty, tmp); + } + else + { + _split_strict(&new_dirty, current, r); + rect_list_del_next(&dirty, NULL); + } + } + dirty = new_dirty; + new_dirty = list_zeroed; + + cur_node = cur_node->next; + } +} +#endif + +static list_node_t * +rect_list_add_split_fuzzy(list_t *rects, list_node_t *node, int accepted_error) +{ + list_t dirty = list_zeroed; + list_node_t *old_last; + + old_last = rects->tail; + + if (!rects->head) + { + rect_list_append_node(rects, node); + return old_last; + } + + rect_list_append_node(&dirty, node); + while (dirty.head) + { + list_node_t *d_node, *cur_node, *prev_cur_node; + int keep_dirty; + rect_t r; + + d_node = rect_list_unlink_next(&dirty, NULL); + r = ((rect_node_t *)d_node)->rect; + + prev_cur_node = NULL; + cur_node = rects->head; + keep_dirty = 1; + while (cur_node) + { + int area, action; + rect_t current, intra, outer; + + current = ((rect_node_t *)cur_node)->rect; + + _calc_intra_outer_rect_area(r, current, &intra, &outer); + area = current.area + r.area - intra.area; + + if ((intra.width == r.width) && (intra.height + == r.height)) + { + /* .-------.cur + * | .---.r| + * | | | | + * | `---' | + * `-------' + */ + keep_dirty = 0; + break; + } + else if ((intra.width == current.width) + && (intra.height == current.height)) + { + /* .-------.r + * | .---.cur + * | | | | + * | `---' | + * `-------' + */ + if (old_last == cur_node) + old_last = prev_cur_node; + + cur_node = cur_node->next; + rect_list_del_next(rects, prev_cur_node); + } + else if ((outer.area - area) <= accepted_error) + { + /* .-----------. bounding box (outer) + * |.---. .---.| + * ||cur| |r || + * || | | || + * |`---' `---'| + * `-----------' + * merge them, remove both and add merged + */ + rect_node_t *n; + + if (old_last == cur_node) + old_last = prev_cur_node; + + n = (rect_node_t *)rect_list_unlink_next( + rects, prev_cur_node); + n->rect = outer; + rect_list_append_node(&dirty, (list_node_t *)n); + + keep_dirty = 0; + break; + } + else if (intra.area <= accepted_error) + { + /* .---.cur .---.r + * | | | | + * `---+---.r `---+---.cur + * | | | | + * `---' `---' + * no split, no merge + */ + prev_cur_node = cur_node; + cur_node = cur_node->next; + } + else + { + /* split is required */ + action = _split_fuzzy(&dirty, current, &r); + if (action == SPLIT_FUZZY_ACTION_MERGE) + { +/* horizontal merge is possible: remove both, add merged */ + rect_node_t *n; + + if (old_last == cur_node) + old_last = prev_cur_node; + + n + = (rect_node_t *)rect_list_unlink_next( + rects, + prev_cur_node); + + n->rect.left = outer.left; + n->rect.width = outer.width; + n->rect.right = outer.right; + n->rect.area = outer.width * r.height; + rect_list_append_node(&dirty, + (list_node_t *)n); + } + else if (action == SPLIT_FUZZY_ACTION_NONE) + { +/* + * this rect check was totally useless, + * should never happen + */ +/* prev_cur_node = cur_node; */ +/* cur_node = cur_node->next; */ + printf("Should not get here!\n"); + abort(); + } + + keep_dirty = 0; + break; + } + } + if (EINA_UNLIKELY(keep_dirty)) + rect_list_append_node(rects, d_node); + else + rect_list_node_pool_put(d_node); + } + + return old_last; +} + +static inline void _calc_outer_rect_area(const rect_t a, const rect_t b, + rect_t *outer) +{ + int min_left, max_right; + int min_top, max_bottom; + + if (a.left < b.left) + min_left = a.left; + else + min_left = b.left; + + if (a.right < b.right) + max_right = b.right; + else + max_right = a.right; + + outer->left = min_left; + outer->right = max_right; + outer->width = max_right - min_left; + + if (a.top < b.top) + min_top = a.top; + else + min_top = b.top; + + if (a.bottom < b.bottom) + max_bottom = b.bottom; + else + max_bottom = a.bottom; + + outer->top = min_top; + outer->bottom = max_bottom; + outer->height = max_bottom - min_top; + + outer->area = outer->width * outer->height; +} + +static void rect_list_merge_rects(list_t *rects, + list_t *to_merge, + int accepted_error) +{ + while (to_merge->head) + { + list_node_t *node, *parent_node; + rect_t r1; + int merged; + + r1 = ((rect_node_t *)to_merge->head)->rect; + + merged = 0; + parent_node = NULL; + node = rects->head; + while (node) + { + rect_t r2, outer; + int area; + + r2 = ((rect_node_t *)node)->rect; + + _calc_outer_rect_area(r1, r2, &outer); + area = r1.area + r2.area; /* intra area is taken as 0 */ + if (outer.area - area <= accepted_error) + { + /* + * remove both r1 and r2, create r3 + * actually r3 uses r2 instance, saves memory + */ + rect_node_t *n; + + n = (rect_node_t *)rect_list_unlink_next( + rects, parent_node); + n->rect = outer; + rect_list_append_node(to_merge, + (list_node_t *)n); + merged = 1; + break; + } + + parent_node = node; + node = node->next; + } + + if (!merged) + { + list_node_t *n; + n = rect_list_unlink_next(to_merge, NULL); + rect_list_append_node(rects, n); + } + else + rect_list_del_next(to_merge, NULL); + } +} + +static void rect_list_add_split_fuzzy_and_merge(list_t *rects, + list_node_t *node, + int split_accepted_error, + int merge_accepted_error) +{ + list_node_t *n; + + n = rect_list_add_split_fuzzy(rects, node, split_accepted_error); + if (n && n->next) + { + list_t to_merge; + + /* split list into 2 segments, already merged and to merge */ + to_merge.head = n->next; + to_merge.tail = rects->tail; + rects->tail = n; + n->next = NULL; + + rect_list_merge_rects(rects, &to_merge, merge_accepted_error); + } +} + +static inline void _splitter_new(Eina_Tiler *t) +{ + t->splitter.rects = list_zeroed; + t->splitter.need_merge = EINA_FALSE; +} + +static inline void _splitter_del(Eina_Tiler *t) +{ + rect_list_clear(&t->splitter.rects); + rect_list_node_pool_flush(); +} + +static inline void _splitter_tile_size_set(Eina_Tiler *t, + int w __UNUSED__, + int h __UNUSED__) +{ + /* TODO are w and h used for something? */ + t->splitter.rects = list_zeroed; +} + +static inline Eina_Bool _splitter_rect_add(Eina_Tiler *t, Eina_Rectangle *rect) +{ + rect_node_t *rn; + + //printf("ACCOUNTING[1]: add_redraw: %4d,%4d %3dx%3d\n", x, y, w, h); + rect->x >>= 1; + rect->y >>= 1; + rect->w += 2; + rect->w >>= 1; + rect->h += 2; + rect->h >>= 1; + + rn = (rect_node_t *)rect_list_node_pool_get(); + rn->_lst = list_node_zeroed; + rect_init(&rn->rect, rect->x, rect->y, rect->w, rect->h); + //printf("ACCOUNTING[2]: add_redraw: %4d,%4d %3dx%3d\n", x, y, w, h); + //testing on my core2 duo desktop - fuzz of 32 or 48 is best. +#define FUZZ 32 + rect_list_add_split_fuzzy_and_merge(&t->splitter.rects, + (list_node_t *)rn, + FUZZ * FUZZ, + FUZZ * FUZZ); + return EINA_TRUE; +} + +static inline void _splitter_rect_del(Eina_Tiler *t, Eina_Rectangle *rect) +{ + rect_t r; + + if (!t->splitter.rects.head) + return; + + rect->x += 1; + rect->y += 1; + rect->x >>= 1; + rect->y >>= 1; + rect->w -= 1; + rect->w >>= 1; + rect->h -= 1; + rect->h >>= 1; + + if ((rect->w <= 0) || (rect->h <= 0)) + return; + + rect_init(&r, rect->x, rect->y, rect->w, rect->h); + //fprintf(stderr, "ACCOUNTING: del_redraw: %4d,%4d %3dx%3d\n", x, y, w, h); + + rect_list_del_split_strict(&t->splitter.rects, r); + t->splitter.need_merge = EINA_TRUE; + return; +} + +static inline void _splitter_clear(Eina_Tiler *t) +{ + rect_list_clear(&t->splitter.rects); + t->splitter.need_merge = EINA_FALSE; +} +/* end of splitter algorithm */ + +static Eina_Bool _iterator_next(Eina_Iterator_Tiler *it, void **data) +{ + Eina_Rectangle *rect = (Eina_Rectangle *)data; + list_node_t *n; + + for (n = it->curr; n; n = n->next) + { + rect_t cur; + + cur = ((rect_node_t *)n)->rect; + + rect->x = cur.left << 1; + rect->y = cur.top << 1; + rect->w = cur.width << 1; + rect->h = cur.height << 1; + + if (eina_rectangle_intersection(rect, &it->tiler->area) == EINA_FALSE) + continue; + + if ((rect->w <= 0) || (rect->h <= 0)) + continue; + + it->curr = n->next; + return EINA_TRUE; + } + return EINA_FALSE; +} + +static void *_iterator_get_container(Eina_Iterator_Tiler *it) +{ + EINA_MAGIC_CHECK_TILER_ITERATOR(it, NULL); + return (void *)it->tiler; +} + +static void _iterator_free(Eina_Iterator_Tiler *it) +{ + EINA_MAGIC_CHECK_TILER_ITERATOR(it); + free(it); +} + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +EAPI Eina_Tiler *eina_tiler_new(int w, int h) +{ + Eina_Tiler *t; + + t = calloc(1, sizeof(Eina_Tiler)); + t->area.w = w; + t->area.h = h; + t->tile.w = w; + t->tile.h = h; + EINA_MAGIC_SET(t, EINA_MAGIC_TILER); + _splitter_new(t); + return t; +} + +EAPI void eina_tiler_free(Eina_Tiler *t) +{ + EINA_MAGIC_CHECK_TILER(t); + _splitter_del(t); + free(t); +} + +EAPI void eina_tiler_tile_size_set(Eina_Tiler *t, int w, int h) +{ + EINA_MAGIC_CHECK_TILER(t); + if ((w <= 0) || (h <= 0)) + return; + + t->tile.w = w; + t->tile.h = h; + _splitter_tile_size_set(t, w, h); +} + +EAPI Eina_Bool eina_tiler_rect_add(Eina_Tiler *t, const Eina_Rectangle *r) +{ + Eina_Rectangle tmp; + + EINA_MAGIC_CHECK_TILER(t, EINA_FALSE); + if ((r->w <= 0) || (r->h <= 0)) + return EINA_FALSE; + + tmp = *r; + if (eina_rectangle_intersection(&tmp, &t->area) == EINA_FALSE) + return EINA_FALSE; + + if ((tmp.w <= 0) || (tmp.h <= 0)) + return EINA_FALSE; + + return _splitter_rect_add(t, &tmp); +} + +EAPI void eina_tiler_rect_del(Eina_Tiler *t, const Eina_Rectangle *r) +{ + Eina_Rectangle tmp; + + EINA_MAGIC_CHECK_TILER(t); + if ((r->w <= 0) || (r->h <= 0)) + return; + + tmp = *r; + if (eina_rectangle_intersection(&tmp, &t->area) == EINA_FALSE) + return; + + if ((tmp.w <= 0) || (tmp.h <= 0)) + return; + + _splitter_rect_del(t, &tmp); +} + +EAPI void eina_tiler_clear(Eina_Tiler *t) +{ + EINA_MAGIC_CHECK_TILER(t); + _splitter_clear(t); +} + + +EAPI Eina_Iterator *eina_tiler_iterator_new(const Eina_Tiler *t) +{ + Eina_Iterator_Tiler *it; + + EINA_MAGIC_CHECK_TILER(t, NULL); + + it = calloc(1, sizeof (Eina_Iterator_Tiler)); + if (!it) + return NULL; + + it->tiler = t; + + if (t->splitter.need_merge == EINA_TRUE) + { + list_t to_merge; + splitter_t *sp; + + sp = (splitter_t *)&(t->splitter); + to_merge = t->splitter.rects; + sp->rects = list_zeroed; + rect_list_merge_rects(&sp->rects, &to_merge, FUZZ * FUZZ); + sp->need_merge = 0; + } + + it->curr = it->tiler->splitter.rects.head; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( + _iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(_iterator_free); + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + EINA_MAGIC_SET(it, EINA_MAGIC_TILER_ITERATOR); + + return &it->iterator; +} + +struct _Eina_Tile_Grid_Slicer_Iterator +{ + Eina_Iterator iterator; + Eina_Tile_Grid_Slicer priv; +}; + +typedef struct _Eina_Tile_Grid_Slicer_Iterator Eina_Tile_Grid_Slicer_Iterator; + +static void +eina_tile_grid_slicer_iterator_free(Eina_Tile_Grid_Slicer_Iterator *it) +{ + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_NONE); + free(it); +} + +static Eina_Bool +eina_tile_grid_slicer_iterator_next(Eina_Tile_Grid_Slicer_Iterator *it, + void **data) +{ + return eina_tile_grid_slicer_next + (&it->priv, (const Eina_Tile_Grid_Info **)data); +} + +/** + * @brief Creates a new Eina_Iterator that slices over a list of tiles. + * + * @param x X axis coordinate. + * @param y Y axis coordinate. + * @param w width. + * @param h height. + * @param tile_w tile width. + * @param tile_h tile height. + * @return A pointer to the Eina_Iterator. + * @c NULL on failure. + * + * The tile grid is defined by @a tile_w and @a tile_h while the region is + * defined by @a x, @a y, @a w, @a h. The output is given as + * @c Eina_Tile_Grid_Info where tile index is given in @c col col and + * @c row row with tile-relative + * coordinates in @c x, @c y, @c w, @c h. If tile was fully filled by + * region, then @c full flag + * is set. + */ +EAPI Eina_Iterator * +eina_tile_grid_slicer_iterator_new(int x, + int y, + int w, + int h, + int tile_w, + int tile_h) +{ + Eina_Tile_Grid_Slicer_Iterator *it; + + it = calloc(1, sizeof(*it)); + if (!it) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(eina_tile_grid_slicer_iterator_next); + it->iterator.free = FUNC_ITERATOR_FREE(eina_tile_grid_slicer_iterator_free); + + eina_tile_grid_slicer_setup(&it->priv, x, y, w, h, tile_w, tile_h); + + return &it->iterator; +} diff --git a/tests/suite/ecore/src/lib/eina_unicode.c b/tests/suite/ecore/src/lib/eina_unicode.c new file mode 100644 index 0000000000..cef07fb4fa --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_unicode.c @@ -0,0 +1,176 @@ +/* EINA - EFL data type library + * Copyright (C) 2010 Tom Hacohen, + * Brett Nash + * + * 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 <http://www.gnu.org/licenses/>. + + */ + +#include <Eina.h> +#include "eina_unicode.h" + +/* FIXME: check if sizeof(wchar_t) == sizeof(Eina_Unicode) if so, + * probably better to use the standard functions */ + +/* Maybe I'm too tired, but this is the only thing that actually worked. */ +const Eina_Unicode _EINA_UNICODE_EMPTY_STRING[1] = {0}; +EAPI const Eina_Unicode *EINA_UNICODE_EMPTY_STRING = _EINA_UNICODE_EMPTY_STRING; +/** + * @brief Same as the standard strcmp just with Eina_Unicode instead of char. + */ +EAPI int +eina_unicode_strcmp(const Eina_Unicode *a, const Eina_Unicode *b) +{ + for (; *a && *a == *b; a++, b++) + ; + if (*a == *b) + return 0; + else if (*a < *b) + return -1; + else + return 1; +} + +/** + * @brief Same as the standard strcpy just with Eina_Unicode instead of char. + */ +EAPI Eina_Unicode * +eina_unicode_strcpy(Eina_Unicode *dest, const Eina_Unicode *source) +{ + Eina_Unicode *ret = dest; + + while (*source) + *dest++ = *source++; + *dest = 0; + return ret; +} + +/** + * @brief Same as the standard strncpy just with Eina_Unicode instead of char. + */ +EAPI Eina_Unicode * +eina_unicode_strncpy(Eina_Unicode *dest, const Eina_Unicode *source, size_t n) +{ + Eina_Unicode *ret = dest; + + for ( ; n && *source ; n--) + *dest++ = *source++; + for (; n; n--) + *dest++ = 0; + return ret; +} + +/** + * @brief Same as the standard strlen just with Eina_Unicode instead of char. + */ +EAPI size_t +eina_unicode_strlen(const Eina_Unicode *ustr) +{ + const Eina_Unicode *end; + for (end = ustr; *end; end++) + ; + return end - ustr; +} + +/** + * @brief Returns the length of a Eina_Unicode string, up to a limit. + * + * This function returns the number of characters in string, up to a maximum + * of n. If the terminating character is not found in the string, it returns + * n. + * + * @param ustr String to search + * @param n Max length to search + * @return Number of characters or n. + */ +EAPI size_t +eina_unicode_strnlen(const Eina_Unicode *ustr, int n) +{ + const Eina_Unicode *end; + const Eina_Unicode *last = ustr + n; /* technically not portable ;-) */ + for (end = ustr; end < last && *end; end++) + ; + return end - ustr; +} + + + + +/** + * @brief Same as the standard strdup just with Eina_Unicode instead of char. + */ +EAPI Eina_Unicode * +eina_unicode_strdup(const Eina_Unicode *text) +{ + Eina_Unicode *ustr; + int len; + + len = eina_unicode_strlen(text); + ustr = (Eina_Unicode *)calloc(len + 1, sizeof(Eina_Unicode)); + memcpy(ustr, text, len * sizeof(Eina_Unicode)); + + return ustr; +} + +/** + * @brief Same as the standard strdup just with Eina_Unicode instead of char. + */ +EAPI Eina_Unicode * +eina_unicode_strstr(const Eina_Unicode *haystack, const Eina_Unicode *needle) +{ + const Eina_Unicode *i, *j; + + for (i = haystack; *i; i++) + { + haystack = i; /* set this location as the base position */ + for (j = needle; *j && *i && *j == *i; j++, i++) + ; + + if (!*j) /*if we got to the end of j this means we got a full match */ + { + return (Eina_Unicode *)haystack; /* return the new base position */ + } + } + + return NULL; +} + +/** + * @see eina_str_escape() + */ +EAPI Eina_Unicode * +eina_unicode_escape(const Eina_Unicode *str) +{ + Eina_Unicode *s2, *d; + const Eina_Unicode *s; + + s2 = malloc((eina_unicode_strlen(str) * 2) + 1); + if (!s2) + return NULL; + + for (s = str, d = s2; *s != 0; s++, d++) + { + if ((*s == ' ') || (*s == '\\') || (*s == '\'')) + { + *d = '\\'; + d++; + } + + *d = *s; + } + *d = 0; + return s2; +} + diff --git a/tests/suite/ecore/src/lib/eina_ustrbuf.c b/tests/suite/ecore/src/lib/eina_ustrbuf.c new file mode 100644 index 0000000000..7df5b1603b --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_ustrbuf.c @@ -0,0 +1,89 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "eina_strbuf_common.h" +#include "eina_unicode.h" +#include "eina_ustrbuf.h" + +/*============================================================================* + * Local * + *============================================================================*/ + +/** + * @cond LOCAL + */ + +#ifdef _STRBUF_DATA_TYPE +# undef _STRBUF_DATA_TYPE +#endif + +#ifdef _STRBUF_CSIZE +# undef _STRBUF_CSIZE +#endif + +#ifdef _STRBUF_STRUCT_NAME +# undef _STRBUF_STRUCT_NAME +#endif + +#ifdef _STRBUF_STRLEN_FUNC +# undef _STRBUF_STRLEN_FUNC +#endif + +#ifdef _STRBUF_STRESCAPE_FUNC +# undef _STRBUF_STRESCAPE_FUNC +#endif + +#ifdef _STRBUF_MAGIC +# undef _STRBUF_MAGIC +#endif + +#ifdef _STRBUF_MAGIC_STR +# undef _STRBUF_MAGIC_STR +#endif + +#ifdef _FUNC_EXPAND +# undef _FUNC_EXPAND +#endif + +#define _STRBUF_DATA_TYPE Eina_Unicode +#define _STRBUF_CSIZE sizeof(_STRBUF_DATA_TYPE) +#define _STRBUF_STRUCT_NAME Eina_UStrbuf +#define _STRBUF_STRLEN_FUNC(x) eina_unicode_strlen(x) +#define _STRBUF_STRESCAPE_FUNC(x) eina_unicode_escape(x) +#define _STRBUF_MAGIC EINA_MAGIC_USTRBUF +#define _STRBUF_MAGIC_STR __USTRBUF_MAGIC_STR +static const char __USTRBUF_MAGIC_STR[] = "Eina UStrbuf"; + +#define _FUNC_EXPAND(y) eina_ustrbuf_ ## y + +/** + * @endcond + */ + + +/*============================================================================* + * Global * + *============================================================================*/ + + +/*============================================================================* + * API * + *============================================================================*/ + +/** + * @addtogroup Eina_Unicode_String_Buffer_Group Unicode String Buffer + * + * @brief These functions provide unicode string buffers management. + * + * The Unicode String Buffer data type is designed to be a mutable string, + * allowing to append, prepend or insert a string to a buffer. + * + * @{ + */ + +#include "eina_strbuf_template_c.x" + +/** + * @} + */ diff --git a/tests/suite/ecore/src/lib/eina_ustringshare.c b/tests/suite/ecore/src/lib/eina_ustringshare.c new file mode 100644 index 0000000000..8fe18c8968 --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_ustringshare.c @@ -0,0 +1,243 @@ +/* EINA - EFL data type library + * Copyright (C) 2002-2008 Carsten Haitzler, + * Jorge Luis Zapata Muga, + * Cedric Bail, + * Gustavo Sverzut Barbieri + * Tom Hacohen + * + * 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 <http://www.gnu.org/licenses/>. + + */ +/** + * @page tutorial_ustringshare_page UStringshare Tutorial + * + * to be written... + * + */ + +#include "eina_share_common.h" +#include "eina_unicode.h" +#include "eina_private.h" +#include "eina_ustringshare.h" + +/* The actual share */ +static Eina_Share *ustringshare_share; +static const char EINA_MAGIC_USTRINGSHARE_NODE_STR[] = "Eina UStringshare Node"; + +/*============================================================================* +* Global * +*============================================================================*/ + +/** + * @internal + * @brief Initialize the share_common module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the share_common module of Eina. It is called by + * eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_ustringshare_init(void) +{ + return eina_share_common_init(&ustringshare_share, + EINA_MAGIC_USTRINGSHARE_NODE, + EINA_MAGIC_USTRINGSHARE_NODE_STR); +} + +/** + * @internal + * @brief Shut down the share_common module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the share_common module set up by + * eina_share_common_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_ustringshare_shutdown(void) +{ + Eina_Bool ret; + ret = eina_share_common_shutdown(&ustringshare_share); + return ret; +} + +/*============================================================================* +* API * +*============================================================================*/ +/** + * @addtogroup Eina_UStringshare_Group Unicode Stringshare + * + * These functions allow you to store one copy of a string, and use it + * throughout your program. + * + * This is a method to reduce the number of duplicated strings kept in + * memory. It's pretty common for the same strings to be dynamically + * allocated repeatedly between applications and libraries, especially in + * circumstances where you could have multiple copies of a structure that + * allocates the string. So rather than duplicating and freeing these + * strings, you request a read-only pointer to an existing string and + * only incur the overhead of a hash lookup. + * + * It sounds like micro-optimizing, but profiling has shown this can have + * a significant impact as you scale the number of copies up. It improves + * string creation/destruction speed, reduces memory use and decreases + * memory fragmentation, so a win all-around. + * + * For more information, you can look at the @ref tutorial_ustringshare_page. + * + * @{ + */ + +/** + * @brief Note that the given string has lost an instance. + * + * @param str string The given string. + * + * This function decreases the reference counter associated to @p str + * if it exists. If that counter reaches 0, the memory associated to + * @p str is freed. If @p str is NULL, the function returns + * immediately. + * + * Note that if the given pointer is not shared or NULL, bad things + * will happen, likely a segmentation fault. + */ +EAPI void +eina_ustringshare_del(const Eina_Unicode *str) +{ + if (!str) + return; + + eina_share_common_del(ustringshare_share,(const char *)str); +} + +/** + * @brief Retrieve an instance of a string for use in a program. + * + * @param str The string to retrieve an instance of. + * @param slen The string size (<= strlen(str)). + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This function retrieves an instance of @p str. If @p str is + * @c NULL, then @c NULL is returned. If @p str is already stored, it + * is just returned and its reference counter is increased. Otherwise + * it is added to the strings to be searched and a duplicated string + * of @p str is returned. + * + * This function does not check string size, but uses the + * exact given size. This can be used to share_common part of a larger + * buffer or substring. + * + * @see eina_ustringshare_add() + */ +EAPI const Eina_Unicode * +eina_ustringshare_add_length(const Eina_Unicode *str, unsigned int slen) +{ + return (const Eina_Unicode *)eina_share_common_add_length(ustringshare_share, + (const char *)str, + slen * + sizeof( + Eina_Unicode), + sizeof( + Eina_Unicode)); +} + +/** + * @brief Retrieve an instance of a string for use in a program. + * + * @param str The NULL terminated string to retrieve an instance of. + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This function retrieves an instance of @p str. If @p str is + * @c NULL, then @c NULL is returned. If @p str is already stored, it + * is just returned and its reference counter is increased. Otherwise + * it is added to the strings to be searched and a duplicated string + * of @p str is returned. + * + * The string @p str must be NULL terminated ('@\0') and its full + * length will be used. To use part of the string or non-null + * terminated, use eina_stringshare_add_length() instead. + * + * @see eina_ustringshare_add_length() + */ +EAPI const Eina_Unicode * +eina_ustringshare_add(const Eina_Unicode *str) +{ + int slen = (str) ? (int)eina_unicode_strlen(str) : -1; + return eina_ustringshare_add_length(str, slen); +} + +/** + * Increment references of the given shared string. + * + * @param str The shared string. + * @return A pointer to an instance of the string on success. + * @c NULL on failure. + * + * This is similar to eina_share_common_add(), but it's faster since it will + * avoid lookups if possible, but on the down side it requires the parameter + * to be shared before, in other words, it must be the return of a previous + * eina_ustringshare_add(). + * + * There is no unref since this is the work of eina_ustringshare_del(). + */ +EAPI const Eina_Unicode * +eina_ustringshare_ref(const Eina_Unicode *str) +{ + return (const Eina_Unicode *)eina_share_common_ref(ustringshare_share, + (const char *)str); +} + +/** + * @brief Note that the given string @b must be shared. + * + * @param str the shared string to know the length. It is safe to + * give NULL, in that case -1 is returned. + * + * This function is a cheap way to known the length of a shared + * string. Note that if the given pointer is not shared, bad + * things will happen, likely a segmentation fault. If in doubt, try + * strlen(). + */ +EAPI int +eina_ustringshare_strlen(const Eina_Unicode *str) +{ + int len = eina_share_common_length(ustringshare_share, (const char *)str); + len = (len > 0) ? len / (int)sizeof(Eina_Unicode) : -1; + return len; +} + +/** + * @brief Dump the contents of the share_common. + * + * This function dumps all strings in the share_common to stdout with a + * DDD: prefix per line and a memory usage summary. + */ +EAPI void +eina_ustringshare_dump(void) +{ + eina_share_common_dump(ustringshare_share, NULL, 0); +} + +/** + * @} + */ + diff --git a/tests/suite/ecore/src/lib/eina_value.c b/tests/suite/ecore/src/lib/eina_value.c new file mode 100644 index 0000000000..554f907d6d --- /dev/null +++ b/tests/suite/ecore/src/lib/eina_value.c @@ -0,0 +1,47 @@ +/* eina_value.c + + Copyright (C) 2001 Christopher Rosendahl <smugg@fatelabs.com> + Nathan Ingersoll <ningerso@d.umn.edu> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies of the Software and its documentation and acknowledgment shall be + given in the documentation and software packages that this Software was + used. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "eina_config.h" +#include "eina_private.h" + +/*============================================================================* +* Global * +*============================================================================*/ + +/*============================================================================* +* API * +*============================================================================*/ + +EAPI const unsigned int eina_prime_table[] = +{ + 17, 31, 61, 127, 257, 509, 1021, + 2053, 4093, 8191, 16381, 32771, 65537, 131071, 262147, 524287, 1048573, + 2097143, 4194301, 8388617, 16777213 +}; diff --git a/tests/suite/mini-eagain2.c b/tests/suite/mini-eagain2.c new file mode 100644 index 0000000000..284fdc9aee --- /dev/null +++ b/tests/suite/mini-eagain2.c @@ -0,0 +1,205 @@ +#include <Ecore.h> +#include <fcntl.h> +#include <netinet/tcp.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gnutls/gnutls.h> + +/* Ecore_Fd_Handler example + * 2010 Mike Blumenkrantz + * compile with gcc $(pkgconfig --cflags --libs gnutls ecore) + */ + + +#define print(...) fprintf(stderr, "line %i: ", __LINE__); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n") + +static int done = 0; + +static void +tls_log_func (int level, const char *str) +{ + fprintf(stderr, "|<%d>| %s", level, str); +} + +static const char* +SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_description_t status) +{ + switch (status) + { + case GNUTLS_HANDSHAKE_HELLO_REQUEST: + return "Hello request"; + case GNUTLS_HANDSHAKE_CLIENT_HELLO: + return "Client hello"; + case GNUTLS_HANDSHAKE_SERVER_HELLO: + return "Server hello"; + case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: + return "Certificate packet"; + case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: + return "Server key exchange"; + case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: + return "Certificate request"; + case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE: + return "Server hello done"; + case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: + return "Certificate verify"; + case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE: + return "Client key exchange"; + case GNUTLS_HANDSHAKE_FINISHED: + return "Finished"; + case GNUTLS_HANDSHAKE_SUPPLEMENTAL: + return "Supplemental"; + default: + return NULL; + } + return NULL; +} + +/* Connects to the peer and returns a socket + * descriptor. + */ +static int +tcp_connect (void) +{ + const char *PORT = "4445"; + const char *SERVER = "127.0.0.1"; //verisign.com + int err, sd; + int flag = 1, curstate = 0; + struct sockaddr_in sa; + + /* sets some fd options such as nonblock */ + sd = socket (AF_INET, SOCK_STREAM, 0); + fcntl(sd, F_SETFL, O_NONBLOCK); + fcntl(sd, F_SETFD, FD_CLOEXEC); + setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate, sizeof(curstate)); + + setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); + + memset (&sa, '\0', sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (atoi (PORT)); + inet_pton (AF_INET, SERVER, &sa.sin_addr); + + /* connects to server + */ + err = connect (sd, (struct sockaddr *) &sa, sizeof (sa)); + if ((err < 0) && (errno != EINPROGRESS)) + { + print("Connect error\n"); + exit (1); + } + + return sd; +} + +/* closes the given socket descriptor. + */ +static void +tcp_close (int sd) +{ + shutdown (sd, SHUT_RDWR); /* no more receptions */ + close (sd); +} + +static Eina_Bool +_process_data(gnutls_session_t client, Ecore_Fd_Handler *fd_handler) +{ + static int ret, lastret; + static unsigned int count = 0; + + if (!done) + { + lastret = ret; + ret = gnutls_handshake (client); + count++; + if (gnutls_record_get_direction(client)) + ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_WRITE); + else + ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_READ); + /* avoid printing messages infinity times */ + if (lastret != ret && ret != 0 && ret != GNUTLS_E_AGAIN) + { + print("gnutls returned with: %s - %s", gnutls_strerror_name(ret), gnutls_strerror(ret)); + if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)) + print("Also received alert: %s", gnutls_alert_get_name(gnutls_alert_get(client))); + print("last out: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_out(client))); + print("last in: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_in(client))); + } + + if (gnutls_error_is_fatal(ret)) + { + print("yarrr this be an error!"); + exit(1); + } + + } + if (ret == GNUTLS_E_SUCCESS) + { + done = 1; + //print("Handshake successful in %u handshake calls!", count); + ecore_main_loop_quit(); + } + + return ECORE_CALLBACK_RENEW; +} + +int +main (void) +{ + /* credentials */ + gnutls_anon_client_credentials_t c_anoncred; + gnutls_certificate_credentials_t c_certcred; + + gnutls_session_t client; + int sd, i; + + /* General init. */ + gnutls_global_init (); + ecore_init(); +// gnutls_global_set_log_function (tls_log_func); +// gnutls_global_set_log_level (2); + + /* Init client */ + gnutls_anon_allocate_client_credentials (&c_anoncred); + gnutls_certificate_allocate_credentials (&c_certcred); + + + for (i=0;i<5;i++) + { + + gnutls_init (&client, GNUTLS_CLIENT); + /* set very specific priorities */ + gnutls_priority_set_direct(client, "NORMAL:+ANON-DH", NULL); + gnutls_credentials_set (client, GNUTLS_CRD_ANON, c_anoncred); + gnutls_credentials_set (client, GNUTLS_CRD_CERTIFICATE, c_certcred); + gnutls_server_name_set(client, GNUTLS_NAME_DNS, "localhost", strlen("localhost")); + + /* connect to the peer + */ + sd = tcp_connect (); + + /* associate gnutls with socket */ + gnutls_transport_set_ptr (client, (gnutls_transport_ptr_t) sd); + /* add a callback for data being available for send/receive on socket */ + if (!ecore_main_fd_handler_add(sd, ECORE_FD_READ | ECORE_FD_WRITE, (Ecore_Fd_Cb)_process_data, client, NULL, NULL)) + { + print("could not create fd handler!"); + exit(1); + } + /* begin main loop */ + ecore_main_loop_begin(); + + gnutls_bye (client, GNUTLS_SHUT_RDWR); + + gnutls_deinit (client); + + tcp_close (sd); + } + + return 0; +} diff --git a/tests/safe-renegotiation/params.dh b/tests/suite/params.dh index 51185ec3bf..51185ec3bf 100644 --- a/tests/safe-renegotiation/params.dh +++ b/tests/suite/params.dh diff --git a/tests/safe-renegotiation/testsrn b/tests/suite/testsrn index 8df797c6e6..8df797c6e6 100755 --- a/tests/safe-renegotiation/testsrn +++ b/tests/suite/testsrn |