From 2dcd0ea0c9635cb4197254ae163beda3b80f3f6d Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tsisyk.com>
Date: Mon, 17 Dec 2012 12:19:09 +0400
Subject: [PATCH] Extract HashIndex from index.h(.m), rename tree.h(.m) to
 tree_index.h(.m)

---
 src/box/CMakeLists.txt           |   4 +-
 src/box/box_lua_space.m          |  13 +-
 src/box/hash_index.h             |  44 ++
 src/box/hash_index.m             | 741 +++++++++++++++++++++++++++++++
 src/box/index.h                  |  19 +-
 src/box/index.m                  | 730 +-----------------------------
 src/box/space.m                  |  13 +-
 src/box/{tree.h => tree_index.h} |   9 +-
 src/box/{tree.m => tree_index.m} |  30 +-
 9 files changed, 844 insertions(+), 759 deletions(-)
 create mode 100644 src/box/hash_index.h
 create mode 100644 src/box/hash_index.m
 rename src/box/{tree.h => tree_index.h} (92%)
 rename src/box/{tree.m => tree_index.m} (98%)

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index cd7d531ac5..a4246b11d2 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -25,5 +25,5 @@ add_custom_target(generate_lua_sources}
     DEPENDS ${lua_sources})
 set_property(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${lua_sources})
 
-tarantool_module("box" tuple.m index.m tree.m space.m port.m request.m
-    txn.m box.m ${lua_sources} box_lua.m box_lua_space.m)
+tarantool_module("box" tuple.m index.m hash_index.m tree_index.m space.m
+    port.m request.m txn.m box.m ${lua_sources} box_lua.m box_lua_space.m)
diff --git a/src/box/box_lua_space.m b/src/box/box_lua_space.m
index 72bbfbee3b..78e7236e28 100644
--- a/src/box/box_lua_space.m
+++ b/src/box/box_lua_space.m
@@ -81,17 +81,8 @@ lbox_pushspace(struct lua_State *L, struct space *space)
 		lua_settable(L, -3);
 
 		lua_pushstring(L, "type");
-		switch (space->key_defs[i].type) {
-		case HASH:
-			lua_pushstring(L, "HASH");
-			break;
-		case TREE:
-			lua_pushstring(L, "TREE");
-			break;
-		default:
-			panic("unknown index type %d",
-				space->key_defs[i].parts[0].type);
-		}
+
+		lua_pushstring(L, index_type_strs[space->key_defs[i].type]);
 		lua_settable(L, -3);
 
 		lua_pushstring(L, "key_field");
