[patches] [server 1/2] tuple: move tuple field names hash to a separate shared struct

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Tue Jan 23 14:38:43 MSK 2018


Struct tuple_dictionary stores hash of field names, defined in
a space format. The structure is refable and are going to be used
to share new field names with old space formats.

Part of #3011

Signed-off-by: Vladislav Shpilevoy <v.shpilevoy at tarantool.org>
---
 src/box/CMakeLists.txt          |   1 +
 src/box/lua/tuple.c             |   7 +-
 src/box/memtx_space.c           |   5 +-
 src/box/space_def.c             |  12 ++-
 src/box/space_def.h             |   8 +-
 src/box/tuple.c                 |   5 +-
 src/box/tuple_dictionary.c      | 217 ++++++++++++++++++++++++++++++++++++++++
 src/box/tuple_dictionary.h      | 110 ++++++++++++++++++++
 src/box/tuple_format.c          | 193 +++++------------------------------
 src/box/tuple_format.h          |  31 +++---
 src/box/vinyl.c                 |   8 +-
 src/box/vy_index.c              |   5 +-
 test/unit/vy_iterators_helper.c |   7 +-
 test/unit/vy_mem.c              |   3 +-
 test/unit/vy_point_lookup.c     |   4 +-
 15 files changed, 409 insertions(+), 207 deletions(-)
 create mode 100644 src/box/tuple_dictionary.c
 create mode 100644 src/box/tuple_dictionary.h

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 494269fb1..bcdae9d99 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -36,6 +36,7 @@ add_library(tuple STATIC
     tuple_update.c
     tuple_compare.cc
     tuple_hash.cc
+    tuple_dictionary.c
     key_def.cc
     coll_def.c
     coll.c
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index d5ed7c489..7ca4299a3 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -287,11 +287,12 @@ lbox_tuple_to_map(struct lua_State *L)
 	const struct tuple_field *field = &format->fields[0];
 	const char *pos = tuple_data(tuple);
 	int field_count = (int)mp_decode_array(&pos);
-	int n_named = tuple_format_named_fields(format);
+	int n_named = format->dict->name_count;
 	lua_createtable(L, field_count, n_named);
 	for (int i = 0; i < n_named; ++i, ++field) {
 		/* Access by name. */
-		lua_pushstring(L, field->name);
+		const char *name = format->dict->names[i];
+		lua_pushstring(L, name);
 		luamp_decode(L, luaL_msgpack_default, &pos);
 		lua_rawset(L, -3);
 		/*
@@ -299,7 +300,7 @@ lbox_tuple_to_map(struct lua_State *L)
 		 * copy for tables - lua optimizes it and uses
 		 * references.
 		 */
-		lua_pushstring(L, field->name);
+		lua_pushstring(L, name);
 		lua_rawget(L, -2);
 		lua_rawseti(L, -2, i + TUPLE_INDEX_BASE);
 	}
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 599a68e13..e1968e5c2 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -933,8 +933,9 @@ memtx_space_new(struct memtx_engine *memtx,
 	rlist_foreach_entry(index_def, key_list, link)
 		keys[key_count++] = index_def->key_def;
 
-	struct tuple_format *format = tuple_format_new(&memtx_tuple_format_vtab,
-			keys, key_count, 0, def->fields, def->field_count);
+	struct tuple_format *format =
+		tuple_format_new(&memtx_tuple_format_vtab, keys, key_count, 0,
+				 def->fields, def->field_count, def->dict);
 	if (format == NULL) {
 		free(memtx_space);
 		return NULL;
diff --git a/src/box/space_def.c b/src/box/space_def.c
index fdbbc907e..ce5872ffb 100644
--- a/src/box/space_def.c
+++ b/src/box/space_def.c
@@ -42,10 +42,10 @@ const struct opt_def space_opts_reg[] = {
 };
 
 /**
- * Size of the space_def, calculated using its name.
+ * Size of the space_def.
  * @param name_len Length of the space name.
- * @param field_defs_size Binary size of a field definitions
- *        array.
+ * @param field_names_size Size of all names.
+ * @param field_count Space field count.
  * @param[out] names_offset Offset from the beginning of a def to
  *             a field names memory.
  * @param[out] fields_offset Offset from the beginning of a def to
@@ -86,6 +86,7 @@ space_def_dup(const struct space_def *src)
 			name_pos += strlen(name_pos) + 1;
 		}
 	}
+	tuple_dictionary_ref(ret->dict);
 	return ret;
 }
 
@@ -109,6 +110,11 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
 	}
 	assert(name_len <= BOX_NAME_MAX);
 	assert(engine_len <= ENGINE_NAME_MAX);
+	def->dict = tuple_dictionary_new(fields, field_count);
+	if (def->dict == NULL) {
+		free(def);
+		return NULL;
+	}
 	def->id = id;
 	def->uid = uid;
 	def->exact_field_count = exact_field_count;
diff --git a/src/box/space_def.h b/src/box/space_def.h
index 2db09cb92..97c7e1380 100644
--- a/src/box/space_def.h
+++ b/src/box/space_def.h
@@ -31,7 +31,7 @@
  * SUCH DAMAGE.
  */
 #include "trivia/util.h"
-#include "field_def.h"
+#include "tuple_dictionary.h"
 #include "schema_def.h"
 #include <stdbool.h>
 
@@ -77,6 +77,11 @@ struct space_def {
 	 */
 	uint32_t exact_field_count;
 	char engine_name[ENGINE_NAME_MAX + 1];
+	/**
+	 * Tuple field names dictionary, shared with a space's
+	 * tuple format.
+	 */
+	struct tuple_dictionary *dict;
 	/** Space fields, specified by a user. */
 	struct field_def *fields;
 	/** Length of @a fields. */
@@ -92,6 +97,7 @@ struct space_def {
 static inline void
 space_def_delete(struct space_def *def)
 {
+	tuple_dictionary_unref(def->dict);
 	TRASH(def);
 	free(def);
 }
diff --git a/src/box/tuple.c b/src/box/tuple.c
index 54325af43..b0e661a48 100644
--- a/src/box/tuple.c
+++ b/src/box/tuple.c
@@ -389,9 +389,8 @@ tuple_init(field_name_hash_f hash)
 	/*
 	 * Create a format for runtime tuples
 	 */
-	RLIST_HEAD(empty_list);
 	tuple_format_runtime = tuple_format_new(&tuple_format_runtime_vtab,
-						NULL, 0, 0, NULL, 0);
+						NULL, 0, 0, NULL, 0, NULL);
 	if (tuple_format_runtime == NULL)
 		return -1;
 
@@ -473,7 +472,7 @@ box_tuple_format_new(struct key_def **keys, uint16_t key_count)
 {
 	box_tuple_format_t *format =
 		tuple_format_new(&tuple_format_runtime_vtab,
-				 keys, key_count, 0, NULL, 0);
+				 keys, key_count, 0, NULL, 0, NULL);
 	if (format != NULL)
 		tuple_format_ref(format);
 	return format;
diff --git a/src/box/tuple_dictionary.c b/src/box/tuple_dictionary.c
new file mode 100644
index 000000000..a8ea13a51
--- /dev/null
+++ b/src/box/tuple_dictionary.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "tuple_dictionary.h"
+#include "error.h"
+#include "diag.h"
+
+field_name_hash_f field_name_hash;
+
+#define mh_name _strnu32
+struct mh_strnu32_key_t {
+	const char *str;
+	size_t len;
+	uint32_t hash;
+};
+#define mh_key_t struct mh_strnu32_key_t *
+struct mh_strnu32_node_t {
+	const char *str;
+	size_t len;
+	uint32_t hash;
+	uint32_t val;
+};
+#define mh_node_t struct mh_strnu32_node_t
+
+#define mh_arg_t void *
+#define mh_hash(a, arg) ((a)->hash)
+#define mh_hash_key(a, arg) mh_hash(a, arg)
+#define mh_cmp(a, b, arg) ((a)->len != (b)->len || \
+			   memcmp((a)->str, (b)->str, (a)->len))
+#define mh_cmp_key(a, b, arg) mh_cmp(a, b, arg)
+#define MH_SOURCE 1
+#include "salad/mhash.h" /* Create mh_strnu32_t hash. */
+
+/** Free names hash and its content. */
+static inline void
+tuple_dictionary_delete_hash(struct mh_strnu32_t *hash)
+{
+	while (mh_size(hash)) {
+		mh_int_t i = mh_first(hash);
+		mh_strnu32_del(hash, i, NULL);
+	}
+	mh_strnu32_delete(hash);
+}
+
+/** Free tuple dictionary and its content. */
+static inline void
+tuple_dictionary_delete(struct tuple_dictionary *dict)
+{
+	assert(dict->refs == 0);
+	if (dict->hash != NULL) {
+		tuple_dictionary_delete_hash(dict->hash);
+		free(dict->names);
+	} else {
+		assert(dict->names == NULL);
+	}
+	free(dict);
+}
+
+/**
+ * Set a new name in a dictionary. Check duplicates. Memory must
+ * be reserved already.
+ * @param dict Tuple dictionary.
+ * @param name New name.
+ * @param name_len Length of @a name.
+ * @param fieldno Field number.
+ *
+ * @retval  0 Success.
+ * @retval -1 Duplicate name error.
+ */
+static inline int
+tuple_dictionary_set_name(struct tuple_dictionary *dict, const char *name,
+			  uint32_t name_len, uint32_t fieldno)
+{
+	assert(fieldno < dict->name_count);
+	uint32_t name_hash = field_name_hash(name, name_len);
+	struct mh_strnu32_key_t key = {
+		name, name_len, name_hash
+	};
+	mh_int_t rc = mh_strnu32_find(dict->hash, &key, NULL);
+	if (rc != mh_end(dict->hash)) {
+		diag_set(ClientError, ER_SPACE_FIELD_IS_DUPLICATE,
+			 name);
+		return -1;
+	}
+	struct mh_strnu32_node_t name_node = {
+		name, name_len, name_hash, fieldno
+	};
+	rc = mh_strnu32_put(dict->hash, &name_node, NULL, NULL);
+	/* Memory was reserved in new(). */
+	assert(rc != mh_end(dict->hash));
+	(void) rc;
+	return 0;
+}
+
+struct tuple_dictionary *
+tuple_dictionary_new(const struct field_def *fields, uint32_t field_count)
+{
+	struct tuple_dictionary *dict =
+		(struct tuple_dictionary *)calloc(1, sizeof(*dict));
+	if (dict == NULL) {
+		diag_set(OutOfMemory, sizeof(*dict), "malloc",
+			 "dict");
+		return NULL;
+	}
+	dict->refs = 1;
+	dict->name_count = field_count;
+	if (field_count == 0)
+		return dict;
+	uint32_t names_offset = sizeof(dict->names[0]) * field_count;
+	uint32_t total = names_offset;
+	for (uint32_t i = 0; i < field_count; ++i)
+		total += strlen(fields[i].name) + 1;
+	dict->names = (char **) malloc(total);
+	if (dict->names == NULL) {
+		diag_set(OutOfMemory, total, "malloc", "dict->names");
+		goto err_memory;
+	}
+	dict->hash = mh_strnu32_new();
+	if (dict->hash == NULL) {
+		diag_set(OutOfMemory, sizeof(*dict->hash),
+			 "mh_strnu32_new", "dict->hash");
+		goto err_hash;
+	}
+	if (mh_strnu32_reserve(dict->hash, field_count, NULL) != 0) {
+		diag_set(OutOfMemory, field_count *
+			 sizeof(struct mh_strnu32_node_t), "mh_strnu32_reserve",
+			 "dict->hash");
+		goto err_name;
+	}
+	char *pos = (char *) dict->names + names_offset;
+	for (uint32_t i = 0; i < field_count; ++i) {
+		int len = strlen(fields[i].name);
+		memcpy(pos, fields[i].name, len);
+		pos[len] = 0;
+		dict->names[i] = pos;
+		if (tuple_dictionary_set_name(dict, pos, len, i) != 0)
+			goto err_name;
+		pos += len + 1;
+	}
+	return dict;
+
+err_name:
+	tuple_dictionary_delete_hash(dict->hash);
+err_hash:
+	free(dict->names);
+err_memory:
+	free(dict);
+	return NULL;
+}
+
+void
+tuple_dictionary_swap(struct tuple_dictionary *a, struct tuple_dictionary *b)
+{
+	int a_refs = a->refs;
+	int b_refs = b->refs;
+	struct tuple_dictionary t = *a;
+	*a = *b;
+	*b = t;
+	a->refs = a_refs;
+	b->refs = b_refs;
+}
+
+void
+tuple_dictionary_unref(struct tuple_dictionary *dict)
+{
+	assert(dict->refs > 0);
+	if (--dict->refs == 0)
+		tuple_dictionary_delete(dict);
+}
+
+void
+tuple_dictionary_ref(struct tuple_dictionary *dict)
+{
+	++dict->refs;
+}
+
+int
+tuple_fieldno_by_name(struct tuple_dictionary *dict, const char *name,
+		      uint32_t name_len, uint32_t name_hash, uint32_t *fieldno)
+{
+	struct mh_strnu32_t *hash = dict->hash;
+	if (hash == NULL)
+		return -1;
+	struct mh_strnu32_key_t key = {name, name_len, name_hash};
+	mh_int_t rc = mh_strnu32_find(hash, &key, NULL);
+	if (rc == mh_end(hash))
+		return -1;
+	*fieldno = mh_strnu32_node(hash, rc)->val;
+	return 0;
+}
diff --git a/src/box/tuple_dictionary.h b/src/box/tuple_dictionary.h
new file mode 100644
index 000000000..5f699f5e1
--- /dev/null
+++ b/src/box/tuple_dictionary.h
@@ -0,0 +1,110 @@
+#ifndef TARANTOOL_BOX_TUPLE_DICTIONARY_H_INCLUDED
+#define TARANTOOL_BOX_TUPLE_DICTIONARY_H_INCLUDED
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "trivia/util.h"
+#include "field_def.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mh_strnu32_t;
+typedef uint32_t (*field_name_hash_f)(const char *str, uint32_t len);
+extern field_name_hash_f field_name_hash;
+
+/**
+ * Shared tuple field names hash. It is referenced by tuple format
+ * and space definition.
+ */
+struct tuple_dictionary {
+	/** Field names hash. Key - name, value - field number. */
+	struct mh_strnu32_t *hash;
+	/**
+	 * Array of names. All of them are stored in monolit
+	 * memory area.
+	 */
+	char **names;
+	/** Length of a names array. */
+	uint32_t name_count;
+	/** Reference counter. */
+	int refs;
+};
+
+/**
+ * Create a new tuple dictionary.
+ * @param fields Array of space fields.
+ * @param field_count Length of @a fields.
+ *
+ * @retval     NULL Memory error.
+ * @retval not NULL Tuple dictionary with one ref.
+ */
+struct tuple_dictionary *
+tuple_dictionary_new(const struct field_def *fields, uint32_t field_count);
+
+/**
+ * Swap content of two dictionaries. Reference counters are not
+ * swaped.
+ */
+void
+tuple_dictionary_swap(struct tuple_dictionary *a, struct tuple_dictionary *b);
+
+/**
+ * Decrement reference counter. If a new reference counter value
+ * is 0, then the dictionary is deleted.
+ */
+void
+tuple_dictionary_unref(struct tuple_dictionary *dict);
+
+/** Increment reference counter. */
+void
+tuple_dictionary_ref(struct tuple_dictionary *dict);
+
+/**
+ * Get field number by a name.
+ * @param dict Tuple dictionary.
+ * @param name Name to search.
+ * @param name_len Length of @a name.
+ * @param name_hash Hash of @a name.
+ * @param[out] fieldno Field number, if it is found.
+ *
+ * @retval  0 Field is found.
+ * @retval -1 No such field.
+ */
+int
+tuple_fieldno_by_name(struct tuple_dictionary *dict, const char *name,
+		      uint32_t name_len, uint32_t name_hash, uint32_t *fieldno);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /*TARANTOOL_BOX_TUPLE_DICTIONARY_H_INCLUDED*/
diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c
index 4e0b22347..3e2c8bf57 100644
--- a/src/box/tuple_format.c
+++ b/src/box/tuple_format.c
@@ -30,32 +30,6 @@
  */
 #include "tuple_format.h"
 
-field_name_hash_f field_name_hash;
-
-#define mh_name _strnu32
-struct mh_strnu32_key_t {
-	const char *str;
-	size_t len;
-	uint32_t hash;
-};
-#define mh_key_t struct mh_strnu32_key_t *
-struct mh_strnu32_node_t {
-	const char *str;
-	size_t len;
-	uint32_t hash;
-	uint32_t val;
-};
-#define mh_node_t struct mh_strnu32_node_t
-
-#define mh_arg_t void *
-#define mh_hash(a, arg) ((a)->hash)
-#define mh_hash_key(a, arg) mh_hash(a, arg)
-#define mh_cmp(a, b, arg) ((a)->len != (b)->len || \
-			   memcmp((a)->str, (b)->str, (a)->len))
-#define mh_cmp_key(a, b, arg) mh_cmp(a, b, arg)
-#define MH_SOURCE 1
-#include "salad/mhash.h" /* Create mh_strnu32_t hash. */
-
 /** Global table of tuple formats */
 struct tuple_format **tuple_formats;
 static intptr_t recycled_format_ids = FORMAT_ID_NIL;
@@ -63,52 +37,9 @@ static intptr_t recycled_format_ids = FORMAT_ID_NIL;
 static uint32_t formats_size = 0, formats_capacity = 0;
 
 static const struct tuple_field tuple_field_default = {
-	FIELD_TYPE_ANY, TUPLE_OFFSET_SLOT_NIL, false, NULL, false,
+	FIELD_TYPE_ANY, TUPLE_OFFSET_SLOT_NIL, false, false,
 };
 
-int
-tuple_format_named_fields(const struct tuple_format *format)
-{
-	return format->names == NULL ? 0 : mh_size(format->names);
-}
-
-/**
- * Add @a name to a name hash of @a format.
- * @param format Format to add name.
- * @param name Name to add.
- * @param name_len Length of @a name.
- * @param fieldno Field number.
- * @param check_dup True, if need to check for duplicates.
- *
- * @retval -1 Duplicate field error.
- * @retval  0 Success.
- */
-static inline int
-tuple_format_add_name(struct tuple_format *format, const char *name,
-		      uint32_t name_len, uint32_t fieldno, bool check_dup)
-{
-	uint32_t name_hash = field_name_hash(name, name_len);
-	struct mh_strnu32_node_t name_node = {
-		name, name_len, name_hash, fieldno
-	};
-	if (check_dup) {
-		struct mh_strnu32_key_t key = {
-			name, name_len, name_hash
-		};
-		mh_int_t rc = mh_strnu32_find(format->names, &key, NULL);
-		if (rc != mh_end(format->names)) {
-			diag_set(ClientError, ER_SPACE_FIELD_IS_DUPLICATE,
-				 name);
-			return -1;
-		}
-	}
-	mh_int_t rc = mh_strnu32_put(format->names, &name_node, NULL, NULL);
-	/* Memory was reserved in alloc(). */
-	assert(rc != mh_end(format->names));
-	(void) rc;
-	return 0;
-}
-
 /**
  * Extract all available type info from keys and field
  * definitions.
@@ -122,21 +53,12 @@ tuple_format_create(struct tuple_format *format, struct key_def * const *keys,
 		format->field_map_size = 0;
 		return 0;
 	}
-	char *name_pos = (char *)format + sizeof(*format) +
-			 sizeof(struct tuple_field) * format->field_count;
 	/* Initialize defined fields */
 	for (uint32_t i = 0; i < field_count; ++i) {
 		format->fields[i].is_key_part = false;
 		format->fields[i].type = fields[i].type;
 		format->fields[i].offset_slot = TUPLE_OFFSET_SLOT_NIL;
 		format->fields[i].is_nullable = fields[i].is_nullable;
-		format->fields[i].name = name_pos;
-		size_t len = strlen(fields[i].name);
-		memcpy(name_pos, fields[i].name, len);
-		name_pos[len] = 0;
-		if (tuple_format_add_name(format, name_pos, len, i, true) != 0)
-			return -1;
-		name_pos += len + 1;
 		if (i + 1 > format->min_field_count && !fields[i].is_nullable)
 			format->min_field_count = i + 1;
 	}
@@ -178,10 +100,13 @@ tuple_format_create(struct tuple_format *format, struct key_def * const *keys,
 			} else if (field->type != part->type) {
 				const char *name;
 				int fieldno = part->fieldno + TUPLE_INDEX_BASE;
-				if (field->name == NULL)
+				if (part->fieldno >= field_count) {
 					name = tt_sprintf("%d", fieldno);
-				else
-					name = tt_sprintf("'%s'", field->name);
+				} else {
+					const struct field_def *def =
+						&fields[part->fieldno];
+					name = tt_sprintf("'%s'", def->name);
+				}
 				int errcode;
 				if (! field->is_key_part)
 					errcode = ER_FORMAT_MISMATCH_INDEX_PART;
@@ -267,8 +192,7 @@ tuple_format_deregister(struct tuple_format *format)
 
 static struct tuple_format *
 tuple_format_alloc(struct key_def * const *keys, uint16_t key_count,
-		   const struct field_def *space_fields,
-		   uint32_t space_field_count)
+		   uint32_t space_field_count, struct tuple_dictionary *dict)
 {
 	uint32_t index_field_count = 0;
 	/* find max max field no */
@@ -284,8 +208,6 @@ tuple_format_alloc(struct key_def * const *keys, uint16_t key_count,
 	uint32_t field_count = MAX(space_field_count, index_field_count);
 	uint32_t total = sizeof(struct tuple_format) +
 			 field_count * sizeof(struct tuple_field);
-	for (uint32_t i = 0; i < space_field_count; ++i)
-		total += strlen(space_fields[i].name) + 1;
 
 	struct tuple_format *format = (struct tuple_format *) malloc(total);
 	if (format == NULL) {
@@ -293,22 +215,16 @@ tuple_format_alloc(struct key_def * const *keys, uint16_t key_count,
 			 "tuple format");
 		return NULL;
 	}
-	if (space_field_count != 0) {
-		format->names = mh_strnu32_new();
-		if (format->names == NULL) {
-			diag_set(OutOfMemory, sizeof(*format->names),
-				 "mh_strnu32_new", "format->names");
-			goto error_name_hash_new;
-		}
-		if (mh_strnu32_reserve(format->names, space_field_count,
-				       NULL) != 0) {
-			diag_set(OutOfMemory, space_field_count *
-					      sizeof(struct mh_strnu32_node_t),
-				 "mh_strnu32_reserve", "format->names");
-			goto error_name_hash_reserve;
+	if (dict == NULL) {
+		assert(space_field_count == 0);
+		format->dict = tuple_dictionary_new(NULL, 0);
+		if (format->dict == NULL) {
+			free(format);
+			return NULL;
 		}
 	} else {
-		format->names = NULL;
+		format->dict = dict;
+		tuple_dictionary_ref(dict);
 	}
 	format->refs = 0;
 	format->id = FORMAT_ID_NIL;
@@ -317,25 +233,13 @@ tuple_format_alloc(struct key_def * const *keys, uint16_t key_count,
 	format->exact_field_count = 0;
 	format->min_field_count = index_field_count;
 	return format;
-
-error_name_hash_reserve:
-	mh_strnu32_delete(format->names);
-error_name_hash_new:
-	free(format);
-	return NULL;
 }
 
 /** Free tuple format resources, doesn't unregister. */
 static inline void
 tuple_format_destroy(struct tuple_format *format)
 {
-	if (format->names != NULL) {
-		while (mh_size(format->names)) {
-			mh_int_t i = mh_first(format->names);
-			mh_strnu32_del(format->names, i, NULL);
-		}
-		mh_strnu32_delete(format->names);
-	}
+	tuple_dictionary_unref(format->dict);
 }
 
 void
@@ -349,11 +253,13 @@ tuple_format_delete(struct tuple_format *format)
 struct tuple_format *
 tuple_format_new(struct tuple_format_vtab *vtab, struct key_def * const *keys,
 		 uint16_t key_count, uint16_t extra_size,
-		 const struct field_def *space_fields, uint32_t space_field_count)
+		 const struct field_def *space_fields,
+		 uint32_t space_field_count, struct tuple_dictionary *dict)
 {
+	assert((dict == NULL && space_field_count == 0) ||
+	       (dict != NULL && space_field_count == dict->name_count));
 	struct tuple_format *format =
-		tuple_format_alloc(keys, key_count, space_fields,
-				   space_field_count);
+		tuple_format_alloc(keys, key_count, space_field_count, dict);
 	if (format == NULL)
 		return NULL;
 	format->vtab = *vtab;
@@ -390,47 +296,17 @@ tuple_format_eq(const struct tuple_format *a, const struct tuple_format *b)
 }
 
 struct tuple_format *
-tuple_format_dup(const struct tuple_format *src)
+tuple_format_dup(struct tuple_format *src)
 {
 	uint32_t total = sizeof(struct tuple_format) +
 			 src->field_count * sizeof(struct tuple_field);
-	uint32_t name_offset = total;
-	uint32_t name_count = 0;
-	for (; name_count < src->field_count &&
-	       src->fields[name_count].name != NULL; ++name_count)
-		total += strlen(src->fields[name_count].name) + 1;
-
 	struct tuple_format *format = (struct tuple_format *) malloc(total);
 	if (format == NULL) {
 		diag_set(OutOfMemory, total, "malloc", "tuple format");
 		return NULL;
 	}
 	memcpy(format, src, total);
-	if (name_count != 0) {
-		format->names = mh_strnu32_new();
-		if (format->names == NULL) {
-			diag_set(OutOfMemory, sizeof(*format->names),
-				 "mh_strnu32_new", "format->names");
-			goto error_name_hash_new;
-		}
-		if (mh_strnu32_reserve(format->names, name_count,
-				       NULL) != 0) {
-			diag_set(OutOfMemory, sizeof(struct mh_strnu32_node_t) *
-					      name_count,
-				 "mh_strnu32_reserve", "format->names");
-			goto error_name_hash_reserve;
-		}
-		char *name_pos = (char *)format + name_offset;
-		for (uint32_t i = 0; i < name_count; ++i) {
-			assert(src->fields[i].name != NULL);
-			uint32_t len = strlen(src->fields[i].name);
-			format->fields[i].name = name_pos;
-			tuple_format_add_name(format, name_pos, len, i, false);
-			name_pos += len + 1;
-		}
-	} else {
-		assert(format->names == NULL);
-	}
+	tuple_dictionary_ref(format->dict);
 	format->id = FORMAT_ID_NIL;
 	format->refs = 0;
 	if (tuple_format_register(format) != 0) {
@@ -439,12 +315,6 @@ tuple_format_dup(const struct tuple_format *src)
 		return NULL;
 	}
 	return format;
-
-error_name_hash_reserve:
-	mh_strnu32_delete(format->names);
-error_name_hash_new:
-	free(format);
-	return NULL;
 }
 
 /** @sa declaration for details. */
@@ -531,18 +401,3 @@ box_tuple_format_unref(box_tuple_format_t *format)
 {
 	tuple_format_unref(format);
 }
-
-const char *
-tuple_field_raw_by_name(struct tuple_format *format, const char *tuple,
-			const uint32_t *field_map, const char *name,
-			uint32_t name_len, uint32_t name_hash)
-{
-	if (format->names == NULL)
-		return NULL;
-	struct mh_strnu32_key_t key = {name, name_len, name_hash};
-	mh_int_t rc = mh_strnu32_find(format->names, &key, NULL);
-	if (rc == mh_end(format->names))
-		return NULL;
-	uint32_t fieldno = mh_strnu32_node(format->names, rc)->val;
-	return tuple_field_raw(format, tuple, field_map, fieldno);
-}
diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h
index bb92e14bb..d33c77ae6 100644
--- a/src/box/tuple_format.h
+++ b/src/box/tuple_format.h
@@ -34,6 +34,7 @@
 #include "key_def.h"
 #include "field_def.h"
 #include "errinj.h"
+#include "tuple_dictionary.h"
 
 #if defined(__cplusplus)
 extern "C" {
@@ -95,16 +96,10 @@ struct tuple_field {
 	int32_t offset_slot;
 	/** True if this field is used by an index. */
 	bool is_key_part;
-	/** Tuple field name, specified by a user. Can be NULL. */
-	char *name;
 	/** True, if a field can store NULL. */
 	bool is_nullable;
 };
 
-struct mh_strnu32_t;
-typedef uint32_t (*field_name_hash_f)(const char *str, uint32_t len);
-extern field_name_hash_f field_name_hash;
-
 /**
  * @brief Tuple format
  * Tuple format describes how tuple is stored and information about its fields
@@ -144,17 +139,16 @@ struct tuple_format {
 	uint32_t min_field_count;
 	/* Length of 'fields' array. */
 	uint32_t field_count;
-	/** Field names hash. Key - name, value - field number. */
-	struct mh_strnu32_t *names;
+	/**
+	 * Shared names storage used by all formats of a space.
+	 */
+	struct tuple_dictionary *dict;
 	/* Formats of the fields */
 	struct tuple_field fields[0];
 };
 
 extern struct tuple_format **tuple_formats;
 
-int
-tuple_format_named_fields(const struct tuple_format *format);
-
 static inline uint32_t
 tuple_format_id(const struct tuple_format *format)
 {
@@ -203,7 +197,7 @@ struct tuple_format *
 tuple_format_new(struct tuple_format_vtab *vtab, struct key_def * const *keys,
 		 uint16_t key_count, uint16_t extra_size,
 		 const struct field_def *space_fields,
-		 uint32_t space_field_count);
+		 uint32_t space_field_count, struct tuple_dictionary *dict);
 
 /**
  * Check that two tuple formats are identical.
@@ -221,7 +215,7 @@ tuple_format_eq(const struct tuple_format *a, const struct tuple_format *b);
  * @retval     NULL Memory or format register error.
  */
 struct tuple_format *
-tuple_format_dup(const struct tuple_format *src);
+tuple_format_dup(struct tuple_format *src);
 
 /**
  * Returns the total size of tuple metadata of this format.
@@ -337,10 +331,17 @@ tuple_field_raw(const struct tuple_format *format, const char *tuple,
  * @retval not NULL MessagePack field.
  * @retval     NULL No field with @a name.
  */
-const char *
+static inline const char *
 tuple_field_raw_by_name(struct tuple_format *format, const char *tuple,
 			const uint32_t *field_map, const char *name,
-			uint32_t name_len, uint32_t name_hash);
+			uint32_t name_len, uint32_t name_hash)
+{
+	uint32_t fieldno;
+	if (tuple_fieldno_by_name(format->dict, name, name_len, name_hash,
+				  &fieldno) != 0)
+		return NULL;
+	return tuple_field_raw(format, tuple, field_map, fieldno);
+}
 
 #if defined(__cplusplus)
 } /* extern "C" */
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 3b971b580..59bd6e7d9 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -581,8 +581,9 @@ vinyl_engine_create_space(struct engine *engine, struct space_def *def,
 	rlist_foreach_entry(index_def, key_list, link)
 		keys[key_count++] = index_def->key_def;
 
-	struct tuple_format *format = tuple_format_new(&vy_tuple_format_vtab,
-			keys, key_count, 0, def->fields, def->field_count);
+	struct tuple_format *format =
+		tuple_format_new(&vy_tuple_format_vtab, keys, key_count, 0,
+				 def->fields, def->field_count, def->dict);
 	if (format == NULL) {
 		free(space);
 		return NULL;
@@ -3017,7 +3018,8 @@ vy_join_cb(const struct vy_log_record *record, void *arg)
 		if (ctx->format != NULL)
 			tuple_format_unref(ctx->format);
 		ctx->format = tuple_format_new(&vy_tuple_format_vtab,
-					       &ctx->key_def, 1, 0, NULL, 0);
+					       &ctx->key_def, 1, 0, NULL, 0,
+					       NULL);
 		if (ctx->format == NULL)
 			return -1;
 		tuple_format_ref(ctx->format);
diff --git a/src/box/vy_index.c b/src/box/vy_index.c
index c70b2d8ef..93bf71a81 100644
--- a/src/box/vy_index.c
+++ b/src/box/vy_index.c
@@ -86,7 +86,7 @@ vy_index_env_create(struct vy_index_env *env, const char *path,
 		    void *upsert_thresh_arg)
 {
 	env->key_format = tuple_format_new(&vy_tuple_format_vtab,
-					   NULL, 0, 0, NULL, 0);
+					   NULL, 0, 0, NULL, 0, NULL);
 	if (env->key_format == NULL)
 		return -1;
 	tuple_format_ref(env->key_format);
@@ -167,7 +167,8 @@ vy_index_new(struct vy_index_env *index_env, struct vy_cache_env *cache_env,
 		tuple_format_ref(format);
 	} else {
 		index->disk_format = tuple_format_new(&vy_tuple_format_vtab,
-						      &cmp_def, 1, 0, NULL, 0);
+						      &cmp_def, 1, 0, NULL, 0,
+						      NULL);
 		if (index->disk_format == NULL)
 			goto fail_format;
 		for (uint32_t i = 0; i < cmp_def->part_count; ++i) {
diff --git a/test/unit/vy_iterators_helper.c b/test/unit/vy_iterators_helper.c
index ee390c499..8e94d7738 100644
--- a/test/unit/vy_iterators_helper.c
+++ b/test/unit/vy_iterators_helper.c
@@ -18,7 +18,7 @@ vy_iterator_C_test_init(size_t cache_size)
 	tuple_init(NULL);
 	vy_cache_env_create(&cache_env, cord_slab_cache(), cache_size);
 	vy_key_format = tuple_format_new(&vy_tuple_format_vtab, NULL, 0, 0,
-					 NULL, 0);
+					 NULL, 0, NULL);
 	tuple_format_ref(vy_key_format);
 
 	size_t mem_size = 64 * 1024 * 1024;
@@ -209,7 +209,7 @@ create_test_mem(struct key_def *def)
 	struct key_def * const defs[] = { def };
 	struct tuple_format *format =
 		tuple_format_new(&vy_tuple_format_vtab, defs, def->part_count,
-				 0, NULL, 0);
+				 0, NULL, 0, NULL);
 	fail_if(format == NULL);
 
 	/* Create format with column mask */
@@ -237,7 +237,8 @@ create_test_cache(uint32_t *fields, uint32_t *types,
 	*def = box_key_def_new(fields, types, key_cnt);
 	assert(*def != NULL);
 	vy_cache_create(cache, &cache_env, *def);
-	*format = tuple_format_new(&vy_tuple_format_vtab, def, 1, 0, NULL, 0);
+	*format = tuple_format_new(&vy_tuple_format_vtab, def, 1, 0, NULL, 0,
+				   NULL);
 	tuple_format_ref(*format);
 }
 
diff --git a/test/unit/vy_mem.c b/test/unit/vy_mem.c
index 0aa56a3ae..5d1608771 100644
--- a/test/unit/vy_mem.c
+++ b/test/unit/vy_mem.c
@@ -79,7 +79,8 @@ test_iterator_restore_after_insertion()
 
 	/* Create format */
 	struct tuple_format *format = tuple_format_new(&vy_tuple_format_vtab,
-						       &key_def, 1, 0, NULL, 0);
+						       &key_def, 1, 0, NULL, 0,
+						       NULL);
 	assert(format != NULL);
 	tuple_format_ref(format);
 
diff --git a/test/unit/vy_point_lookup.c b/test/unit/vy_point_lookup.c
index c14f89f36..05abe0efb 100644
--- a/test/unit/vy_point_lookup.c
+++ b/test/unit/vy_point_lookup.c
@@ -44,9 +44,9 @@ test_basic()
 	isnt(key_def, NULL, "key_def is not NULL");
 
 	vy_cache_create(&cache, &cache_env, key_def);
-
 	struct tuple_format *format = tuple_format_new(&vy_tuple_format_vtab,
-						       &key_def, 1, 0, NULL, 0);
+						       &key_def, 1, 0, NULL, 0,
+						       NULL);
 	isnt(format, NULL, "tuple_format_new is not NULL");
 	tuple_format_ref(format);
 
-- 
2.11.0 (Apple Git-81)




More information about the Tarantool-patches mailing list