From bc2638334bca11e79b24ad1e44df9af09acbd0f7 Mon Sep 17 00:00:00 2001
From: Moises Silva <moy@sangoma.com>
Date: Fri, 10 Sep 2010 14:01:52 -0400
Subject: [PATCH] freetdm: iterators refactoring          add channel iterator

---
 libs/freetdm/mod_freetdm/mod_freetdm.c        |  19 +--
 libs/freetdm/src/ftdm_io.c                    | 116 ++++++++++++++++--
 libs/freetdm/src/include/freetdm.h            |  28 ++++-
 libs/freetdm/src/include/private/ftdm_core.h  |   3 +
 libs/freetdm/src/include/private/ftdm_types.h |  17 +++
 5 files changed, 161 insertions(+), 22 deletions(-)

diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c
index b911155334..6b4fec6d87 100755
--- a/libs/freetdm/mod_freetdm/mod_freetdm.c
+++ b/libs/freetdm/mod_freetdm/mod_freetdm.c
@@ -1404,6 +1404,7 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session
 	private_t *tech_pvt = NULL;
 	switch_channel_t *channel = NULL;
 	ftdm_iterator_t *iter = NULL;
+	ftdm_iterator_t *curr = NULL;
 	const char *var_name = NULL;
 	const char *var_value = NULL;
 	uint32_t spanid, chanid;
@@ -1516,12 +1517,13 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session
 	}
 	/* Add any channel variable to the dial plan */
 	iter = ftdm_channel_get_var_iterator(sigmsg->channel);