diff --git a/src/box/hash_index.h b/src/box/hash_index.h
new file mode 100644
index 0000000000..5e1fd9c438
--- /dev/null
+++ b/src/box/hash_index.h
@@ -0,0 +1,44 @@
+#ifndef TARANTOOL_BOX_HASH_INDEX_H_INCLUDED
+#define TARANTOOL_BOX_HASH_INDEX_H_INCLUDED
+/*
+ * 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 "index.h"
+
+@class Index;
+
+@interface HashIndex: Index
+
++ (struct index_traits *) traits;
++ (HashIndex *) alloc: (struct key_def *) key_def :(struct space *) space;
+
+- (void) reserve: (u32) n_tuples;
+@end
+
+#endif /* TARANTOOL_BOX_HASH_INDEX_H_INCLUDED */
diff --git a/src/box/hash_index.m b/src/box/hash_index.m
new file mode 100644
index 0000000000..49773839ee
--- /dev/null
+++ b/src/box/hash_index.m
@@ -0,0 +1,741 @@
+/*
+ * 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 "hash_index.h"
+#include "say.h"
+#include "tuple.h"
+#include "pickle.h"
+#include "exception.h"
+#include "space.h"
+#include "assoc.h"
+#include "errinj.h"
+
+static struct index_traits hash_index_traits = {
+	.allows_partial_key = false,
+};
+
+/* {{{ HashIndex Iterators ****************************************/
+
+struct hash_i32_iterator {
+	struct iterator base; /* Must be the first member. */
+	struct mh_i32ptr_t *hash;
+	mh_int_t h_pos;
+};
+
+struct hash_i64_iterator {
+	struct iterator base;
+	struct mh_i64ptr_t *hash;
+	mh_int_t h_pos;
+};
+
+struct hash_lstr_iterator {
+	struct iterator base;
+	struct mh_lstrptr_t *hash;
+	mh_int_t h_pos;
+};
+
+void
+hash_iterator_free(struct iterator *iterator)
+{
+	assert(iterator->free == hash_iterator_free);
+	free(iterator);
+}
+
+struct tuple *
+hash_iterator_i32_ge(struct iterator *ptr)
+{
+	assert(ptr->free == hash_iterator_free);
+	struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr;
+
+	while (it->h_pos < mh_end(it->hash)) {
+		if (mh_exist(it->hash, it->h_pos))
+			return mh_i32ptr_node(it->hash, it->h_pos++)->val;
+		it->h_pos++;
+	}
+	return NULL;
+}
+
+struct tuple *
+hash_iterator_i64_ge(struct iterator *ptr)
+{
+	assert(ptr->free == hash_iterator_free);
+	struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr;
+
+	while (it->h_pos < mh_end(it->hash)) {
+		if (mh_exist(it->hash, it->h_pos))
+			return mh_i64ptr_node(it->hash, it->h_pos++)->val;
+		it->h_pos++;
+	}
+	return NULL;
+}
+
+struct tuple *
+hash_iterator_lstr_ge(struct iterator *ptr)
+{
+	assert(ptr->free == hash_iterator_free);
+	struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr;
+
+	while (it->h_pos < mh_end(it->hash)) {
+		if (mh_exist(it->hash, it->h_pos))
+			return mh_lstrptr_node(it->hash, it->h_pos++)->val;
+		it->h_pos++;
+	}
+	return NULL;
+}
+
+static struct tuple *
+hash_iterator_eq_next(struct iterator *it __attribute__((unused)))
+{
+	return NULL;
+}
+
+static struct tuple *
+hash_iterator_i32_eq(struct iterator *it)
+{
+	it->next = hash_iterator_eq_next;
+	return hash_iterator_i32_ge(it);
+}
+
+static struct tuple *
+hash_iterator_i64_eq(struct iterator *it)
+{
+	it->next = hash_iterator_eq_next;
+	return hash_iterator_i64_ge(it);
+}
+
+static struct tuple *
+hash_iterator_lstr_eq(struct iterator *it)
+{
+	it->next = hash_iterator_eq_next;
+	return hash_iterator_lstr_ge(it);
+}
+
+/* }}} */
+
+/* {{{ HashIndex -- base class for all hashes. ********************/
+
+@interface Hash32Index: HashIndex {
+	 struct mh_i32ptr_t *int_hash;
+};
+
+- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg;
+@end
+
+@interface Hash64Index: HashIndex {
+	struct mh_i64ptr_t *int64_hash;
+};
+
+- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg;
+@end
+
+@interface HashStrIndex: HashIndex {
+	struct mh_lstrptr_t *str_hash;
+};
+
+- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg;
+@end
+
+@implementation HashIndex
+
++ (struct index_traits *) traits
+{
+	return &hash_index_traits;
+}
+
++ (HashIndex *) alloc: (struct key_def *) key_def :(struct space *) space
+{
+	(void) space;
+
+	/*
+	 * Hash index always has a single-field key.
+	 */
+	switch (key_def->parts[0].type) {
+	case NUM:
+		return [Hash32Index alloc];  /* 32-bit integer hash */
+	case NUM64:
+		return [Hash64Index alloc];  /* 64-bit integer hash */
+	case STRING:
+		return [HashStrIndex alloc]; /* string hash */
+	default:
+		assert(false);
+	}
+}
+
+- (void) reserve: (u32) n_tuples
+{
+	(void) n_tuples;
+	[self subclassResponsibility: _cmd];
+}
+
+- (void) beginBuild
+{
+}
+
+- (void) buildNext: (struct tuple *)tuple
+{
+	[self replace: NULL :tuple :DUP_INSERT];
+}
+
+- (void) endBuild
+{
+}
+
+- (void) build: (Index *) pk
+{
+	u32 n_tuples = [pk size];
+
+	if (n_tuples == 0)
+		return;
+
+	[self reserve: n_tuples];
+
+	say_info("Adding %"PRIu32 " keys to HASH index %"
+		 PRIu32 "...", n_tuples, index_n(self));
+
+	struct iterator *it = pk->position;
+	struct tuple *tuple;
+	[pk initIterator: it :ITER_ALL :NULL :0];
+
+	while ((tuple = it->next(it)))
+	      [self replace: NULL :tuple :DUP_INSERT];
+}
+
+- (void) free
+{
+	[super free];
+}
+
+- (struct tuple *) min
+{
+	tnt_raise(ClientError, :ER_UNSUPPORTED, "Hash index", "min()");
+	return NULL;
+}
+
+- (struct tuple *) max
+{
+	tnt_raise(ClientError, :ER_UNSUPPORTED, "Hash index", "max()");
+	return NULL;
+}
+
+@end
+
+/* }}} */
+
+/* {{{ Hash32Index ************************************************/
+
+static inline struct mh_i32ptr_node_t
+int32_key_to_node(void *key)
+{
+	u32 key_size = load_varint32(&key);
+	if (key_size != 4)
+		tnt_raise(ClientError, :ER_KEY_FIELD_TYPE, "u32");
+	struct mh_i32ptr_node_t node = { .key = *(u32 *) key };
+	return node;
+}
+
+static inline struct mh_i32ptr_node_t
+int32_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
+{
+	void *field = tuple_field(tuple, key_def->parts[0].fieldno);
+	struct mh_i32ptr_node_t node = int32_key_to_node(field);
+	node.val = tuple;
+	return node;
+}
+
+@implementation Hash32Index
+
+- (void) reserve: (u32) n_tuples
+{
+	mh_i32ptr_reserve(int_hash, n_tuples, NULL, NULL);
+}
+
+- (void) free
+{
+	mh_i32ptr_destroy(int_hash);
+	[super free];
+}
+
+- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
+{
+	self = [super init: key_def_arg :space_arg];
+
+	if (self == NULL)
+		return NULL;
+
+	int_hash = mh_i32ptr_init();
+	return self;
+}
+
+- (size_t) size
+{
+	return mh_size(int_hash);
+}
+
+- (struct tuple *) findByKey: (void *) key :(int) part_count
+{
+	assert(key_def->is_unique);
+	check_key_parts(key_def, part_count, false);
+
+	(void) part_count;
+
+	struct tuple *ret = NULL;
+	struct mh_i32ptr_node_t node = int32_key_to_node(key);
+	mh_int_t k = mh_i32ptr_get(int_hash, &node, NULL, NULL);
+	if (k != mh_end(int_hash))
+		ret = mh_i32ptr_node(int_hash, k)->val;
+#ifdef DEBUG
+	say_debug("Hash32Index find(self:%p, key:%i) = %p", self, node.key, ret);
+#endif
+	return ret;
+}
+
+- (struct tuple *) replace: (struct tuple *) old_tuple
+			  :(struct tuple *) new_tuple
+			  :(enum dup_replace_mode) mode
+{
+	struct mh_i32ptr_node_t new_node, old_node;
+	uint32_t errcode;
+
+	if (new_tuple) {
+		struct mh_i32ptr_node_t *dup_node = &old_node;
+		new_node = int32_tuple_to_node(new_tuple, key_def);
+		mh_int_t pos = mh_i32ptr_replace(int_hash, &new_node,
+						 &dup_node, NULL, NULL);
+
+		ERROR_INJECT(ERRINJ_INDEX_ALLOC,
+		{
+			mh_i32ptr_del(int_hash, pos, NULL, NULL);
+			pos = mh_end(int_hash);
+		});
+
+		if (pos == mh_end(int_hash)) {
+			tnt_raise(LoggedError, :ER_MEMORY_ISSUE, (ssize_t) pos,
+				  "int hash", "key");
+		}
+		struct tuple *dup_tuple = dup_node ? dup_node->val : NULL;
+		errcode = replace_check_dup(old_tuple, dup_tuple, mode);
+
+		if (errcode) {
+			mh_i32ptr_remove(int_hash, &new_node, NULL, NULL);
+			if (dup_node) {
+				pos = mh_i32ptr_replace(int_hash, dup_node,
+							NULL, NULL, NULL);
+				if (pos == mh_end(int_hash)) {
+					panic("Failed to allocate memory in "
+					      "recover of int hash");
+				}
+			}
+			tnt_raise(ClientError, :errcode, index_n(self));
+		}
+		if (dup_tuple)
+			return dup_tuple;
+	}
+	if (old_tuple) {
+		old_node = int32_tuple_to_node(old_tuple, key_def);
+		mh_i32ptr_remove(int_hash, &old_node, NULL, NULL);
+	}
+	return old_tuple;
+}
+
+
+- (struct iterator *) allocIterator
+{
+	struct hash_i32_iterator *it = malloc(sizeof(struct hash_i32_iterator));
+	if (it) {
+		memset(it, 0, sizeof(*it));
+		it->base.next = hash_iterator_i32_ge;
+		it->base.free = hash_iterator_free;
+	}
+	return (struct iterator *) it;
+}
+
+- (void) initIterator: (struct iterator *) ptr: (enum iterator_type) type
+                        :(void *) key :(int) part_count
+{
+	assert(ptr->free == hash_iterator_free);
+	struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr;
+	struct mh_i32ptr_node_t node;
+
+	switch (type) {
+	case ITER_GE:
+		if (key != NULL) {
+			check_key_parts(key_def, part_count,
+					traits->allows_partial_key);
+			node = int32_key_to_node(key);
+			it->h_pos = mh_i32ptr_get(int_hash, &node, NULL, NULL);
+			it->base.next = hash_iterator_i32_ge;
+			break;
+		}
+		/* Fall through. */
+	case ITER_ALL:
+		it->h_pos = mh_begin(int_hash);
+		it->base.next = hash_iterator_i32_ge;
+		break;
+	case ITER_EQ:
+		check_key_parts(key_def, part_count,
+				traits->allows_partial_key);
+		node = int32_key_to_node(key);
+		it->h_pos = mh_i32ptr_get(int_hash, &node, NULL, NULL);
+		it->base.next = hash_iterator_i32_eq;
+		break;
+	default:
+		tnt_raise(ClientError, :ER_UNSUPPORTED,
+			  "Hash index", "requested iterator type");
+	}
+	it->hash = int_hash;
+}
+@end
+
+/* }}} */
+
+/* {{{ Hash64Index ************************************************/
+
+static inline struct mh_i64ptr_node_t
+int64_key_to_node(void *key)
+{
+	u32 key_size = load_varint32(&key);
+	if (key_size != 8)
+		tnt_raise(ClientError, :ER_KEY_FIELD_TYPE, "u64");
+	struct mh_i64ptr_node_t node = { .key = *(u64 *) key };
+	return node;
+}
+
+static inline struct mh_i64ptr_node_t
+int64_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
+{
+	void *field = tuple_field(tuple, key_def->parts[0].fieldno);
+	struct mh_i64ptr_node_t node = int64_key_to_node(field);
+	node.val = tuple;
+	return node;
+}
+
+@implementation Hash64Index
+- (void) reserve: (u32) n_tuples
+{
+	mh_i64ptr_reserve(int64_hash, n_tuples, NULL, NULL);
+}
+
+- (void) free
+{
+	mh_i64ptr_destroy(int64_hash);
+	[super free];
+}
+
+- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
+{
+	self = [super init: key_def_arg :space_arg];
+	if (self == NULL)
+		return NULL;
+
+	int64_hash = mh_i64ptr_init();
+	return self;
+}
+
+- (size_t) size
+{
+	return mh_size(int64_hash);
+}
+
+- (struct tuple *) findByKey: (void *) key :(int) part_count
+{
+	assert(key_def->is_unique);
+	check_key_parts(key_def, part_count, false);
+
+	struct tuple *ret = NULL;
+	struct mh_i64ptr_node_t node = int64_key_to_node(key);
+	mh_int_t k = mh_i64ptr_get(int64_hash, &node, NULL, NULL);
+	if (k != mh_end(int64_hash))
+		ret = mh_i64ptr_node(int64_hash, k)->val;
+#ifdef DEBUG
+	say_debug("Hash64Index find(self:%p, key:%"PRIu64") = %p", self, node.key, ret);
+#endif
+	return ret;
+}
+
+- (struct tuple *) replace: (struct tuple *) old_tuple
+			  :(struct tuple *) new_tuple
+			  :(enum dup_replace_mode) mode
+{
+	struct mh_i64ptr_node_t new_node, old_node;
+	uint32_t errcode;
+
+	if (new_tuple) {
+		struct mh_i64ptr_node_t *dup_node = &old_node;
+		new_node = int64_tuple_to_node(new_tuple, key_def);
+		mh_int_t pos = mh_i64ptr_replace(int64_hash, &new_node,
+						 &dup_node, NULL, NULL);
+
+		ERROR_INJECT(ERRINJ_INDEX_ALLOC,
+		{
+			mh_i64ptr_del(int64_hash, pos, NULL, NULL);
+			pos = mh_end(int64_hash);
+		});
+		if (pos == mh_end(int64_hash)) {
+			tnt_raise(LoggedError, :ER_MEMORY_ISSUE, (ssize_t) pos,
+				  "int64 hash", "key");
+		}
+		struct tuple *dup_tuple = dup_node ? dup_node->val : NULL;
+		errcode = replace_check_dup(old_tuple, dup_tuple, mode);
+
+		if (errcode) {
+			mh_i64ptr_remove(int64_hash, &new_node, NULL, NULL);
+			if (dup_node) {
+				pos = mh_i64ptr_replace(int64_hash, dup_node,
+							NULL, NULL, NULL);
+				if (pos == mh_end(int64_hash)) {
+					panic("Failed to allocate memory in "
+					      "recover of int64 hash");
+				}
+			}
+			tnt_raise(ClientError, :errcode, index_n(self));
+		}
+		if (dup_tuple)
+			return dup_tuple;
+	}
+	if (old_tuple) {
+		old_node = int64_tuple_to_node(old_tuple, key_def);
+		mh_i64ptr_remove(int64_hash, &old_node, NULL, NULL);
+	}
+	return old_tuple;
+}
+
+
+- (struct iterator *) allocIterator
+{
+	struct hash_i64_iterator *it = malloc(sizeof(struct hash_i64_iterator));
+	if (it) {
+		memset(it, 0, sizeof(*it));
+		it->base.next = hash_iterator_i64_ge;
+		it->base.free = hash_iterator_free;
+	}
+	return (struct iterator *) it;
+}
+
+
+- (void) initIterator: (struct iterator *) ptr: (enum iterator_type) type
+                        :(void *) key :(int) part_count
+{
+	(void) part_count;
+	assert(ptr->free == hash_iterator_free);
+	struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr;
+	struct mh_i64ptr_node_t node;
+
+	switch (type) {
+	case ITER_GE:
+		if (key != NULL) {
+			check_key_parts(key_def, part_count,
+					traits->allows_partial_key);
+			node = int64_key_to_node(key);
+			it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL, NULL);
+			it->base.next = hash_iterator_i64_ge;
+			break;
+		}
+		/* Fallthrough. */
+	case ITER_ALL:
+		it->base.next = hash_iterator_i64_ge;
+		it->h_pos = mh_begin(int64_hash);
+		break;
+	case ITER_EQ:
+		check_key_parts(key_def, part_count,
+				traits->allows_partial_key);
+		node = int64_key_to_node(key);
+		it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL, NULL);
+		it->base.next = hash_iterator_i64_eq;
+		break;
+	default:
+		tnt_raise(ClientError, :ER_UNSUPPORTED,
+			  "Hash index", "requested iterator type");
+	}
+	it->hash = int64_hash;
+}
+@end
+
+/* }}} */
+
+/* {{{ HashStrIndex ***********************************************/
+
+static inline struct mh_lstrptr_node_t
+lstrptr_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
+{
+	void *field = tuple_field(tuple, key_def->parts[0].fieldno);
+	if (field == NULL)
+		tnt_raise(ClientError, :ER_NO_SUCH_FIELD,
+			  key_def->parts[0].fieldno);
+
+	struct mh_lstrptr_node_t node = { .key = field, .val = tuple };
+	return node;
+}
+
+
+@implementation HashStrIndex
+- (void) reserve: (u32) n_tuples
+{
+	mh_lstrptr_reserve(str_hash, n_tuples, NULL, NULL);
+}
+
+- (void) free
+{
+	mh_lstrptr_destroy(str_hash);
+	[super free];
+}
+
+- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
+{
+	self = [super init: key_def_arg :space_arg];
+	if (self == NULL)
+		return NULL;
+
+	str_hash = mh_lstrptr_init();
+	return self;
+}
+
+- (size_t) size
+{
+	return mh_size(str_hash);
+}
+
+- (struct tuple *) findByKey: (void *) key :(int) part_count
+{
+	assert(key_def->is_unique);
+	check_key_parts(key_def, part_count, false);
+
+	struct tuple *ret = NULL;
+	const struct mh_lstrptr_node_t node = { .key = key };
+	mh_int_t k = mh_lstrptr_get(str_hash, &node, NULL, NULL);
+	if (k != mh_end(str_hash))
+		ret = mh_lstrptr_node(str_hash, k)->val;
+#ifdef DEBUG
+	u32 key_size = load_varint32(&key);
+	say_debug("HashStrIndex find(self:%p, key:(%i)'%.*s') = %p",
+		  self, key_size, key_size, (u8 *)key, ret);
+#endif
+	return ret;
+}
+
+- (struct tuple *) replace: (struct tuple *) old_tuple
+			  :(struct tuple *) new_tuple
+			  :(enum dup_replace_mode) mode
+{
+	struct mh_lstrptr_node_t new_node, old_node;
+	uint32_t errcode;
+
+	if (new_tuple) {
+		struct mh_lstrptr_node_t *dup_node = &old_node;
+		new_node = lstrptr_tuple_to_node(new_tuple, key_def);
+		mh_int_t pos = mh_lstrptr_replace(str_hash, &new_node,
+						  &dup_node, NULL, NULL);
+
+		ERROR_INJECT(ERRINJ_INDEX_ALLOC,
+		{
+			mh_lstrptr_del(str_hash, pos, NULL, NULL);
+			pos = mh_end(str_hash);
+		});
+
+		if (pos == mh_end(str_hash)) {
+			tnt_raise(LoggedError, :ER_MEMORY_ISSUE, (ssize_t) pos,
+				  "str hash", "key");
+		}
+		struct tuple *dup_tuple = dup_node ? dup_node->val : NULL;
+		errcode = replace_check_dup(old_tuple, dup_tuple, mode);
+
+		if (errcode) {
+			mh_lstrptr_remove(str_hash, &new_node, NULL, NULL);
+			if (dup_node) {
+				pos = mh_lstrptr_replace(str_hash, dup_node,
+							 NULL, NULL, NULL);
+				if (pos == mh_end(str_hash)) {
+					panic("Failed to allocate memory in "
+					      "recover of str hash");
+				}
+			}
+			tnt_raise(ClientError, :errcode, index_n(self));
+		}
+		if (dup_tuple)
+			return dup_tuple;
+	}
+	if (old_tuple) {
+		old_node = lstrptr_tuple_to_node(old_tuple, key_def);
+		mh_lstrptr_remove(str_hash, &old_node, NULL, NULL);
+	}
+	return old_tuple;
+}
+
+- (struct iterator *) allocIterator
+{
+	struct hash_lstr_iterator *it = malloc(sizeof(struct hash_lstr_iterator));
+	if (it) {
+		memset(it, 0, sizeof(*it));
+		it->base.next = hash_iterator_lstr_ge;
+		it->base.free = hash_iterator_free;
+	}
+	return (struct iterator *) it;
+}
+
+
+- (void) initIterator: (struct iterator *) ptr
+			:(enum iterator_type) type
+                        :(void *) key :(int) part_count
+{
+	(void) part_count;
+
+	assert(ptr->free == hash_iterator_free);
+	struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr;
+	struct mh_lstrptr_node_t node;
+
+	switch (type) {
+	case ITER_GE:
+		if (key != NULL) {
+			check_key_parts(key_def, part_count,
+					traits->allows_partial_key);
+			node.key = key;
+			it->h_pos = mh_lstrptr_get(str_hash, &node, NULL, NULL);
+			it->base.next = hash_iterator_lstr_ge;
+			break;
+		}
+		/* Fall through. */
+	case ITER_ALL:
+		it->base.next = hash_iterator_lstr_ge;
+		it->h_pos = mh_begin(str_hash);
+		break;
+	case ITER_EQ:
+		check_key_parts(key_def, part_count,
+				traits->allows_partial_key);
+		node.key = key;
+		it->h_pos = mh_lstrptr_get(str_hash, &node, NULL, NULL);
+		it->base.next = hash_iterator_lstr_eq;
+		break;
+	default:
+		tnt_raise(ClientError, :ER_UNSUPPORTED,
+			  "Hash index", "requested iterator type");
+	}
+	it->hash = str_hash;
+}
+@end
+
+/* }}} */
+
diff --git a/src/box/index.h b/src/box/index.h
index fb81e2e79f..dba734b9d4 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -29,12 +29,12 @@
  * SUCH DAMAGE.
  */
 #import "object.h"
