MEDIUM: pattern: merge same pattern

Sometimes the same pattern file is used with the same index, parse and
parse_smp functions. If this two condition are true, these two pattern
are identical and the same struct can be used.
diff --git a/include/types/pattern.h b/include/types/pattern.h
index d19bcc1..7973bce 100644
--- a/include/types/pattern.h
+++ b/include/types/pattern.h
@@ -178,15 +178,28 @@
  * are grouped together in order to optimize caching.
  */
 struct pattern_expr {
-	struct list listh; /* Used for chaining pattern_expr in pattern_head. */
-	struct list listr; /* Used for chaining pattern_expr in pat_ref. */
+	struct list list; /* Used for chaining pattern_expr in pat_ref. */
 	struct pat_ref *ref; /* The pattern reference if exists. */
-	struct pattern_head *pat_head; /* Point to the pattern_head that contain manipulation functions. */
+	struct pattern_head *pat_head; /* Point to the pattern_head that contain manipulation functions.
+	                                * Note that this link point on compatible head but not on the real
+	                                * head. You can use only the function, and you must not use the
+	                                * "head". Dont write "(struct pattern_expr *)any->pat_head->expr".
+	                                */
 	struct list patterns;         /* list of acl_patterns */
 	struct eb_root pattern_tree;  /* may be used for lookup in large datasets */
 	struct eb_root pattern_tree_2;  /* may be used for different types */
 };
 
+/* This is a list of expression. A struct pattern_expr can be used by
+ * more than one "struct pattern_head". this intermediate struct
+ * permit more than one list.
+ */
+struct pattern_expr_list {
+	struct list list; /* Used for chaining pattern_expr in pattern_head. */
+	int do_free;
+	struct pattern_expr *expr; /* The used expr. */
+};
+
 /* This struct contain a list of pattern expr */
 struct pattern_head {
 	int (*parse)(const char *text, struct pattern *pattern, char **err);
@@ -197,7 +210,7 @@
 	void (*prune)(struct pattern_expr *);
 	struct pattern *(*match)(struct sample *, struct pattern_expr *, int);
 
-	struct list head;
+	struct list head; /* This is a list of struct pattern_expr_list. */
 };
 
 extern char *pat_match_names[PAT_MATCH_NUM];
diff --git a/src/acl.c b/src/acl.c
index 9a962d3..fa54ab9 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -1184,7 +1184,7 @@
 	struct acl_expr *expr;
 	struct pattern_list *pattern;
 	int cfgerr = 0;
