diff --git a/Makefile.am b/Makefile.am index fe74366..70a50d0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,6 +24,7 @@ pkginclude_HEADERS = \ include/igloo/io.h \ include/igloo/stdio.h \ include/igloo/filter.h \ + include/igloo/objecthandler.h \ include/igloo/buffer.h \ include/igloo/list.h \ include/igloo/reportxml.h @@ -35,6 +36,7 @@ libigloo_la_SOURCES = \ src/io.c \ src/stdio.c \ src/filter.c \ + src/objecthandler.c \ src/buffer.c \ src/list.c \ src/reportxml.c diff --git a/include/igloo/objecthandler.h b/include/igloo/objecthandler.h new file mode 100644 index 0000000..df02618 --- /dev/null +++ b/include/igloo/objecthandler.h @@ -0,0 +1,88 @@ +/* Copyright (C) 2019 Philipp "ph3-der-loewe" Schafft + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _LIBIGLOO__OBJECTHANDLER_H_ +#define _LIBIGLOO__OBJECTHANDLER_H_ +/** + * @file + * Put a good description of this file here + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ro.h" +#include "interface.h" +#include "filter.h" + +/* About thread safety: + * This set of functions is thread safe. + */ + +igloo_RO_FORWARD_TYPE(igloo_objecthandler_t); + +/* Interface description */ +typedef struct { + igloo_interface_base_ifdesc_t __base; + + /* Whether the this backend is thread safe. */ + int is_thread_safe; + + /* Perform the actual test on the object. */ + igloo_filter_result_t (*handle)(igloo_INTERFACE_BASIC_ARGS, igloo_ro_t object); +} igloo_objecthandler_ifdesc_t; + +/* This creates a new objecthandler from a interface description and state. + * Parameters: + * ifdesc + * The interface description to use. + * backend_object + * A object used by the backend or igloo_RO_NULL. + * backend_userdata + * A userdata pointer used by the backend or NULL. + * name, associated + * See refobject_new(). + */ +igloo_objecthandler_t * igloo_objecthandler_new(const igloo_objecthandler_ifdesc_t *ifdesc, igloo_ro_t backend_object, void *backend_userdata, const char *name, igloo_ro_t associated); + +/* This handles a object according to the filter. + * Parameters: + * handler + * The handler to use. + * object + * The object to test. + * Returns: + * Whether the object was accepted by the handler or not. + */ +igloo_filter_result_t igloo_objecthandler_handle(igloo_objecthandler_t *handler, igloo_ro_t object); + +/* This adds a filter to the handler. + * Parameters: + * handler + * The handler to add the filter to. + * filter + * The filter to add. + */ +int igloo_objecthandler_push_filter(igloo_objecthandler_t *handler, igloo_filter_t *filter); + +#ifdef __cplusplus +} +#endif + +#endif /* ! _LIBIGLOO__OBJECTHANDLER_H_ */ diff --git a/include/igloo/types.h b/include/igloo/types.h index 2e487c7..2df5e6b 100644 --- a/include/igloo/types.h +++ b/include/igloo/types.h @@ -34,6 +34,7 @@ extern "C" { typedef struct igloo_io_tag igloo_io_t; typedef struct igloo_filter_tag igloo_filter_t; +typedef struct igloo_objecthandler_tag igloo_objecthandler_t; typedef struct igloo_buffer_tag igloo_buffer_t; typedef struct igloo_list_tag igloo_list_t; @@ -54,6 +55,7 @@ typedef union __attribute__ ((__transparent_union__)) { igloo_RO_TYPE(igloo_ro_base_t) igloo_RO_TYPE(igloo_io_t) igloo_RO_TYPE(igloo_filter_t) + igloo_RO_TYPE(igloo_objecthandler_t) igloo_RO_TYPE(igloo_buffer_t) igloo_RO_TYPE(igloo_list_t) igloo_RO_TYPE(igloo_reportxml_t) diff --git a/src/objecthandler.c b/src/objecthandler.c new file mode 100644 index 0000000..325d0d8 --- /dev/null +++ b/src/objecthandler.c @@ -0,0 +1,166 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2019, Philipp "ph3-der-loewe" Schafft , + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include "private.h" + +struct igloo_objecthandler_tag { + igloo_interface_base(objecthandler) + igloo_rwlock_t rwlock; + + /* filters */ + igloo_filter_t *filter_a; + igloo_filter_t *filter_b; + igloo_list_t *filter_list; +}; + +static void __free(igloo_ro_t self) +{ + igloo_objecthandler_t *handler = igloo_RO_TO_TYPE(self, igloo_objecthandler_t); + + igloo_thread_rwlock_wlock(&(handler->rwlock)); + igloo_thread_rwlock_unlock(&(handler->rwlock)); + igloo_thread_rwlock_destroy(&(handler->rwlock)); + + igloo_interface_base_free(self); +} + +igloo_RO_PUBLIC_TYPE(igloo_objecthandler_t, + igloo_RO_TYPEDECL_FREE(__free) + ); + +igloo_objecthandler_t * igloo_objecthandler_new(const igloo_objecthandler_ifdesc_t *ifdesc, igloo_ro_t backend_object, void *backend_userdata, const char *name, igloo_ro_t associated) +{ + igloo_objecthandler_t *handler = igloo_interface_base_new(igloo_objecthandler_t, ifdesc, backend_object, backend_userdata, name, associated); + + if (!handler) + return NULL; + + igloo_thread_rwlock_create(&(handler->rwlock)); + + return handler; +} + +igloo_filter_result_t igloo_objecthandler_handle(igloo_objecthandler_t *handler, igloo_ro_t object) +{ + igloo_filter_result_t result = igloo_FILTER_RESULT_PASS; + int require_wlock = 0; + + if (!igloo_RO_IS_VALID(handler, igloo_objecthandler_t)) + return igloo_FILTER_RESULT_ERROR; + + if (!handler->ifdesc->handle) + return igloo_FILTER_RESULT_ERROR; + + /* Accessing handler->ifdesc->is_thread_safe is thread-safe. handler->filter_list is not, so we will recheck that later. */ + if (!handler->ifdesc->is_thread_safe || handler->filter_list) + require_wlock = 1; + + if (require_wlock) { + igloo_thread_rwlock_wlock(&(handler->rwlock)); + } else { + igloo_thread_rwlock_rlock(&(handler->rwlock)); + + /* now re-check if we got a handler->filter_list now... If so, we re-lock. + * This is no problem as we can run with wlock anyway and handler->filter_list may not + * be reset to NULL as well. + */ + + if (handler->filter_list) { + igloo_thread_rwlock_unlock(&(handler->rwlock)); + require_wlock = 1; + igloo_thread_rwlock_wlock(&(handler->rwlock)); + } + } + + /* Ok, now we are locked. Let's process input! */ + + if (handler->filter_list) { + igloo_list_iterator_storage_t iterator_storage; + igloo_list_iterator_t *iterator = igloo_list_iterator_start(handler->filter_list, &iterator_storage, sizeof(iterator_storage)); + igloo_filter_t *filter; + + for (; !igloo_RO_IS_NULL(filter = igloo_RO_TO_TYPE(igloo_list_iterator_next(iterator), igloo_filter_t)); ) { + result = igloo_filter_test(filter, object); + igloo_ro_unref(filter); + if (result != igloo_FILTER_RESULT_PASS) { + igloo_list_iterator_end(iterator); + igloo_thread_rwlock_unlock(&(handler->rwlock)); + return result; + } + } + igloo_list_iterator_end(iterator); + } else { + if (handler->filter_a) + result = igloo_filter_test(handler->filter_a, object); + + if (result == igloo_FILTER_RESULT_PASS && handler->filter_b) + result = igloo_filter_test(handler->filter_b, object); + + if (result != igloo_FILTER_RESULT_PASS) { + igloo_thread_rwlock_unlock(&(handler->rwlock)); + return result; + } + } + + result = handler->ifdesc->handle(igloo_INTERFACE_BASIC_CALL(handler), object); + + igloo_thread_rwlock_unlock(&(handler->rwlock)); + + return result; +} + +int igloo_objecthandler_push_filter(igloo_objecthandler_t *handler, igloo_filter_t *filter) +{ + int ret = -1; + + if (!igloo_RO_IS_VALID(handler, igloo_objecthandler_t) || !igloo_RO_IS_VALID(filter, igloo_filter_t)) + return -1; + + igloo_thread_rwlock_wlock(&(handler->rwlock)); + if (!handler->filter_list && handler->filter_a && handler->filter_b) { + handler->filter_list = igloo_ro_new(igloo_list_t); + if (!handler->filter_list) { + igloo_thread_rwlock_unlock(&(handler->rwlock)); + return -1; + } + + igloo_list_set_type(handler->filter_list, igloo_filter_t); + igloo_list_preallocate(handler->filter_list, 3); + + igloo_list_push(handler->filter_list, handler->filter_a); + igloo_ro_unref(handler->filter_a); + handler->filter_a = NULL; + igloo_list_push(handler->filter_list, handler->filter_b); + igloo_ro_unref(handler->filter_b); + handler->filter_b = NULL; + } + + if (handler->filter_list) { + ret = igloo_list_push(handler->filter_list, filter); + } else { + ret = igloo_ro_ref(filter); + if (ret == 0) { + if (!handler->filter_a) { + handler->filter_a = filter; + } else { + handler->filter_b = filter; + } + } + } + igloo_thread_rwlock_unlock(&(handler->rwlock)); + + return ret; +}