+
 #include <stdbool.h>
 #include <util.h>
 
 struct tuple;
 struct space;
-struct index;
 
 /*
  * Possible field data types. Can't use STRS/ENUM macros for them,
@@ -44,7 +44,11 @@ struct index;
 enum field_data_type { UNKNOWN = -1, NUM = 0, NUM64, STRING, field_data_type_MAX };
 extern const char *field_data_type_strs[];
 
-enum index_type { HASH, TREE, index_type_MAX };
+#define INDEX_TYPE(_)                                             \
+	_(HASH,  0)       /* HASH Index  */                       \
+	_(TREE,  1)       /* TREE Index  */                       \
+
+ENUM(index_type, INDEX_TYPE);
 extern const char *index_type_strs[];
 
 /**
@@ -86,6 +90,11 @@ iterator_type_is_reverse(enum iterator_type type)
 	return type == ITER_REQ || type == ITER_LT || type == ITER_LE;
 }
 
+struct iterator {
+	struct tuple *(*next)(struct iterator *);
+	void (*free)(struct iterator *);
+};
+
 /** Descriptor of a single part in a multipart key. */
 struct key_part {
 	int fieldno;
@@ -206,14 +215,8 @@ enum dup_replace_mode {
 - (void) initIterator: (struct iterator *) iterator
 		     :(enum iterator_type) type
 		     :(void *) key :(int) part_count;
-
 @end
 
-struct iterator {
-	struct tuple *(*next)(struct iterator *);
-	void (*free)(struct iterator *);
-};
-
 void
 check_key_parts(struct key_def *key_def, int part_count,
 		bool partial_key_allowed);
diff --git a/src/box/index.m b/src/box/index.m
index 8d02ed5238..5ec5480ccd 100644
--- a/src/box/index.m
+++ b/src/box/index.m
@@ -27,28 +27,23 @@
  * SUCH DAMAGE.
  */
 #include "index.h"
-#include "tree.h"
-#include "say.h"
+#include "hash_index.h"
+#include "tree_index.h"
 #include "tuple.h"
-#include "pickle.h"
+#include "say.h"
 #include "exception.h"
 #include "space.h"
-#include "assoc.h"
-#include "errinj.h"
 
 static struct index_traits index_traits = {
 	.allows_partial_key = false,
 };
 
-static struct index_traits hash_index_traits = {
-	.allows_partial_key = false,
-};
-
 const char *field_data_type_strs[] = {"NUM", "NUM64", "STR", "\0"};
-const char *index_type_strs[] = { "HASH", "TREE", "\0" };
-
+STRS(index_type, INDEX_TYPE);
 STRS(iterator_type, ITERATOR_TYPE);
 
+/* {{{ Utilities. **********************************************/
+
 void
 check_key_parts(struct key_def *key_def,
 		int part_count, bool partial_key_allowed)
@@ -93,36 +88,12 @@ replace_check_dup(struct tuple *old_tuple,
 	return 0;
 }
 
+/* }}} */
 
 /* {{{ Index -- base class for all indexes. ********************/
 
-@interface HashIndex: Index
-- (void) reserve: (u32) n_tuples;
-@end
-
-@interface HashStrIndex: HashIndex {
-	 struct mh_lstrptr_t *str_hash;
-};
-@end
-
-@interface Hash64Index: HashIndex {
-	struct mh_i64ptr_t *int64_hash;
-};
-@end
-
-@interface Hash32Index: HashIndex {
-	 struct mh_i32ptr_t *int_hash;
-};
-@end
-
-
 @implementation Index
 
-@class Hash32Index;
-@class Hash64Index;
-@class HashStrIndex;
-@class TreeIndex;
-
 + (struct index_traits *) traits
 {
 	return &index_traits;
@@ -134,37 +105,25 @@ replace_check_dup(struct tuple *old_tuple,
 {
 	switch (type) {
 	case HASH:
-		/* Hash index, check key type.
-		 * Hash index always has a single-field key.
-		 */
-		switch (key_def->parts[0].type) {
-		case NUM:
-			return [Hash32Index alloc]; /* 32-bit integer hash */
-		case NUM64:
-			return [Hash64Index alloc]; /* 64-bit integer hash */
-		case STRING:
-			return [HashStrIndex alloc]; /* string hash */
-		default:
-			break;
-		}
-		break;
+		return [HashIndex alloc: key_def :space];
 	case TREE:
 		return [TreeIndex alloc: key_def :space];
 	default:
-		break;
+		assert(false);
 	}
-	panic("unsupported index type");
 }
 
-- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
+- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg;
 {
 	self = [super init];
-	if (self) {
-		traits = [object_getClass(self) traits];
-		key_def = key_def_arg;
-		space = space_arg;
-		position = [self allocIterator];
-	}
+	if (self == NULL)
+		return NULL;
+
+	traits = [object_getClass(self) traits];
+	key_def = key_def_arg;
+	space = space_arg;
+	position = [self allocIterator];
+
 	return self;
 }
 
@@ -255,656 +214,3 @@ replace_check_dup(struct tuple *old_tuple,
 @end
 
 /* }}} */
-
-/* {{{ HashIndex -- base class for all hashes. ********************/
-
-struct hash_i32_iterator {
-	struct iterator base; /* Must be the first member. */
-	struct mh_i32ptr_t *hash;
-	mh_int_t h_pos;
-};
-
-struct hash_i64_iterator {
-	struct iterator base;
-	struct mh_i64ptr_t *hash;
-	mh_int_t h_pos;
-};
-
-struct hash_lstr_iterator {
-	struct iterator base;
-	struct mh_lstrptr_t *hash;
-	mh_int_t h_pos;
-};
-
-void
-hash_iterator_free(struct iterator *iterator)
-{
-	assert(iterator->free == hash_iterator_free);
-	free(iterator);
-}
-
-struct tuple *
-hash_iterator_i32_ge(struct iterator *ptr)
-{
-	assert(ptr->free == hash_iterator_free);
-	struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr;
-
-	while (it->h_pos < mh_end(it->hash)) {
-		if (mh_exist(it->hash, it->h_pos))
-			return mh_i32ptr_node(it->hash, it->h_pos++)->val;
-		it->h_pos++;
-	}
-	return NULL;
-}
-
-struct tuple *
-hash_iterator_i64_ge(struct iterator *ptr)
-{
-	assert(ptr->free == hash_iterator_free);
-	struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr;
-
-	while (it->h_pos < mh_end(it->hash)) {
-		if (mh_exist(it->hash, it->h_pos))
-			return mh_i64ptr_node(it->hash, it->h_pos++)->val;
-		it->h_pos++;
-	}
-	return NULL;
-}
-
-struct tuple *
-hash_iterator_lstr_ge(struct iterator *ptr)
-{
-	assert(ptr->free == hash_iterator_free);
-	struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr;
-
-	while (it->h_pos < mh_end(it->hash)) {
-		if (mh_exist(it->hash, it->h_pos))
-			return mh_lstrptr_node(it->hash, it->h_pos++)->val;
-		it->h_pos++;
-	}
-	return NULL;
-}
-
-static struct tuple *
-hash_iterator_eq_next(struct iterator *it __attribute__((unused)))
-{
-	return NULL;
-}
-
-static struct tuple *
-hash_iterator_i32_eq(struct iterator *it)
-{
-	it->next = hash_iterator_eq_next;
-	return hash_iterator_i32_ge(it);
-}
-
-static struct tuple *
-hash_iterator_i64_eq(struct iterator *it)
-{
-	it->next = hash_iterator_eq_next;
-	return hash_iterator_i64_ge(it);
-}
-
-static struct tuple *
-hash_iterator_lstr_eq(struct iterator *it)
-{
-	it->next = hash_iterator_eq_next;
-	return hash_iterator_lstr_ge(it);
-}
-
-@implementation HashIndex
-
-+ (struct index_traits *) traits
-{
-	return &hash_index_traits;
-}
-
-- (void) reserve: (u32) n_tuples
-{
-	(void) n_tuples;
-	[self subclassResponsibility: _cmd];
-}
-
-- (void) beginBuild
-{
-}
-
-- (void) buildNext: (struct tuple *)tuple
-{
-	[self replace: NULL :tuple :DUP_INSERT];
-}
-
-- (void) endBuild
-{
-}
-
-- (void) build: (Index *) pk
-{
-	u32 n_tuples = [pk size];
-
-	if (n_tuples == 0)
-		return;
-
-	[self reserve: n_tuples];
-
-	say_info("Adding %"PRIu32 " keys to HASH index %"
-		 PRIu32 "...", n_tuples, index_n(self));
-
-	struct iterator *it = pk->position;
-	struct tuple *tuple;
-	[pk initIterator: it :ITER_ALL :NULL :0];
-
-	while ((tuple = it->next(it)))
-	      [self replace: NULL :tuple :DUP_INSERT];
-}
-
-- (void) free
-{
-	[super free];
-}
-
-- (struct tuple *) min
-{
-	tnt_raise(ClientError, :ER_UNSUPPORTED, "Hash index", "min()");
-	return NULL;
-}
-
-- (struct tuple *) max
-{
-	tnt_raise(ClientError, :ER_UNSUPPORTED, "Hash index", "max()");
-	return NULL;
-}
-
-@end
-
-/* }}} */
-
-/* {{{ Hash32Index ************************************************/
-
-static inline struct mh_i32ptr_node_t
-int32_key_to_node(void *key)
-{
-	u32 key_size = load_varint32(&key);
-	if (key_size != 4)
-		tnt_raise(ClientError, :ER_KEY_FIELD_TYPE, "u32");
-	struct mh_i32ptr_node_t node = { .key = *(u32 *) key };
-	return node;
-}
-
-static inline struct mh_i32ptr_node_t
-int32_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
-{
-	void *field = tuple_field(tuple, key_def->parts[0].fieldno);
-	struct mh_i32ptr_node_t node = int32_key_to_node(field);
-	node.val = tuple;
-	return node;
-}
-
-@implementation Hash32Index
-
-- (void) reserve: (u32) n_tuples
-{
-	mh_i32ptr_reserve(int_hash, n_tuples, NULL, NULL);
-}
-
-- (void) free
-{
-	mh_i32ptr_destroy(int_hash);
-	[super free];
-}
-
-- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
-{
-	self = [super init: key_def_arg :space_arg];
-	if (self) {
-		int_hash = mh_i32ptr_init();
-	}
-	return self;
-}
-
-- (size_t) size
-{
-	return mh_size(int_hash);
-}
-
-- (struct tuple *) findByKey: (void *) key :(int) part_count
-{
-	assert(key_def->is_unique);
-	check_key_parts(key_def, part_count, false);
-
-	(void) part_count;
-
-	struct tuple *ret = NULL;
-	struct mh_i32ptr_node_t node = int32_key_to_node(key);
-	mh_int_t k = mh_i32ptr_get(int_hash, &node, NULL, NULL);
-	if (k != mh_end(int_hash))
-		ret = mh_i32ptr_node(int_hash, k)->val;
-#ifdef DEBUG
-	say_debug("Hash32Index find(self:%p, key:%i) = %p", self, node.key, ret);
-#endif
-	return ret;
-}
-
-- (struct tuple *) replace: (struct tuple *) old_tuple
-			  :(struct tuple *) new_tuple
-			  :(enum dup_replace_mode) mode
-{
-	struct mh_i32ptr_node_t new_node, old_node;
-	uint32_t errcode;
-
-	if (new_tuple) {
-		struct mh_i32ptr_node_t *dup_node = &old_node;
-		new_node = int32_tuple_to_node(new_tuple, key_def);
-		mh_int_t pos = mh_i32ptr_replace(int_hash, &new_node,
-						 &dup_node, NULL, NULL);
-
-		ERROR_INJECT(ERRINJ_INDEX_ALLOC,
-		{
-			mh_i32ptr_del(int_hash, pos, NULL, NULL);
-			pos = mh_end(int_hash);
-		});
-
-		if (pos == mh_end(int_hash)) {
-			tnt_raise(LoggedError, :ER_MEMORY_ISSUE, (ssize_t) pos,
-				  "int hash", "key");
-		}
-		struct tuple *dup_tuple = dup_node ? dup_node->val : NULL;
-		errcode = replace_check_dup(old_tuple, dup_tuple, mode);
-
-		if (errcode) {
-			mh_i32ptr_remove(int_hash, &new_node, NULL, NULL);
-			if (dup_node) {
-				pos = mh_i32ptr_replace(int_hash, dup_node,
-							NULL, NULL, NULL);
-				if (pos == mh_end(int_hash)) {
-					panic("Failed to allocate memory in "
-					      "recover of int hash");
-				}
-			}
-			tnt_raise(ClientError, :errcode, index_n(self));
-		}
-		if (dup_tuple)
-			return dup_tuple;
-	}
-	if (old_tuple) {
-		old_node = int32_tuple_to_node(old_tuple, key_def);
-		mh_i32ptr_remove(int_hash, &old_node, NULL, NULL);
-	}
-	return old_tuple;
-}
-
-
-- (struct iterator *) allocIterator
-{
-	struct hash_i32_iterator *it = malloc(sizeof(struct hash_i32_iterator));
-	if (it) {
-		memset(it, 0, sizeof(*it));
-		it->base.next = hash_iterator_i32_ge;
-		it->base.free = hash_iterator_free;
-	}
-	return (struct iterator *) it;
-}
-
-- (void) initIterator: (struct iterator *) ptr: (enum iterator_type) type
-                        :(void *) key :(int) part_count
-{
-	assert(ptr->free == hash_iterator_free);
-	struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr;
-	struct mh_i32ptr_node_t node;
-
-	switch (type) {
-	case ITER_GE:
-		if (key != NULL) {
-			check_key_parts(key_def, part_count,
-					traits->allows_partial_key);
-			node = int32_key_to_node(key);
-			it->h_pos = mh_i32ptr_get(int_hash, &node, NULL, NULL);
-			it->base.next = hash_iterator_i32_ge;
-			break;
-		}
-		/* Fall through. */
-	case ITER_ALL:
-		it->h_pos = mh_begin(int_hash);
-		it->base.next = hash_iterator_i32_ge;
-		break;
-	case ITER_EQ:
-		check_key_parts(key_def, part_count,
-				traits->allows_partial_key);
-		node = int32_key_to_node(key);
-		it->h_pos = mh_i32ptr_get(int_hash, &node, NULL, NULL);
-		it->base.next = hash_iterator_i32_eq;
-		break;
-	default:
-		tnt_raise(ClientError, :ER_UNSUPPORTED,
-			  "Hash index", "requested iterator type");
-	}
-	it->hash = int_hash;
-}
-@end
-
-/* }}} */
-
-/* {{{ Hash64Index ************************************************/
-
-static inline struct mh_i64ptr_node_t
-int64_key_to_node(void *key)
-{
-	u32 key_size = load_varint32(&key);
-	if (key_size != 8)
-		tnt_raise(ClientError, :ER_KEY_FIELD_TYPE, "u64");
-	struct mh_i64ptr_node_t node = { .key = *(u64 *) key };
-	return node;
-}
-
-static inline struct mh_i64ptr_node_t
-int64_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
-{
-	void *field = tuple_field(tuple, key_def->parts[0].fieldno);
-	struct mh_i64ptr_node_t node = int64_key_to_node(field);
-	node.val = tuple;
-	return node;
-}
-
-@implementation Hash64Index
-- (void) reserve: (u32) n_tuples
-{
-	mh_i64ptr_reserve(int64_hash, n_tuples, NULL, NULL);
-}
-
-- (void) free
-{
-	mh_i64ptr_destroy(int64_hash);
-	[super free];
-}
-
-- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
-{
-	self = [super init: key_def_arg :space_arg];
-	if (self) {
-		int64_hash = mh_i64ptr_init();
-	}
-	return self;
-}
-
-- (size_t) size
-{
-	return mh_size(int64_hash);
-}
-
-- (struct tuple *) findByKey: (void *) key :(int) part_count
-{
-	assert(key_def->is_unique);
-	check_key_parts(key_def, part_count, false);
-
-	struct tuple *ret = NULL;
-	struct mh_i64ptr_node_t node = int64_key_to_node(key);
-	mh_int_t k = mh_i64ptr_get(int64_hash, &node, NULL, NULL);
-	if (k != mh_end(int64_hash))
-		ret = mh_i64ptr_node(int64_hash, k)->val;
-#ifdef DEBUG
-	say_debug("Hash64Index find(self:%p, key:%"PRIu64") = %p", self, node.key, ret);
-#endif
-	return ret;
-}
-
-- (struct tuple *) replace: (struct tuple *) old_tuple
-			  :(struct tuple *) new_tuple
-			  :(enum dup_replace_mode) mode
-{
-	struct mh_i64ptr_node_t new_node, old_node;
-	uint32_t errcode;
-
-	if (new_tuple) {
-		struct mh_i64ptr_node_t *dup_node = &old_node;
-		new_node = int64_tuple_to_node(new_tuple, key_def);
-		mh_int_t pos = mh_i64ptr_replace(int64_hash, &new_node,
-						 &dup_node, NULL, NULL);
-
-		ERROR_INJECT(ERRINJ_INDEX_ALLOC,
-		{
-			mh_i64ptr_del(int64_hash, pos, NULL, NULL);
-			pos = mh_end(int64_hash);
-		});
-		if (pos == mh_end(int64_hash)) {
-			tnt_raise(LoggedError, :ER_MEMORY_ISSUE, (ssize_t) pos,
-				  "int64 hash", "key");
-		}
-		struct tuple *dup_tuple = dup_node ? dup_node->val : NULL;
-		errcode = replace_check_dup(old_tuple, dup_tuple, mode);
-
-		if (errcode) {
-			mh_i64ptr_remove(int64_hash, &new_node, NULL, NULL);
-			if (dup_node) {
-				pos = mh_i64ptr_replace(int64_hash, dup_node,
-							NULL, NULL, NULL);
-				if (pos == mh_end(int64_hash)) {
-					panic("Failed to allocate memory in "
-					      "recover of int64 hash");
-				}
-			}
-			tnt_raise(ClientError, :errcode, index_n(self));
-		}
-		if (dup_tuple)
-			return dup_tuple;
-	}
-	if (old_tuple) {
-		old_node = int64_tuple_to_node(old_tuple, key_def);
-		mh_i64ptr_remove(int64_hash, &old_node, NULL, NULL);
-	}
-	return old_tuple;
-}
-
-
-- (struct iterator *) allocIterator
-{
-	struct hash_i64_iterator *it = malloc(sizeof(struct hash_i64_iterator));
-	if (it) {
-		memset(it, 0, sizeof(*it));
-		it->base.next = hash_iterator_i64_ge;
-		it->base.free = hash_iterator_free;
-	}
-	return (struct iterator *) it;
-}
-
-
-- (void) initIterator: (struct iterator *) ptr: (enum iterator_type) type
-                        :(void *) key :(int) part_count
-{
-	(void) part_count;
-	assert(ptr->free == hash_iterator_free);
-	struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr;
-	struct mh_i64ptr_node_t node;
-
-	switch (type) {
-	case ITER_GE:
-		if (key != NULL) {
-			check_key_parts(key_def, part_count,
-					traits->allows_partial_key);
-			node = int64_key_to_node(key);
-			it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL, NULL);
-			it->base.next = hash_iterator_i64_ge;
-			break;
-		}
-		/* Fallthrough. */
-	case ITER_ALL:
-		it->base.next = hash_iterator_i64_ge;
-		it->h_pos = mh_begin(int64_hash);
-		break;
-	case ITER_EQ:
-		check_key_parts(key_def, part_count,
-				traits->allows_partial_key);
-		node = int64_key_to_node(key);
-		it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL, NULL);
-		it->base.next = hash_iterator_i64_eq;
-		break;
-	default:
-		tnt_raise(ClientError, :ER_UNSUPPORTED,
-			  "Hash index", "requested iterator type");
-	}
-	it->hash = int64_hash;
-}
-@end
-
-/* }}} */
-
-/* {{{ HashStrIndex ***********************************************/
-
-static inline struct mh_lstrptr_node_t
-lstrptr_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
-{
-	void *field = tuple_field(tuple, key_def->parts[0].fieldno);
-	if (field == NULL)
-		tnt_raise(ClientError, :ER_NO_SUCH_FIELD,
-			  key_def->parts[0].fieldno);
-
-	struct mh_lstrptr_node_t node = { .key = field, .val = tuple };
-	return node;
-}
-
-
-@implementation HashStrIndex
-- (void) reserve: (u32) n_tuples
-{
-	mh_lstrptr_reserve(str_hash, n_tuples, NULL, NULL);
-}
-
-- (void) free
-{
-	mh_lstrptr_destroy(str_hash);
-	[super free];
-}
-
-- (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
-{
-	self = [super init: key_def_arg :space_arg];
-	if (self) {
-		str_hash = mh_lstrptr_init();
-	}
-	return self;
-}
-
-- (size_t) size
-{
-	return mh_size(str_hash);
-}
-
-- (struct tuple *) findByKey: (void *) key :(int) part_count
-{
-	assert(key_def->is_unique);
-	check_key_parts(key_def, part_count, false);
-
-	struct tuple *ret = NULL;
-	const struct mh_lstrptr_node_t node = { .key = key };
-	mh_int_t k = mh_lstrptr_get(str_hash, &node, NULL, NULL);
-	if (k != mh_end(str_hash))
-		ret = mh_lstrptr_node(str_hash, k)->val;
-#ifdef DEBUG
-	u32 key_size = load_varint32(&key);
-	say_debug("HashStrIndex find(self:%p, key:(%i)'%.*s') = %p",
-		  self, key_size, key_size, (u8 *)key, ret);
-#endif
-	return ret;
-}
-
-- (struct tuple *) replace: (struct tuple *) old_tuple
-			  :(struct tuple *) new_tuple
-			  :(enum dup_replace_mode) mode
-{
-	struct mh_lstrptr_node_t new_node, old_node;
-	uint32_t errcode;
-
-	if (new_tuple) {
-		struct mh_lstrptr_node_t *dup_node = &old_node;
-		new_node = lstrptr_tuple_to_node(new_tuple, key_def);
-		mh_int_t pos = mh_lstrptr_replace(str_hash, &new_node,
-						  &dup_node, NULL, NULL);
-
-		ERROR_INJECT(ERRINJ_INDEX_ALLOC,
-		{
-			mh_lstrptr_del(str_hash, pos, NULL, NULL);
-			pos = mh_end(str_hash);
-		});
-
-		if (pos == mh_end(str_hash)) {
-			tnt_raise(LoggedError, :ER_MEMORY_ISSUE, (ssize_t) pos,
-				  "str hash", "key");
-		}
-		struct tuple *dup_tuple = dup_node ? dup_node->val : NULL;
-		errcode = replace_check_dup(old_tuple, dup_tuple, mode);
-
-		if (errcode) {
-			mh_lstrptr_remove(str_hash, &new_node, NULL, NULL);
-			if (dup_node) {
-				pos = mh_lstrptr_replace(str_hash, dup_node,
-							 NULL, NULL, NULL);
-				if (pos == mh_end(str_hash)) {
-					panic("Failed to allocate memory in "
-					      "recover of str hash");
-				}
-			}
-			tnt_raise(ClientError, :errcode, index_n(self));
-		}
-		if (dup_tuple)
-			return dup_tuple;
-	}
-	if (old_tuple) {
-		old_node = lstrptr_tuple_to_node(old_tuple, key_def);
-		mh_lstrptr_remove(str_hash, &old_node, NULL, NULL);
-	}
-	return old_tuple;
-}
-
-- (struct iterator *) allocIterator
-{
-	struct hash_lstr_iterator *it = malloc(sizeof(struct hash_lstr_iterator));
-	if (it) {
-		memset(it, 0, sizeof(*it));
-		it->base.next = hash_iterator_lstr_ge;
-		it->base.free = hash_iterator_free;
-	}
-	return (struct iterator *) it;
-}
-
-
-- (void) initIterator: (struct iterator *) ptr
-			:(enum iterator_type) type
-                        :(void *) key :(int) part_count
-{
-	(void) part_count;
-
-	assert(ptr->free == hash_iterator_free);
-	struct hash_lstr_iterator *it = (struct hash_lstr_iterator *) ptr;
-	struct mh_lstrptr_node_t node;
-
-	switch (type) {
-	case ITER_GE:
-		if (key != NULL) {
-			check_key_parts(key_def, part_count,
-					traits->allows_partial_key);
-			node.key = key;
-			it->h_pos = mh_lstrptr_get(str_hash, &node, NULL, NULL);
-			it->base.next = hash_iterator_lstr_ge;
-			break;
-		}
-		/* Fall through. */
-	case ITER_ALL:
-		it->base.next = hash_iterator_lstr_ge;
-		it->h_pos = mh_begin(str_hash);
-		break;
-	case ITER_EQ:
-		check_key_parts(key_def, part_count,
-				traits->allows_partial_key);
-		node.key = key;
-		it->h_pos = mh_lstrptr_get(str_hash, &node, NULL, NULL);
-		it->base.next = hash_iterator_lstr_eq;
-		break;
-	default:
-		tnt_raise(ClientError, :ER_UNSUPPORTED,
-			  "Hash index", "requested iterator type");
-	}
-	it->hash = str_hash;
-}
-@end
-
-/* }}} */
-
diff --git a/src/box/space.m b/src/box/space.m
index 523f7ae05e..a202fd2127 100644
--- a/src/box/space.m
+++ b/src/box/space.m
@@ -216,11 +216,9 @@ key_init(struct key_def *def, struct tarantool_cfg_space_index *cfg_index)
 {
 	def->max_fieldno = 0;
 	def->part_count = 0;
-	if (strcmp(cfg_index->type, "HASH") == 0)
-		def->type = HASH;
-	else if (strcmp(cfg_index->type, "TREE") == 0)
-		def->type = TREE;
-	else
+
+	def->type = STR2ENUM(index_type, cfg_index->type);
+	if (def->type == index_type_MAX)
 		panic("Wrong index type: %s", cfg_index->type);
 
 	/* Calculate key part count and maximal field number. */
@@ -370,8 +368,9 @@ space_config()
 			typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j];
 			enum index_type type = STR2ENUM(index_type, cfg_index->type);
 			struct key_def *key_def = &space->key_defs[j];
-			Index *index = [Index alloc: type :key_def :space];
-			[index init: key_def :space];
+			Index *index = [[Index alloc :type :key_def :space]
+					init :key_def :space];
+			assert (index != NULL);
 			space->index[j] = index;
 		}
 
diff --git a/src/box/tree.h b/src/box/tree_index.h
similarity index 92%
rename from src/box/tree.h
rename to src/box/tree_index.h
index 6cacb96a19..d1ff337d80 100644
--- a/src/box/tree.h
+++ b/src/box/tree_index.h
@@ -1,5 +1,5 @@
-#ifndef TARANTOOL_BOX_TREE_H_INCLUDED
-#define TARANTOOL_BOX_TREE_H_INCLUDED
+#ifndef TARANTOOL_BOX_TREE_INDEX_H_INCLUDED
+#define TARANTOOL_BOX_TREE_INDEX_H_INCLUDED
 /*
  * Redistribution and use in source and binary forms, with or
  * without modification, are permitted provided that the following
@@ -46,11 +46,8 @@ typedef int (*tree_cmp_t)(const void *, const void *, void *);
 };
 
 + (struct index_traits *) traits;
-
 + (Index *) alloc: (struct key_def *) key_def :(struct space *) space;
 
-- (void) buildNext: (struct tuple *) tuple;
-
 /** To be defined in subclasses. */
 - (size_t) node_size;
 - (tree_cmp_t) node_cmp;
@@ -61,4 +58,4 @@ typedef int (*tree_cmp_t)(const void *, const void *, void *);
 
 @end
 
-#endif /* TARANTOOL_BOX_TREE_H_INCLUDED */
+#endif /* TARANTOOL_BOX_TREE_INDEX_H_INCLUDED */
diff --git a/src/box/tree.m b/src/box/tree_index.m
similarity index 98%
rename from src/box/tree.m
rename to src/box/tree_index.m
index b437e987f1..c105723c49 100644
--- a/src/box/tree.m
+++ b/src/box/tree_index.m
@@ -26,7 +26,7 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "tree.h"
+#include "tree_index.h"
 #include "tuple.h"
 #include "space.h"
 #include "exception.h"
@@ -738,7 +738,7 @@ linear_key_node_compare(struct key_def *key_def,
 
 /* }}} */
 
-/* {{{ Tree iterator **********************************************/
+/* {{{ TreeIndex Iterators ****************************************/
 
 struct tree_iterator {
 	struct iterator base;
@@ -889,8 +889,9 @@ tree_iterator_gt(struct iterator *iterator)
 		return [Num32TreeIndex alloc];
 	case TREE_FIXED:
 		return [FixedTreeIndex alloc];
+	default:
+		assert(false);
 	}
-	panic("tree index type not implemented");
 }
 
 - (void) free
@@ -902,9 +903,10 @@ tree_iterator_gt(struct iterator *iterator)
 - (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
 {
 	self = [super init: key_def_arg :space_arg];
-	if (self) {
-		memset(&tree, 0, sizeof tree);
-	}
+	if (self == NULL)
+		return NULL;
+
+	memset(&tree, 0, sizeof tree);
 	return self;
 }
 
@@ -1328,9 +1330,10 @@ linear_dense_key_node_cmp(const void *key, const void * node, void *arg)
 - (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
 {
 	self = [super init: key_def_arg :space_arg];
-	if (self) {
-		first_field = find_first_field(key_def);
-	}
+	if (self == NULL)
+		return NULL;
+
+	first_field = find_first_field(key_def);
 	return self;
 }
 
@@ -1525,10 +1528,11 @@ linear_fixed_key_node_cmp(const void *key, const void * node, void *arg)
 - (id) init: (struct key_def *) key_def_arg :(struct space *) space_arg
 {
 	self = [super init: key_def_arg :space_arg];
-	if (self) {
-		first_field = find_first_field(key_def);
-		first_offset = find_fixed_offset(space, first_field, 0);
-	}
+	if (self == NULL)
+		return NULL;
+
+	first_field = find_first_field(key_def);
+	first_offset = find_fixed_offset(space, first_field, 0);
 	return self;
 }
 
-- 
GitLab