From 5cad0759dc2a624af7a9465823628775dc390456 Mon Sep 17 00:00:00 2001
From: Georgiy Lebedev <g.lebedev@tarantool.org>
Date: Tue, 18 Oct 2022 11:24:23 +0300
Subject: [PATCH] uri: optimize allocation of parameters and their values
 dynamic arrays
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Allocation of URI parameters and their values dynamic arrays is done
inefficiently: they are reallocated each time a new parameter or parameter
value is added — grow them exponentially instead.

`struct uri_param` and `struct uri` are exposed in Lua via FFI
(see src/lua/uri.lua): add warnings about the necessity of reflecting
changes to them in `ffi.cdecl`.

Closes #7155

NO_DOC=optimization
NO_TEST=optimization
---
 ...-optimize-add-param-and-add-param-value.md |  3 ++
 src/lib/uri/uri.c                             | 40 ++++++++++++++-----
 src/lib/uri/uri.h                             |  9 +++++
 src/lua/uri.lua                               |  2 +
 4 files changed, 45 insertions(+), 9 deletions(-)
 create mode 100644 changelogs/unreleased/gh-7155-uri-optimize-add-param-and-add-param-value.md

diff --git a/changelogs/unreleased/gh-7155-uri-optimize-add-param-and-add-param-value.md b/changelogs/unreleased/gh-7155-uri-optimize-add-param-and-add-param-value.md
new file mode 100644
index 0000000000..e35a324d1b
--- /dev/null
+++ b/changelogs/unreleased/gh-7155-uri-optimize-add-param-and-add-param-value.md
@@ -0,0 +1,3 @@
+## bugfix/uri
+
+* Optimized addition of parameters and parameter values (gh-7155).
diff --git a/src/lib/uri/uri.c b/src/lib/uri/uri.c
index edf9f8b707..eab70b34b8 100644
--- a/src/lib/uri/uri.c
+++ b/src/lib/uri/uri.c
@@ -10,9 +10,18 @@
 #define XSTRDUP(s)      (s != NULL ? xstrdup(s) : NULL)
 #define XSTRNDUP(s, n)  (s != NULL ? xstrndup(s, n) : NULL)
 
+/**
+ * WARNING: this structure is exposed in Lua via FFI (see src/lua/uri.lua): any
+ * change  must be reflected in `ffi.cdef`.
+ */
 struct uri_param {
 	/** Name of URI parameter. */
 	char *name;
+	/**
+	 * Capacity of URI parameter values dynamic array (used for exponential
+	 * reallocation).
+	 */
+	int values_capacity;
 	/** Count of values for this parameter. */
 	int value_count;
 	/** Array of values for this parameter. */
@@ -38,8 +47,13 @@ uri_find_param(const struct uri *uri, const char *name)
 static void
 uri_param_add_value(struct uri_param *param, const char *value)
 {
-	size_t size = (param->value_count + 1) * sizeof(char *);
-	param->values = xrealloc(param->values, size);
+	assert(param->value_count <= param->values_capacity);
+	if (param->value_count == param->values_capacity) {
+		param->values_capacity = param->values_capacity == 0 ? 16 :
+					 param->values_capacity * 2;
+		size_t size = param->values_capacity * sizeof(char *);
+		param->values = xrealloc(param->values, size);
+	}
 	param->values[param->value_count++] = xstrdup(value);
 }
 
@@ -65,6 +79,7 @@ uri_param_create(struct uri_param *param, const char *name)
 	param->name = xstrdup(name);
 	param->values = NULL;
 	param->value_count = 0;
+	param->values_capacity = 0;
 }
 
 /**
@@ -74,9 +89,10 @@ static void
 uri_param_copy(struct uri_param *dst, const struct uri_param *src)
 {
 	dst->name = xstrdup(src->name);
+	dst->values_capacity = src->values_capacity;
 	dst->value_count = src->value_count;
-	dst->values = (src->value_count == 0 ? NULL :
-		       xmalloc(src->value_count * sizeof(*dst->values)));
+	dst->values = (src->values_capacity == 0 ? NULL :
+		       xmalloc(src->values_capacity * sizeof(*dst->values)));
 	for (int i = 0; i < src->value_count; i++)
 		dst->values[i] = xstrdup(src->values[i]);
 }
@@ -99,9 +115,14 @@ uri_add_param(struct uri *uri, const char *name, const char *value)
 {
 	struct uri_param *param = uri_find_param(uri, name);
 	if (param == NULL) {
-		size_t size = (uri->param_count + 1) *
-			sizeof(struct uri_param);
-		uri->params = xrealloc(uri->params, size);
+		assert(uri->param_count <= uri->params_capacity);
+		if (uri->param_count == uri->params_capacity) {
+			uri->params_capacity = uri->params_capacity == 0 ? 16 :
+					       uri->params_capacity * 2;
+			size_t size =
+				uri->params_capacity * sizeof(struct uri_param);
+			uri->params = xrealloc(uri->params, size);
+		}
 		param = &uri->params[uri->param_count++];
 		uri_param_create(param, name);
 	}
@@ -161,9 +182,10 @@ uri_copy(struct uri *dst, const struct uri *src)
 	dst->query = XSTRDUP(src->query);
 	dst->fragment = XSTRDUP(src->fragment);
 	dst->host_hint = src->host_hint;
+	dst->params_capacity = src->params_capacity;
 	dst->param_count = src->param_count;
-	dst->params = (src->param_count == 0 ? NULL :
-		       xmalloc(src->param_count * sizeof(*dst->params)));
+	dst->params = (src->params_capacity == 0 ? NULL :
+		       xmalloc(src->params_capacity * sizeof(*dst->params)));
 	for (int i = 0; i < src->param_count; i++)
 		uri_param_copy(&dst->params[i], &src->params[i]);
 }
diff --git a/src/lib/uri/uri.h b/src/lib/uri/uri.h
index bfbeb923b4..a0a97e90ef 100644
--- a/src/lib/uri/uri.h
+++ b/src/lib/uri/uri.h
@@ -15,6 +15,10 @@ extern "C" {
 
 struct uri_param;
 
+/**
+ * WARNING: this structure is exposed in Lua via FFI (see src/lua/uri.lua): any
+ * change  must be reflected in `ffi.cdef`.
+ */
 struct uri {
 	char *scheme;
 	char *login;
@@ -25,6 +29,11 @@ struct uri {
 	char *query;
 	char *fragment;
 	int host_hint;
+	/**
+	 * Capacity of URI parameters dynamic array (used for exponential
+	 * reallocation).
+	 */
+	int params_capacity;
 	/** Count of URI parameters */
 	int param_count;
 	/** Different URI parameters */
diff --git a/src/lua/uri.lua b/src/lua/uri.lua
index a263064286..ed1b272a7b 100644
--- a/src/lua/uri.lua
+++ b/src/lua/uri.lua
@@ -7,6 +7,7 @@ local uri = require('uri')
 local uri_cdef = [[
 struct uri_param {
     const char *name;
+    int values_capacity;
     int value_count;
     const char **values;
 };
@@ -28,6 +29,7 @@ struct uri {
     const char *query;
     const char *fragment;
     int host_hint;
+    int params_capacity;
     int param_count;
     struct uri_param *params;
 };
-- 
GitLab