-	struct pattern_expr *pexp;
+	struct pattern_expr_list *pexp;
 
 	list_for_each_entry(acl, &p->acl, list) {
 		list_for_each_entry(expr, &acl->expr, list) {
@@ -1207,15 +1207,16 @@
 				}
 
 				/* For each pattern, check if the group exists. */
-				list_for_each_entry(pexp, &expr->pat.head, listh) {
-					if (LIST_ISEMPTY(&pexp->patterns)) {
+				list_for_each_entry(pexp, &expr->pat.head, list) {
+					if (LIST_ISEMPTY(&pexp->expr->patterns)) {
 						Alert("proxy %s: acl %s %s(): no groups specified.\n",
 							p->id, acl->name, expr->kw);
 						cfgerr++;
 						continue;
 					}
 
-					list_for_each_entry(pattern, &pexp->patterns, list) {
+					list_for_each_entry(pattern, &pexp->expr->patterns, list) {
+						/* this keyword only has one argument */
 						if (!check_group(expr->smp->arg_p->data.usr, pattern->pat.ptr.str)) {
 							Alert("proxy %s: acl %s %s(): invalid group '%s'.\n",
 							      p->id, acl->name, expr->kw, pattern->pat.ptr.str);
diff --git a/src/dumpstats.c b/src/dumpstats.c
index cc549d3..13bf201 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -989,8 +989,8 @@
 struct pattern_expr *pat_expr_get_next(struct pattern_expr *getnext, struct list *end)
 {
 	struct pattern_expr *expr;
-	expr = LIST_NEXT(&getnext->listr, struct pattern_expr *, listr);
-	if (&expr->listr == end)
+	expr = LIST_NEXT(&getnext->list, struct pattern_expr *, list);
+	if (&expr->list == end)
 		return NULL;
 	return expr;
 }
@@ -4807,7 +4807,7 @@
 	switch (appctx->st2) {
 	case STAT_ST_INIT:
 		/* Init to the first entry. The list cannot be change */
-		appctx->ctx.map.expr = LIST_ELEM(&appctx->ctx.map.ref->pat, struct pattern_expr *, listr);
+		appctx->ctx.map.expr = LIST_ELEM(&appctx->ctx.map.ref->pat, struct pattern_expr *, list);
 		appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr, &appctx->ctx.map.ref->pat);
 		appctx->st2 = STAT_ST_LIST;
 		/* fall through */
diff --git a/src/pattern.c b/src/pattern.c
index f03c8fc..3d87a47 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -1660,7 +1660,7 @@
 	if (!found)
 		return 0;
 
-	list_for_each_entry(expr, &ref->pat, listr)
+	list_for_each_entry(expr, &ref->pat, list)
 		pattern_delete(key, expr, NULL);
 
 	return 1;
@@ -1691,7 +1691,7 @@
 	if (!found)
 		return 0;
 
-	list_for_each_entry(expr, &ref->pat, listr) {
+	list_for_each_entry(expr, &ref->pat, list) {
 		smp = pattern_find_smp(key, expr, NULL);
 		if (smp && expr->pat_head->parse_smp)
 			if (!expr->pat_head->parse_smp(value, *smp))
@@ -1879,7 +1879,7 @@
 
 	LIST_ADDQ(&ref->head, &elt->list);
 
-	list_for_each_entry(expr, &ref->pat, listr) {
+	list_for_each_entry(expr, &ref->pat, list) {
 		if (!pat_ref_push(elt, expr, 0, err)) {
 			/* Try to delete all the added entries. */
 			pat_ref_delete(ref, pattern);
@@ -1905,7 +1905,7 @@
 		free(elt);
 	}
 
-	list_for_each_entry(expr, &ref->pat, listr)
+	list_for_each_entry(expr, &ref->pat, list)
 		expr->pat_head->prune(expr);
 }
 
@@ -1933,11 +1933,11 @@
 /* This function lookup for existing reference <ref> in pattern_head <head>. */
 struct pattern_expr *pattern_lookup_expr(struct pattern_head *head, struct pat_ref *ref)
 {
-	struct pattern_expr *expr;
+	struct pattern_expr_list *expr;
 
-	list_for_each_entry(expr, &head->head, listh)
-		if (expr->ref == ref)
-			return expr;
+	list_for_each_entry(expr, &head->head, list)
+		if (expr->expr->ref == ref)
+			return expr->expr;
 	return NULL;
 }
 
@@ -1949,27 +1949,69 @@
 struct pattern_expr *pattern_new_expr(struct pattern_head *head, struct pat_ref *ref, char **err)
 {
 	struct pattern_expr *expr;
+	struct pattern_expr_list *list;
 
-	/* A lot of memory. */
-	expr = malloc(sizeof(*expr));
-	if (!expr) {
+	/* Memory and initialization of the chain element. */
+	list = malloc(sizeof(*list));
+	if (!list) {
 		memprintf(err, "out of memory");
 		return NULL;
 	}
 
-	pattern_init_expr(expr);
+	/* Look for existing similar expr. No that only the index, parse and
+	 * parse_smp function must be identical for having similar pattern.
+	 * The other function depends of theses first.
+	 */
+	if (ref) {
+		list_for_each_entry(expr, &ref->pat, list)
+			if (expr->pat_head->index     == head->index &&
+			    expr->pat_head->parse     == head->parse &&
+			    expr->pat_head->parse_smp == head->parse_smp)
+				break;
+		if (&expr->list == &ref->pat)
+			expr = NULL;
+	}
+	else
+		expr = NULL;
 
-	/* Link with the pattern_head. */
-	LIST_ADDQ(&head->head, &expr->listh);
-	expr->pat_head = head;
+	/* If no similar expr was found, we create new expr. */
+	if (!expr) {
+		/* Get a lot of memory for the expr struct. */
+		expr = malloc(sizeof(*expr));
+		if (!expr) {
+			memprintf(err, "out of memory");
+			return NULL;
+		}
 
-	/* Link with ref, or to self to facilitate LIST_DEL() */
-	if (ref)
-		LIST_ADDQ(&ref->pat, &expr->listr);
-	else
-		LIST_INIT(&expr->listr);
+		/* Initialize this new expr. */
+		pattern_init_expr(expr);
 
-	expr->ref = ref;
+		/* This new pattern expression reference one of his heads. */
+		expr->pat_head = head;
+
+		/* Link with ref, or to self to facilitate LIST_DEL() */
+		if (ref)
+			LIST_ADDQ(&ref->pat, &expr->list);
+		else
+			LIST_INIT(&expr->list);
+
+		expr->ref = ref;
+
+		/* We must free this pattern if it is no more used. */
+		list->do_free = 1;
+	}
+	else {
+		/* If the pattern used already exists, it is already linked
+		 * with ref and we must not free it.
+		 */
+		list->do_free = 0;
+	}
+
+	/* The new list element reference the pattern_expr. */
+	list->expr = expr;
+
+	/* Link the list element with the pattern_head. */
+	LIST_ADDQ(&head->head, &list->list);
 	return expr;
 }
 
@@ -2119,7 +2161,7 @@
  */
 struct pattern *pattern_exec_match(struct pattern_head *head, struct sample *smp, int fill)
 {
-	struct pattern_expr *expr;
+	struct pattern_expr_list *list;
 	struct pattern *pat;
 
 	if (!head->match) {
@@ -2132,8 +2174,8 @@
 		return &static_pattern;
 	}
 
-	list_for_each_entry(expr, &head->head, listh) {
-		pat = head->match(smp, expr, fill);
+	list_for_each_entry(list, &head->head, list) {
+		pat = head->match(smp, list->expr, fill);
 		if (pat)
 			return pat;
 	}
@@ -2143,13 +2185,16 @@
 /* This function prune the pattern expression. */
 void pattern_prune(struct pattern_head *head)
 {
-	struct pattern_expr *expr, *safe;
+	struct pattern_expr_list *list, *safe;
 
-	list_for_each_entry_safe(expr, safe, &head->head, listh) {
-		LIST_DEL(&expr->listh);
-		LIST_DEL(&expr->listr);
-		head->prune(expr);
-		free(expr);
+	list_for_each_entry_safe(list, safe, &head->head, list) {
+		LIST_DEL(&list->list);
+		if (list->do_free) {
+			LIST_DEL(&list->expr->list);
+			head->prune(list->expr);
+			free(list->expr);
+		}
+		free(list);
 	}
 }