-	for ( ; iter; iter = ftdm_iterator_next(iter)) {
-		ftdm_channel_get_current_var(iter, &var_name, &var_value);
+	for (curr = iter ; curr; curr = ftdm_iterator_next(curr)) {
+		ftdm_channel_get_current_var(curr, &var_name, &var_value);
 		snprintf(name, sizeof(name), FREETDM_VAR_PREFIX "%s", var_name);
 		switch_channel_set_variable_printf(channel, name, "%s", var_value);
 	}
-		
+	ftdm_iterator_free(iter);
+
 	switch_channel_set_state(channel, CS_INIT);
 	if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) {
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error spawning thread\n");
@@ -2496,7 +2498,8 @@ static switch_status_t load_config(void)
 	unsigned boosti = 0;
 	unsigned int i = 0;
 	ftdm_channel_t *fchan = NULL;
-	unsigned int chancount = 0;
+	ftdm_iterator_t *chaniter = NULL;
+	ftdm_iterator_t *curr = NULL;
 
 	memset(boost_spans, 0, sizeof(boost_spans));
 	memset(&globals, 0, sizeof(globals));
@@ -2780,11 +2783,13 @@ static switch_status_t load_config(void)
 			switch_set_string(SPAN_CONFIG[span_id].dialplan, dialplan);
 			SPAN_CONFIG[span_id].analog_options = analog_options | globals.analog_options;
 			
-			chancount = ftdm_span_get_chan_count(span);
-			for (i = 1; i <= chancount; i++) {
-				fchan = ftdm_span_get_channel(span, i);
+			chaniter = ftdm_span_get_chan_iterator(span);
+			curr = chaniter
+			for (curr = chaniter ; curr; curr = ftdm_iterator_next(curr)) {
+				fchan = ftdm_iterator_current(curr);
 				ftdm_channel_set_private(fchan, &SPAN_CONFIG[span_id].pvts[i]);
 			}
+			ftdm_iterator_free(chaniter);
 			
 			if (dial_regex) {
 				switch_set_string(SPAN_CONFIG[span_id].dial_regex, dial_regex);
diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c
index b275403c85..b83f1b7e88 100644
--- a/libs/freetdm/src/ftdm_io.c
+++ b/libs/freetdm/src/ftdm_io.c
@@ -3524,16 +3524,48 @@ done:
 	return var;
 }
 
-FT_DECLARE(ftdm_iterator_t *) ftdm_channel_get_var_iterator(const ftdm_channel_t *ftdmchan)
+static ftdm_iterator_t *get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter)
 {
-	ftdm_hash_iterator_t *iter = NULL;
+	int allocated = 0;
+	if (iter) {
+		if (iter->type != type) {
+			ftdm_log(FTDM_LOG_ERROR, "Cannot switch iterator types\n");
+			return NULL;
+		}
+		allocated = iter->allocated;
+		memset(iter, 0, sizeof(*iter));
+		iter->type = type;
+		iter->allocated = allocated;
+		return iter;
+	}
 
+	iter = ftdm_calloc(1, sizeof(*iter));
+	if (!iter) {
+		return NULL;
+	}
+	iter->type = type;
+	iter->allocated = 1;
+	return iter;
+}
+
+FT_DECLARE(ftdm_iterator_t *) ftdm_channel_get_var_iterator(const ftdm_channel_t *ftdmchan, ftdm_iterator_t *iter)
+{
+	if (!(iter = get_iterator(FTDM_ITERATOR_VARS, iter))) {
+		return NULL;
+	}
 	ftdm_channel_lock(ftdmchan);
-
-	iter = ftdmchan->variable_hash == NULL ? NULL : hashtable_first(ftdmchan->variable_hash);
-
+	iter->pvt.hashiter = ftdmchan->variable_hash == NULL ? NULL : hashtable_first(ftdmchan->variable_hash);
 	ftdm_channel_unlock(ftdmchan);
+	return iter;
+}
 
+FT_DECLARE(ftdm_iterator_t *) ftdm_span_get_chan_iterator(const ftdm_span_t *span, ftdm_iterator_t *iter)
+{
+	if (!(iter = get_iterator(FTDM_ITERATOR_CHANS, iter))) {
+		return NULL;
+	}
+	iter->pvt.chaniter.index = 1;
+	iter->pvt.chaniter.span = span;
 	return iter;
 }
 
@@ -3545,11 +3577,9 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_get_current_var(ftdm_iterator_t *iter, co
 	*var_name = NULL;
 	*var_val = NULL;
 
-	if (!iter) {
-		return FTDM_FAIL;
-	}
+	ftdm_assert_return(iter && (iter->type == FTDM_ITERATOR_CHANS) && iter->pvt.hashiter, FTDM_FAIL, "Cannot get variable from invalid iterator!\n");
 
-	hashtable_this(iter, &key, NULL, &val);
+	hashtable_this(iter->pvt.hashiter, &key, NULL, &val);
 
 	*var_name = key;
 	*var_val = val;
@@ -3559,10 +3589,72 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_get_current_var(ftdm_iterator_t *iter, co
 
 FT_DECLARE(ftdm_iterator_t *) ftdm_iterator_next(ftdm_iterator_t *iter)
 {
-	if (!iter) {
-		return NULL;
+	ftdm_assert_return(iter && iter->type, NULL, "Invalid iterator\n");
+
+	switch (iter->type) {
+	case FTDM_ITERATOR_VARS:
+		if (!iter->pvt.hashiter) {
+			return NULL;
+		}
+		iter->pvt.hashiter = hashtable_next(iter->pvt.hashiter);
+		if (!iter->pvt.hashiter) {
+			return NULL;
+		}
+		return iter;
+	case FTDM_ITERATOR_CHANS:
+		if (iter->pvt.chaniter.index == iter->pvt.chaniter.span->chan_count) {
+			return NULL;
+		}
+		iter->pvt.chaniter.index++;
+		return iter;
+	default:
+		break;
 	}
-	return hashtable_next(iter);
+
+	ftdm_assert_return(0, NULL, "Unknown iterator type\n");
+	return NULL;
+}
+
+FT_DECLARE(void *) ftdm_iterator_current(ftdm_iterator_t *iter)
+{
+	const void *key = NULL;
+	void *val = NULL;
+
+	ftdm_assert_return(iter && iter->type, NULL, "Invalid iterator\n");
+
+	switch (iter->type) {
+	case FTDM_ITERATOR_VARS:
+		hashtable_this(iter->pvt.hashiter, &key, NULL, &val);
+		/* I decided to return the key instead of the value since the value can be retrieved using the key */
+		return (void *)key;
+	case FTDM_ITERATOR_CHANS:
+		ftdm_assert_return(iter->pvt.chaniter.index, NULL, "channel iterator index cannot be zero!\n");
+		ftdm_assert_return(iter->pvt.chaniter.index > iter->pvt.chaniter.span->chan_count, NULL, "channel iterator index bigger than span chan count!\n");
+		return iter->pvt.chaniter.span->channels[iter->pvt.chaniter.index];
+	default:
+		break;
+	}
+
+	ftdm_assert_return(0, NULL, "Unknown iterator type\n");
+	return NULL;
+}
+
+FT_DECLARE(ftdm_status_t) ftdm_iterator_free(ftdm_iterator_t *iter)
+{
+	/* it's valid to pass a NULL iterator, do not return failure  */
+	if (!iter) {
+		return FTDM_SUCCESS;
+	}
+
+	if (!iter->allocated) {
+		memset(iter, 0, sizeof(*iter));
+		return FTDM_SUCCESS;
+	}
+
+	ftdm_assert_return(iter->type, FTDM_FAIL, "Cannot free invalid iterator\n");
+	ftdm_safe_free(iter);
+
+	return FTDM_SUCCESS;
 }
 
 static struct {
diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h
index 0437e02e51..1f50a2ccaa 100644
--- a/libs/freetdm/src/include/freetdm.h
+++ b/libs/freetdm/src/include/freetdm.h
@@ -386,7 +386,7 @@ typedef struct ftdm_conf_parameter {
 } ftdm_conf_parameter_t;
 
 /*! \brief Opaque general purpose iterator */
-typedef void ftdm_iterator_t;
+typedef struct ftdm_iterator ftdm_iterator_t;
 
 /*! \brief Channel commands that can be executed through ftdm_channel_command() */
 typedef enum {
@@ -1032,8 +1032,19 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_add_var(ftdm_channel_t *ftdmchan, const c
 FT_DECLARE(const char *) ftdm_channel_get_var(ftdm_channel_t *ftdmchan, const char *var_name);
 
 /*! \brief Get an iterator to iterate over the channel variables
- *  \note The iterator pointer returned is only valid while the channel is open and it'll be destroyed when the channel is closed. */
-FT_DECLARE(ftdm_iterator_t *) ftdm_channel_get_var_iterator(const ftdm_channel_t *ftdmchan);
+ *  \param ftdmchan The channel structure containing the variables
+ *  \param iter Optional iterator. You can reuse an old iterator (not previously freed) to avoid the extra allocation of a new iterator.
+ *  \note The iterator pointer returned is only valid while the channel is open and it'll be destroyed when the channel is closed. 
+ *        This iterator is completely non-thread safe, if you are adding variables or removing variables while iterating 
+ *        results are unpredictable
+ */
+FT_DECLARE(ftdm_iterator_t *) ftdm_channel_get_var_iterator(const ftdm_channel_t *ftdmchan, ftdm_iterator_t *iter);
+
+/*! \brief Get iterator current value (depends on the iterator type)
+ *  \note Channel iterators return a pointer to ftdm_channel_t
+ *        Variable iterators return a pointer to the variable name (not the variable value)
+ */
+FT_DECLARE(void *) ftdm_iterator_current(ftdm_iterator_t *iter);
 
 /*! \brief Get variable name and value for the current iterator position */
 FT_DECLARE(ftdm_status_t) ftdm_channel_get_current_var(ftdm_iterator_t *iter, const char **var_name, const char **var_val);
@@ -1041,6 +1052,11 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_get_current_var(ftdm_iterator_t *iter, co
 /*! \brief Advance iterator */
 FT_DECLARE(ftdm_iterator_t *) ftdm_iterator_next(ftdm_iterator_t *iter);
 
+/*! \brief Free iterator 
+ *  \note You must free an iterator after using it unless you plan to reuse it
+ */
+FT_DECLARE(ftdm_status_t) ftdm_iterator_free(ftdm_iterator_t *iter);
+
 /*! \brief Get the span pointer associated to the channel */
 FT_DECLARE(ftdm_span_t *) ftdm_channel_get_span(const ftdm_channel_t *ftdmchan);
 
@@ -1144,6 +1160,12 @@ FT_DECLARE(uint32_t) ftdm_span_get_id(const ftdm_span_t *span);
 /*! \brief Get the span name */
 FT_DECLARE(const char *) ftdm_span_get_name(const ftdm_span_t *span);
 
+/*! \brief Get iterator for the span channels
+ *  \param span The span containing the channels
+ *  \param iter Optional iterator. You can reuse an old iterator (not previously freed) to avoid the extra allocation of a new iterator.
+ */
+FT_DECLARE(ftdm_iterator_t *) ftdm_span_get_chan_iterator(const ftdm_span_t *span, ftdm_iterator_t *iter);
+
 /*! 
  * \brief Execute a text command. The text command output will be returned and must be free'd 
  *
diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h
index 74bea8148c..d8947f6de4 100644
--- a/libs/freetdm/src/include/private/ftdm_core.h
+++ b/libs/freetdm/src/include/private/ftdm_core.h
@@ -619,6 +619,9 @@ FT_DECLARE(ftdm_status_t) ftdm_span_trigger_signals(const ftdm_span_t *span);
 #define ftdm_log_chan(fchan, level, format, ...) ftdm_log(level, "[s%dc%d][%d:%d] " format, fchan->span_id, fchan->chan_id, fchan->physical_span_id, fchan->physical_chan_id, __VA_ARGS__)
 #define ftdm_log_chan_msg(fchan, level, msg) ftdm_log(level, "[s%dc%d][%d:%d] " msg, fchan->span_id, fchan->chan_id, fchan->physical_span_id, fchan->physical_chan_id)
 
+#define ftdm_span_lock(span) ftdm_mutex_lock(span->mutex)
+#define ftdm_span_unlock(span) ftdm_mutex_unlock(span->mutex)
+
 FT_DECLARE_DATA extern const char *FTDM_LEVEL_NAMES[9];
 
 static __inline__ void ftdm_abort(void)
diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h
index d8e8b5c2e6..ba6956344d 100644
--- a/libs/freetdm/src/include/private/ftdm_types.h
+++ b/libs/freetdm/src/include/private/ftdm_types.h
@@ -366,6 +366,23 @@ typedef ftdm_status_t (*ftdm_span_start_t)(ftdm_span_t *span);
 typedef ftdm_status_t (*ftdm_span_stop_t)(ftdm_span_t *span);
 typedef ftdm_status_t (*ftdm_channel_sig_read_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size);
 
+typedef enum {
+	FTDM_ITERATOR_VARS = 1,
+	FTDM_ITERATOR_CHANS, 
+} ftdm_iterator_type_t;
+
+struct ftdm_iterator {
+	ftdm_iterator_type_t type;
+	unsigned int allocated:1;
+	union {
+		struct {
+			int32_t index;
+			const ftdm_span_t *span;
+		} chaniter;
+		ftdm_hash_iterator_t *hashiter;
+	} pvt;
+};
+
 #ifdef __cplusplus
 }
 #endif