MEDIUM: stick-table: make it easier to register extra data types

Some users want to add their own data types to stick tables. We don't
want to use a linked list here for performance reasons, so we need to
continue to use an indexed array. This patch allows one to reserve a
compile-time-defined number of extra data types by setting the new
macro STKTABLE_EXTRA_DATA_TYPES to anything greater than zero, keeping
in mind that anything larger will slightly inflate the memory consumed
by stick tables (not per entry though).

Then calling stktable_register_data_store() with the new keyword will
either register a new keyword or fail if the desired entry was already
taken or the keyword already registered.

Note that this patch does not dictate how the data will be used, it only
offers the possibility to create new keywords and have an index to
reference them in the config and in the tables. The caller will not be
able to use stktable_data_cast() and will have to explicitly cast the
stable pointers to the expected types. It can be used for experimentation
as well.
diff --git a/include/common/defaults.h b/include/common/defaults.h
index 6e5840e..cc1fbbd 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -83,6 +83,11 @@
 #define MAX_SESS_STKCTR 3
 #endif
 
+// max # of extra stick-table data types that can be registred at runtime
+#ifndef STKTABLE_EXTRA_DATA_TYPES
+#define STKTABLE_EXTRA_DATA_TYPES 0
+#endif
+
 // max # of loops we can perform around a read() which succeeds.
 // It's very frequent that the system returns a few TCP segments at a time.
 #ifndef MAX_READ_POLL_LOOPS
diff --git a/include/proto/stick_table.h b/include/proto/stick_table.h
index a121ad5..a5e7520 100644
--- a/include/proto/stick_table.h
+++ b/include/proto/stick_table.h
@@ -51,6 +51,7 @@
 				        struct session *l4, void *l7, unsigned int opt,
 				        struct sample_expr *expr, struct sample *smp);
 int stktable_compatible_sample(struct sample_expr *expr, unsigned long table_type);
+int stktable_register_data_store(int idx, const char *name, int std_type, int arg_type);
 int stktable_get_data_type(char *name);
 struct proxy *find_stktable(const char *name);
 int stktable_trash_oldest(struct stktable *t, int to_batch);
diff --git a/include/types/stick_table.h b/include/types/stick_table.h
index e28492c..6fdc58e 100644
--- a/include/types/stick_table.h
+++ b/include/types/stick_table.h
@@ -60,7 +60,11 @@
 	STKTABLE_DT_BYTES_IN_RATE,/* bytes rate from client to servers */
 	STKTABLE_DT_BYTES_OUT_CNT,/* cumulated bytes count from servers to client */
 	STKTABLE_DT_BYTES_OUT_RATE,/* bytes rate from servers to client */
-	STKTABLE_DATA_TYPES       /* Number of data types, must always be last */
+	STKTABLE_STATIC_DATA_TYPES,/* number of types above */
+	/* up to STKTABLE_EXTRA_DATA_TYPES types may be registered here, always
+	 * followed by the number of data types, must always be last.
+	 */
+	STKTABLE_DATA_TYPES = STKTABLE_STATIC_DATA_TYPES + STKTABLE_EXTRA_DATA_TYPES
 };
 
 /* The equivalent standard types of the stored data */
diff --git a/src/stick_table.c b/src/stick_table.c
index a2a8688..a6ee77f 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -688,7 +688,10 @@
 	return 1;
 }
 
-/* Extra data types processing */
+/* Extra data types processing : after the last one, some room may remain
+ * before STKTABLE_DATA_TYPES that may be used to register extra data types
+ * at run time.
+ */
 struct stktable_data_type stktable_data_types[STKTABLE_DATA_TYPES] = {
 	[STKTABLE_DT_SERVER_ID]     = { .name = "server_id",      .std_type = STD_T_SINT  },
 	[STKTABLE_DT_GPC0]          = { .name = "gpc0",           .std_type = STD_T_UINT  },
@@ -708,6 +711,36 @@
 	[STKTABLE_DT_BYTES_OUT_RATE]= { .name = "bytes_out_rate", .std_type = STD_T_FRQP, .arg_type = ARG_T_DELAY },
 };
 
+/* Registers stick-table extra data type with index <idx>, name <name>, type
+ * <std_type> and arg type <arg_type>. If the index is negative, the next free
+ * index is automatically allocated. The allocated index is returned, or -1 if
+ * no free index was found or <name> was already registered. The <name> is used
+ * directly as a pointer, so if it's not stable, the caller must allocate it.
+ */
+int stktable_register_data_store(int idx, const char *name, int std_type, int arg_type)
+{
+	if (idx < 0) {
+		for (idx = 0; idx < STKTABLE_DATA_TYPES; idx++) {
+			if (!stktable_data_types[idx].name)
+				break;
+
+			if (strcmp(stktable_data_types[idx].name, name) == 0)
+				return -1;
+		}
+	}
+
+	if (idx >= STKTABLE_DATA_TYPES)
+		return -1;
+
+	if (stktable_data_types[idx].name != NULL)
+		return -1;
+
+	stktable_data_types[idx].name = name;
+	stktable_data_types[idx].std_type = std_type;
+	stktable_data_types[idx].arg_type = arg_type;
+	return idx;
+}
+
 /*
  * Returns the data type number for the stktable_data_type whose name is <name>,
  * or <0 if not found.
@@ -717,6 +750,8 @@
 	int type;
 
 	for (type = 0; type < STKTABLE_DATA_TYPES; type++) {
+		if (!stktable_data_types[type].name)
+			continue;
 		if (strcmp(name, stktable_data_types[type].name) == 0)
 			return type;
 	}