Tarantool development patches archive
 help / color / mirror / Atom feed
* [tarantool-patches] [PATCH 0/3] Optimize and simplify some tuple_update things
@ 2018-04-27 22:36 Vladislav Shpilevoy
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 1/3] vinyl: remove vy_apply_upsert_ops Vladislav Shpilevoy
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-27 22:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: kostja

Branch: http://github.com/tarantool/tarantool/tree/gh-1261-update-by-json-preliminary
Issue: https://github.com/tarantool/tarantool/issues/1261

The patchset is a preparation for tuple update by JSON. It removes big amount of
legacy and not effective code, especially from tuple_update.c, and improves
tuple update performance by elimination of function pointers from rope library.

Vladislav Shpilevoy (3):
  vinyl: remove vy_apply_upsert_ops
  tuple_update: remove alloc and alloc_ctx args
  rope: make rope library be C template using macros

 debian/copyright             |   2 +-
 src/box/memtx_space.c        |  18 +-
 src/box/space.c              |  23 +-
 src/box/tuple.c              |   6 +-
 src/box/tuple_update.c       | 123 ++++---
 src/box/tuple_update.h       |  14 +-
 src/box/vinyl.c              |  10 +-
 src/box/vy_upsert.c          |  64 +---
 src/lib/salad/CMakeLists.txt |   2 +-
 src/lib/salad/rope.c         | 664 ------------------------------------
 src/lib/salad/rope.h         | 790 ++++++++++++++++++++++++++++++++++++++-----
 test/unit/column_mask.c      |  30 +-
 test/unit/rope.c             |   1 -
 test/unit/rope_avl.c         |   1 -
 test/unit/rope_basic.c       |   1 -
 test/unit/rope_common.h      |  21 +-
 test/unit/rope_stress.c      |   5 +-
 17 files changed, 834 insertions(+), 941 deletions(-)
 delete mode 100644 src/lib/salad/rope.c

-- 
2.15.1 (Apple Git-101)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tarantool-patches] [PATCH 1/3] vinyl: remove vy_apply_upsert_ops
  2018-04-27 22:36 [tarantool-patches] [PATCH 0/3] Optimize and simplify some tuple_update things Vladislav Shpilevoy
@ 2018-04-27 22:36 ` Vladislav Shpilevoy
  2018-05-08 20:18   ` [tarantool-patches] " Konstantin Osipov
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 2/3] tuple_update: remove alloc and alloc_ctx args Vladislav Shpilevoy
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 3/3] rope: make rope library be C template using macros Vladislav Shpilevoy
  2 siblings, 1 reply; 7+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-27 22:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: kostja

Function vy_apply_upsert_opts originaly appears in this commit:
5627e53bf020bf83b8f74d3d04b93047bdf221b4, where it is a
refactored version of a sophia upsertion. But when a vy_stmt was
introduced, vinyl_apply_upsert_ops works just like ordinary
tuple_upsert_execute. Remove this useless wrapper.
---
 src/box/vy_upsert.c | 43 +++++--------------------------------------
 1 file changed, 5 insertions(+), 38 deletions(-)

diff --git a/src/box/vy_upsert.c b/src/box/vy_upsert.c
index 67f9ef09f..7af58d967 100644
--- a/src/box/vy_upsert.c
+++ b/src/box/vy_upsert.c
@@ -49,42 +49,6 @@ vy_update_alloc(void *arg, size_t size)
 	return data;
 }
 
-/**
- * vinyl wrapper of tuple_upsert_execute.
- * vibyl upsert opts are slightly different from tarantool ops,
- *  so they need some preparation before tuple_upsert_execute call.
- *  The function does this preparation.
- * On successfull upsert the result is placed into stmt and stmt_end args.
- * On fail the stmt and stmt_end args are not changed.
- * Possibly allocates new stmt via fiber region alloc,
- * so call fiber_gc() after usage
- */
-static void
-vy_apply_upsert_ops(struct region *region, const char **stmt,
-		    const char **stmt_end, const char *ops, const char *ops_end,
-		    bool suppress_error, uint64_t *column_mask)
-{
-	if (ops == ops_end)
-		return;
-
-#ifndef NDEBUG
-	const char *serie_end_must_be = ops;
-	mp_next(&serie_end_must_be);
-	assert(ops_end == serie_end_must_be);
-#endif
-	const char *result;
-	uint32_t size;
-	result = tuple_upsert_execute(vy_update_alloc, region,
-				      ops, ops_end,
-				      *stmt, *stmt_end,
-				      &size, 0, suppress_error, column_mask);
-	if (result != NULL) {
-		/* if failed, just skip it and leave stmt the same */
-		*stmt = result;
-		*stmt_end = result + size;
-	}
-}
-
 /**
  * Try to squash two upsert series (msgspacked index_base + ops)
  * Try to create a tuple with squahed operations
@@ -165,8 +129,11 @@ vy_apply_upsert(const struct tuple *new_stmt, const struct tuple *old_stmt,
 	size_t region_svp = region_used(region);
 	uint8_t old_type = vy_stmt_type(old_stmt);
 	uint64_t column_mask = COLUMN_MASK_FULL;
-	vy_apply_upsert_ops(region, &result_mp, &result_mp_end, new_ops,
-			    new_ops_end, suppress_error, &column_mask);
+	result_mp = tuple_upsert_execute(vy_update_alloc, region, new_ops,
+					 new_ops_end, result_mp, result_mp_end,
+					 &mp_size, 0, suppress_error,
+					 &column_mask);
+	result_mp_end = result_mp + mp_size;
 	if (old_type != IPROTO_UPSERT) {
 		assert(old_type == IPROTO_INSERT ||
 		       old_type == IPROTO_REPLACE);
-- 
2.15.1 (Apple Git-101)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tarantool-patches] [PATCH 2/3] tuple_update: remove alloc and alloc_ctx args
  2018-04-27 22:36 [tarantool-patches] [PATCH 0/3] Optimize and simplify some tuple_update things Vladislav Shpilevoy
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 1/3] vinyl: remove vy_apply_upsert_ops Vladislav Shpilevoy
@ 2018-04-27 22:36 ` Vladislav Shpilevoy
  2018-05-08 20:18   ` [tarantool-patches] " Konstantin Osipov
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 3/3] rope: make rope library be C template using macros Vladislav Shpilevoy
  2 siblings, 1 reply; 7+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-27 22:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: kostja

They are always region_aligned_alloc and region pointer. Lets use
them always inside tuple_update.c, with no necessity to pass via
args.
---
 src/box/memtx_space.c   | 18 +++++-------
 src/box/space.c         | 23 ++++++++-------
 src/box/tuple.c         |  6 ++--
 src/box/tuple_update.c  | 75 +++++++++++++++++++++++++------------------------
 src/box/tuple_update.h  | 14 +++------
 src/box/vinyl.c         | 10 ++-----
 src/box/vy_upsert.c     | 29 +++++--------------
 test/unit/column_mask.c | 30 ++++++++------------
 8 files changed, 85 insertions(+), 120 deletions(-)

diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 73df4c7d5..b40a91e67 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -385,8 +385,7 @@ memtx_space_execute_update(struct space *space, struct txn *txn,
 	uint32_t new_size = 0, bsize;
 	const char *old_data = tuple_data_range(stmt->old_tuple, &bsize);
 	const char *new_data =
-		tuple_update_execute(region_aligned_alloc_cb, &fiber()->gc,
-				     request->tuple, request->tuple_end,
+		tuple_update_execute(request->tuple, request->tuple_end,
 				     old_data, old_data + bsize,
 				     &new_size, request->index_base, NULL);
 	if (new_data == NULL)
@@ -452,9 +451,8 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 		 *   we get it here, it's also OK to throw it
 		 * @sa https://github.com/tarantool/tarantool/issues/1156
 		 */
-		if (tuple_update_check_ops(region_aligned_alloc_cb, &fiber()->gc,
-				       request->ops, request->ops_end,
-				       request->index_base)) {
+		if (tuple_update_check_ops(request->ops, request->ops_end,
+					   request->index_base)) {
 			return -1;
 		}
 		stmt->new_tuple = memtx_tuple_new(space->format,
@@ -475,12 +473,10 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 		 */
 		uint64_t column_mask = COLUMN_MASK_FULL;
 		const char *new_data =
-			tuple_upsert_execute(region_aligned_alloc_cb,
-					     &fiber()->gc, request->ops,
-					     request->ops_end, old_data,
-					     old_data + bsize, &new_size,
-					     request->index_base, false,
-					     &column_mask);
+			tuple_upsert_execute(request->ops, request->ops_end,
+					     old_data, old_data + bsize,
+					     &new_size, request->index_base,
+					     false, &column_mask);
 		if (new_data == NULL)
 			return -1;
 
diff --git a/src/box/space.c b/src/box/space.c
index 02a97926e..3e5fa86c1 100644
--- a/src/box/space.c
+++ b/src/box/space.c
@@ -275,7 +275,6 @@ space_before_replace(struct space *space, struct txn *txn,
 		return 0;
 	}
 
-	struct region *gc = &fiber()->gc;
 	enum iproto_type type = request->type;
 	struct index *pk = space->index[0];
 
@@ -332,10 +331,10 @@ space_before_replace(struct space *space, struct txn *txn,
 		}
 		old_data = tuple_data_range(old_tuple, &old_size);
 		old_data_end = old_data + old_size;
-		new_data = tuple_update_execute(region_aligned_alloc_cb, gc,
-					request->tuple, request->tuple_end,
-					old_data, old_data_end, &new_size,
-					request->index_base, NULL);
+		new_data = tuple_update_execute(request->tuple,
+						request->tuple_end, old_data,
+						old_data_end, &new_size,
+						request->index_base, NULL);
 		if (new_data == NULL)
 			return -1;
 		new_data_end = new_data + new_size;
@@ -355,18 +354,18 @@ space_before_replace(struct space *space, struct txn *txn,
 			 */
 			new_data = request->tuple;
 			new_data_end = request->tuple_end;
-			if (tuple_update_check_ops(region_aligned_alloc_cb, gc,
-					request->ops, request->ops_end,
-					request->index_base) != 0)
+			if (tuple_update_check_ops(request->ops,
+						   request->ops_end,
+						   request->index_base) != 0)
 				return -1;
 			break;
 		}
 		old_data = tuple_data_range(old_tuple, &old_size);
 		old_data_end = old_data + old_size;
-		new_data = tuple_upsert_execute(region_aligned_alloc_cb, gc,
-					request->ops, request->ops_end,
-					old_data, old_data_end, &new_size,
-					request->index_base, false, NULL);
+		new_data = tuple_upsert_execute(request->ops, request->ops_end,
+						old_data, old_data_end,
+						&new_size, request->index_base,
+						false, NULL);
 		new_data_end = new_data + new_size;
 		break;
 	default:
diff --git a/src/box/tuple.c b/src/box/tuple.c
index d4760f3b1..e10129fe2 100644
--- a/src/box/tuple.c
+++ b/src/box/tuple.c
@@ -403,8 +403,7 @@ box_tuple_update(const box_tuple_t *tuple, const char *expr,
 	struct region *region = &fiber()->gc;
 	size_t used = region_used(region);
 	const char *new_data =
-		tuple_update_execute(region_aligned_alloc_cb, region, expr,
-				     expr_end, old_data, old_data + bsize,
+		tuple_update_execute(expr, expr_end, old_data, old_data + bsize,
 				     &new_size, 1, NULL);
 	if (new_data == NULL) {
 		region_truncate(region, used);
@@ -429,8 +428,7 @@ box_tuple_upsert(const box_tuple_t *tuple, const char *expr,
 	struct region *region = &fiber()->gc;
 	size_t used = region_used(region);
 	const char *new_data =
-		tuple_upsert_execute(region_aligned_alloc_cb, region, expr,
-				     expr_end, old_data, old_data + bsize,
+		tuple_upsert_execute(expr, expr_end, old_data, old_data + bsize,
 				     &new_size, 1, false, NULL);
 	if (new_data == NULL) {
 		region_truncate(region, used);
diff --git a/src/box/tuple_update.c b/src/box/tuple_update.c
index 7bf210433..03a293785 100644
--- a/src/box/tuple_update.c
+++ b/src/box/tuple_update.c
@@ -43,7 +43,7 @@
 #include <bit/int96.h>
 #include <salad/rope.h>
 #include "column_mask.h"
-
+#include "fiber.h"
 
 /** UPDATE request implementation.
  * UPDATE request is represented by a sequence of operations, each
@@ -96,10 +96,28 @@
  * deleted one.
  */
 
+static inline void *
+update_alloc(void *ctx, size_t size)
+{
+	void *ptr = region_aligned_alloc((struct region *) ctx, size,
+					 alignof(uint64_t));
+	if (ptr == NULL)
+		diag_set(OutOfMemory, size, "region_aligned_alloc",
+			 "update internals");
+	return ptr;
+}
+
+/** Free rope node - do nothing, since we use a pool allocator. */
+static void
+update_free(void *ctx, void *mem)
+{
+	(void) ctx;
+	(void) mem;
+}
+
 /** Update internal state */
 struct tuple_update
 {
-	tuple_update_alloc_func alloc;
 	void *alloc_ctx;
 	struct rope *rope;
 	struct update_op *ops;
@@ -508,7 +526,7 @@ do_op_insert(struct tuple_update *update, struct update_op *op)
 	if (op_adjust_field_no(update, op, rope_size(update->rope) + 1))
 		return -1;
 	struct update_field *field = (struct update_field *)
-		update->alloc(update->alloc_ctx, sizeof(*field));
+		update_alloc(update->alloc_ctx, sizeof(*field));
 	if (field == NULL)
 		return -1;
 	update_field_init(field, op->arg.set.value, op->arg.set.length, 0);
@@ -778,7 +796,7 @@ update_field_split(void *split_ctx, void *data, size_t size, size_t offset)
 	struct update_field *prev = (struct update_field *) data;
 
 	struct update_field *next = (struct update_field *)
-			update->alloc(update->alloc_ctx, sizeof(*next));
+			update_alloc(update->alloc_ctx, sizeof(*next));
 	if (next == NULL)
 		return NULL;
 	assert(offset > 0 && prev->tail_len > 0);
@@ -799,14 +817,6 @@ update_field_split(void *split_ctx, void *data, size_t size, size_t offset)
 	return next;
 }
 
-/** Free rope node - do nothing, since we use a pool allocator. */
-static void
-region_alloc_free_stub(void *ctx, void *mem)
-{
-	(void) ctx;
-	(void) mem;
-}
-
 /**
  * We found a tuple to do the update on. Prepare a rope
  * to perform operations on.
@@ -822,14 +832,14 @@ int
 update_create_rope(struct tuple_update *update, const char *tuple_data,
 		   const char *tuple_data_end, uint32_t field_count)
 {
-	update->rope = rope_new(update_field_split, update, update->alloc,
-				region_alloc_free_stub, update->alloc_ctx);
+	update->rope = rope_new(update_field_split, update, update_alloc,
+				update_free, update->alloc_ctx);
 	if (update->rope == NULL)
 		return -1;
 	/* Initialize the rope with the old tuple. */
 
 	struct update_field *first = (struct update_field *)
-			update->alloc(update->alloc_ctx, sizeof(*first));
+			update_alloc(update->alloc_ctx, sizeof(*first));
 	if (first == NULL)
 		return -1;
 	const char *field = tuple_data;
@@ -968,7 +978,7 @@ update_read_ops(struct tuple_update *update, const char *expr,
 	}
 
 	/* Read update operations.  */
-	update->ops = (struct update_op *) update->alloc(update->alloc_ctx,
+	update->ops = (struct update_op *) update_alloc(update->alloc_ctx,
 				update->op_count * sizeof(struct update_op));
 	if (update->ops == NULL)
 		return -1;
@@ -1158,13 +1168,10 @@ upsert_do_ops(struct tuple_update *update, const char *old_data,
 }
 
 static void
-update_init(struct tuple_update *update,
-	    tuple_update_alloc_func alloc, void *alloc_ctx,
-	    int index_base)
+update_init(struct tuple_update *update, int index_base)
 {
 	memset(update, 0, sizeof(*update));
-	update->alloc = alloc;
-	update->alloc_ctx = alloc_ctx;
+	update->alloc_ctx = &fiber()->gc;
 	/*
 	 * Base field offset, e.g. 0 for C and 1 for Lua. Used only for
 	 * error messages. All fields numbers must be zero-based!
@@ -1176,7 +1183,7 @@ const char *
 update_finish(struct tuple_update *update, uint32_t *p_tuple_len)
 {
 	uint32_t tuple_len = update_calc_tuple_length(update);
-	char *buffer = (char *) update->alloc(update->alloc_ctx, tuple_len);
+	char *buffer = (char *) update_alloc(update->alloc_ctx, tuple_len);
 	if (buffer == NULL)
 		return NULL;
 	*p_tuple_len = update_write_tuple(update, buffer, buffer + tuple_len);
@@ -1184,23 +1191,21 @@ update_finish(struct tuple_update *update, uint32_t *p_tuple_len)
 }
 
 int
-tuple_update_check_ops(tuple_update_alloc_func alloc, void *alloc_ctx,
-		       const char *expr, const char *expr_end, int index_base)
+tuple_update_check_ops(const char *expr, const char *expr_end, int index_base)
 {
 	struct tuple_update update;
-	update_init(&update, alloc, alloc_ctx, index_base);
+	update_init(&update, index_base);
 	return update_read_ops(&update, expr, expr_end, 0);
 }
 
 const char *
-tuple_update_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
-		     const char *expr,const char *expr_end,
+tuple_update_execute(const char *expr,const char *expr_end,
 		     const char *old_data, const char *old_data_end,
 		     uint32_t *p_tuple_len, int index_base,
 		     uint64_t *column_mask)
 {
 	struct tuple_update update;
-	update_init(&update, alloc, alloc_ctx, index_base);
+	update_init(&update, index_base);
 	uint32_t field_count = mp_decode_array(&old_data);
 
 	if (update_read_ops(&update, expr, expr_end, field_count) != 0)
@@ -1214,14 +1219,13 @@ tuple_update_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
 }
 
 const char *
-tuple_upsert_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
-		     const char *expr,const char *expr_end,
+tuple_upsert_execute(const char *expr,const char *expr_end,
 		     const char *old_data, const char *old_data_end,
 		     uint32_t *p_tuple_len, int index_base, bool suppress_error,
 		     uint64_t *column_mask)
 {
 	struct tuple_update update;
-	update_init(&update, alloc, alloc_ctx, index_base);
+	update_init(&update, index_base);
 	uint32_t field_count = mp_decode_array(&old_data);
 
 	if (update_read_ops(&update, expr, expr_end, field_count) != 0)
@@ -1236,8 +1240,7 @@ tuple_upsert_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
 }
 
 const char *
-tuple_upsert_squash(tuple_update_alloc_func alloc, void *alloc_ctx,
-		    const char *expr1, const char *expr1_end,
+tuple_upsert_squash(const char *expr1, const char *expr1_end,
 		    const char *expr2, const char *expr2_end,
 		    size_t *result_size, int index_base)
 {
@@ -1245,7 +1248,7 @@ tuple_upsert_squash(tuple_update_alloc_func alloc, void *alloc_ctx,
 	const char *expr_end[2] = {expr1_end, expr2_end};
 	struct tuple_update update[2];
 	for (int j = 0; j < 2; j++) {
-		update_init(&update[j], alloc, alloc_ctx, index_base);
+		update_init(&update[j], index_base);
 		if (update_read_ops(&update[j], expr[j], expr_end[j], 0))
 			return NULL;
 		mp_decode_array(&expr[j]);
@@ -1262,8 +1265,8 @@ tuple_upsert_squash(tuple_update_alloc_func alloc, void *alloc_ctx,
 	}
 	size_t possible_size = expr1_end - expr1 + expr2_end - expr2;
 	const uint32_t space_for_arr_tag = 5;
-	char *buf = (char *)alloc(alloc_ctx,
-				  possible_size + space_for_arr_tag);
+	char *buf = (char *) update_alloc(&fiber()->gc,
+					  possible_size + space_for_arr_tag);
 	if (buf == NULL)
 		return NULL;
 	/* reserve some space for mp array header */
diff --git a/src/box/tuple_update.h b/src/box/tuple_update.h
index 706edd55a..37faa1918 100644
--- a/src/box/tuple_update.h
+++ b/src/box/tuple_update.h
@@ -44,22 +44,17 @@ enum {
 	BOX_UPDATE_OP_CNT_MAX = 4000,
 };
 
-typedef void *(*tuple_update_alloc_func)(void *, size_t);
-
 int
-tuple_update_check_ops(tuple_update_alloc_func alloc, void *alloc_ctx,
-		       const char *expr, const char *expr_end, int index_base);
+tuple_update_check_ops(const char *expr, const char *expr_end, int index_base);
 
 const char *
-tuple_update_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
-		     const char *expr,const char *expr_end,
+tuple_update_execute(const char *expr,const char *expr_end,
 		     const char *old_data, const char *old_data_end,
 		     uint32_t *p_new_size, int index_base,
 		     uint64_t *column_mask);
 
 const char *
-tuple_upsert_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
-		     const char *expr, const char *expr_end,
+tuple_upsert_execute(const char *expr, const char *expr_end,
 		     const char *old_data, const char *old_data_end,
 		     uint32_t *p_new_size, int index_base, bool suppress_error,
 		     uint64_t *column_mask);
@@ -75,8 +70,7 @@ tuple_upsert_execute(tuple_update_alloc_func alloc, void *alloc_ctx,
  * If it isn't possible to merge expressions NULL is returned.
  */
 const char *
-tuple_upsert_squash(tuple_update_alloc_func alloc, void *alloc_ctx,
-		    const char *expr1, const char *expr1_end,
+tuple_upsert_squash(const char *expr1, const char *expr1_end,
 		    const char *expr2, const char *expr2_end,
 		    size_t *result_size, int index_base);
 
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index ff4c28314..30261e41d 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -1877,8 +1877,7 @@ vy_update(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 	uint32_t new_size, old_size;
 	const char *old_tuple = tuple_data_range(stmt->old_tuple, &old_size);
 	const char *old_tuple_end = old_tuple + old_size;
-	new_tuple = tuple_update_execute(region_aligned_alloc_cb, &fiber()->gc,
-					 request->tuple, request->tuple_end,
+	new_tuple = tuple_update_execute(request->tuple, request->tuple_end,
 					 old_tuple, old_tuple_end, &new_size,
 					 request->index_base, &column_mask);
 	if (new_tuple == NULL)
@@ -2090,8 +2089,7 @@ vy_upsert(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 	if (vy_is_committed(env, space))
 		return 0;
 	/* Check update operations. */
-	if (tuple_update_check_ops(region_aligned_alloc_cb, &fiber()->gc,
-				   request->ops, request->ops_end,
+	if (tuple_update_check_ops(request->ops, request->ops_end,
 				   request->index_base)) {
 		return -1;
 	}
@@ -2154,9 +2152,7 @@ vy_upsert(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 	old_tuple_end = old_tuple + old_size;
 
 	/* Apply upsert operations to the old tuple. */
-	new_tuple = tuple_upsert_execute(region_aligned_alloc_cb,
-					 &fiber()->gc, ops, ops_end,
-					 old_tuple, old_tuple_end,
+	new_tuple = tuple_upsert_execute(ops, ops_end, old_tuple, old_tuple_end,
 					 &new_size, 0, false, &column_mask);
 	if (new_tuple == NULL)
 		return -1;
diff --git a/src/box/vy_upsert.c b/src/box/vy_upsert.c
index 7af58d967..8211ba968 100644
--- a/src/box/vy_upsert.c
+++ b/src/box/vy_upsert.c
@@ -38,17 +38,6 @@
 #include "fiber.h"
 #include "column_mask.h"
 
-static void *
-vy_update_alloc(void *arg, size_t size)
-{
-	/* TODO: rewrite tuple_upsert_execute() without exceptions */
-	struct region *region = (struct region *) arg;
-	void *data = region_aligned_alloc(region, size, sizeof(uint64_t));
-	if (data == NULL)
-		diag_set(OutOfMemory, size, "region", "upsert");
-	return data;
-}
-
 /**
  * Try to squash two upsert series (msgspacked index_base + ops)
  * Try to create a tuple with squahed operations
@@ -58,7 +47,7 @@ vy_update_alloc(void *arg, size_t size)
  * @retval -1 - memory error
  */
 static int
-vy_upsert_try_to_squash(struct tuple_format *format, struct region *region,
+vy_upsert_try_to_squash(struct tuple_format *format,
 			const char *key_mp, const char *key_mp_end,
 			const char *old_serie, const char *old_serie_end,
 			const char *new_serie, const char *new_serie_end,
@@ -68,8 +57,7 @@ vy_upsert_try_to_squash(struct tuple_format *format, struct region *region,
 
 	size_t squashed_size;
 	const char *squashed =
-		tuple_upsert_squash(vy_update_alloc, region,
-				    old_serie, old_serie_end,
+		tuple_upsert_squash(old_serie, old_serie_end,
 				    new_serie, new_serie_end,
 				    &squashed_size, 0);
 	if (squashed == NULL)
@@ -129,10 +117,9 @@ vy_apply_upsert(const struct tuple *new_stmt, const struct tuple *old_stmt,
 	size_t region_svp = region_used(region);
 	uint8_t old_type = vy_stmt_type(old_stmt);
 	uint64_t column_mask = COLUMN_MASK_FULL;
-	result_mp = tuple_upsert_execute(vy_update_alloc, region, new_ops,
-					 new_ops_end, result_mp, result_mp_end,
-					 &mp_size, 0, suppress_error,
-					 &column_mask);
+	result_mp = tuple_upsert_execute(new_ops, new_ops_end, result_mp,
+					 result_mp_end, &mp_size, 0,
+					 suppress_error, &column_mask);
 	result_mp_end = result_mp + mp_size;
 	if (old_type != IPROTO_UPSERT) {
 		assert(old_type == IPROTO_INSERT ||
@@ -162,10 +149,8 @@ vy_apply_upsert(const struct tuple *new_stmt, const struct tuple *old_stmt,
 	 * UPSERT + UPSERT case: combine operations
 	 */
 	assert(old_ops_end - old_ops > 0);
-	if (vy_upsert_try_to_squash(format, region,
-				    result_mp, result_mp_end,
-				    old_ops, old_ops_end,
-				    new_ops, new_ops_end,
+	if (vy_upsert_try_to_squash(format, result_mp, result_mp_end,
+				    old_ops, old_ops_end, new_ops, new_ops_end,
 				    &result_stmt) != 0) {
 		region_truncate(region, region_svp);
 		return NULL;
diff --git a/test/unit/column_mask.c b/test/unit/column_mask.c
index 7859626f8..3ffd351ac 100644
--- a/test/unit/column_mask.c
+++ b/test/unit/column_mask.c
@@ -3,6 +3,9 @@
 #include "unit.h"
 #include "msgpuck.h"
 #include "trivia/util.h"
+#include "fiber.h"
+#include "memory.h"
+#include "tuple.h"
 
 #define MAX_OPS 20
 #define MAX_FIELDS 100
@@ -102,20 +105,6 @@ tuple_new_update(const struct tuple_update_template *update, char **end)
 	return ret;
 }
 
-static char buffer[2048];
-static size_t pos = 0;
-
-/** Allocator for the tuple_update function. */
-static void *
-tuple_update_alloc_f(void *unused, size_t size)
-{
-	(void) unused;
-	fail_if(pos + size > sizeof(buffer));
-	char *ret = &buffer[pos];
-	pos += size;
-	return ret;
-}
-
 /**
  * Execute an update operation from the template and compare it
  * with the expected tuple and expected column_mask.
@@ -138,20 +127,19 @@ check_update_result(const struct tuple_template *original,
 
 	uint32_t actual_len;
 	uint64_t column_mask;
+	struct region *region = &fiber()->gc;
 	const char *actual =
-		tuple_update_execute(tuple_update_alloc_f, NULL, ops, ops_end,
-				     old, old_end, &actual_len, 1,
+		tuple_update_execute(ops, ops_end, old, old_end, &actual_len, 1,
 				     &column_mask);
 	fail_if(actual == NULL);
 	is((int32_t)actual_len, new_end - new, "check result length");
 	is(memcmp(actual, new, actual_len), 0, "tuple update is correct");
 	is(column_mask, expected_mask, "column_mask is correct");
+	fiber_gc();
 
 	free(old);
 	free(new);
 	free(ops);
-	/* reset the global buffer. */
-	pos = 0;
 }
 
 static inline void
@@ -239,6 +227,9 @@ basic_test()
 int
 main()
 {
+	memory_init();
+	fiber_init(fiber_c_invoke);
+	tuple_init(NULL);
 	header();
 	plan(27);
 
@@ -246,4 +237,7 @@ main()
 
 	footer();
 	check_plan();
+	tuple_free();
+	fiber_free();
+	memory_free();
 }
-- 
2.15.1 (Apple Git-101)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tarantool-patches] [PATCH 3/3] rope: make rope library be C template using macros
  2018-04-27 22:36 [tarantool-patches] [PATCH 0/3] Optimize and simplify some tuple_update things Vladislav Shpilevoy
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 1/3] vinyl: remove vy_apply_upsert_ops Vladislav Shpilevoy
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 2/3] tuple_update: remove alloc and alloc_ctx args Vladislav Shpilevoy
@ 2018-04-27 22:36 ` Vladislav Shpilevoy
  2018-05-08 20:22   ` [tarantool-patches] " Konstantin Osipov
  2 siblings, 1 reply; 7+ messages in thread
From: Vladislav Shpilevoy @ 2018-04-27 22:36 UTC (permalink / raw)
  To: tarantool-patches; +Cc: kostja

Rope library stores alloc, split and free functions by pointer,
that is a huge slog at performance. There is no reason, why the
rope library can not become a template.

Inline a concrete functions on compilation instead of pointers on
runtime.
---
 debian/copyright             |   2 +-
 src/box/tuple_update.c       |  84 ++---
 src/lib/salad/CMakeLists.txt |   2 +-
 src/lib/salad/rope.c         | 664 ------------------------------------
 src/lib/salad/rope.h         | 790 ++++++++++++++++++++++++++++++++++++++-----
 test/unit/rope.c             |   1 -
 test/unit/rope_avl.c         |   1 -
 test/unit/rope_basic.c       |   1 -
 test/unit/rope_common.h      |  21 +-
 test/unit/rope_stress.c      |   5 +-
 10 files changed, 766 insertions(+), 805 deletions(-)
 delete mode 100644 src/lib/salad/rope.c

diff --git a/debian/copyright b/debian/copyright
index a48171371..c56b8a246 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -218,7 +218,7 @@ Files: third_party/lua-cjson/*
 Copyright: 2010-2012 Mark Pulford <mark@kyne.com.au>
 License: Expat
 
-Files: src/lib/salad/rope.c
+Files: src/lib/salad/rope.h
 Copyright: 1993-1994 by Xerox Corporation.
 License: BSD-2-Clause
 
diff --git a/src/box/tuple_update.c b/src/box/tuple_update.c
index 03a293785..b1e341d55 100644
--- a/src/box/tuple_update.c
+++ b/src/box/tuple_update.c
@@ -30,8 +30,6 @@
  */
 
 #include "tuple_update.h"
-
-#include <stdio.h>
 #include <stdbool.h>
 
 #include "say.h"
@@ -41,7 +39,6 @@
 #include "third_party/queue.h"
 #include <msgpuck/msgpuck.h>
 #include <bit/int96.h>
-#include <salad/rope.h>
 #include "column_mask.h"
 #include "fiber.h"
 
@@ -97,28 +94,22 @@
  */
 
 static inline void *
-update_alloc(void *ctx, size_t size)
+update_alloc(struct region *region, size_t size)
 {
-	void *ptr = region_aligned_alloc((struct region *) ctx, size,
-					 alignof(uint64_t));
+	void *ptr = region_aligned_alloc(region, size, alignof(uint64_t));
 	if (ptr == NULL)
 		diag_set(OutOfMemory, size, "region_aligned_alloc",
 			 "update internals");
 	return ptr;
 }
 
-/** Free rope node - do nothing, since we use a pool allocator. */
-static void
-update_free(void *ctx, void *mem)
-{
-	(void) ctx;
-	(void) mem;
-}
+struct update_field;
+struct update_op;
 
 /** Update internal state */
 struct tuple_update
 {
-	void *alloc_ctx;
+	struct region *region;
 	struct rope *rope;
 	struct update_op *ops;
 	uint32_t op_count;
@@ -127,6 +118,18 @@ struct tuple_update
 	uint64_t column_mask;
 };
 
+static struct update_field *
+update_field_split(struct tuple_update *split_ctx, struct update_field *data,
+		   size_t size, size_t offset);
+
+#define ROPE_SPLIT_F update_field_split
+#define ROPE_ALLOC_F update_alloc
+#define rope_data_t struct update_field *
+#define rope_alloc_ctx_t struct region *
+#define rope_split_ctx_t struct tuple_update *
+
+#include "salad/rope.h"
+
 /** Argument of SET (and INSERT) operation. */
 struct op_set_arg {
 	uint32_t length;
@@ -204,9 +207,6 @@ union update_op_arg {
 	struct op_splice_arg splice;
 };
 
-struct update_field;
-struct update_op;
-
 typedef int (*do_op_func)(struct tuple_update *update, struct update_op *op);
 typedef int (*read_arg_func)(int index_base, struct update_op *op,
 			     const char **expr);
@@ -526,7 +526,7 @@ do_op_insert(struct tuple_update *update, struct update_op *op)
 	if (op_adjust_field_no(update, op, rope_size(update->rope) + 1))
 		return -1;
 	struct update_field *field = (struct update_field *)
-		update_alloc(update->alloc_ctx, sizeof(*field));
+		update_alloc(update->region, sizeof(*field));
 	if (field == NULL)
 		return -1;
 	update_field_init(field, op->arg.set.value, op->arg.set.length, 0);
@@ -541,8 +541,7 @@ do_op_set(struct tuple_update *update, struct update_op *op)
 		return do_op_insert(update, op);
 	if (op_adjust_field_no(update, op, rope_size(update->rope)))
 		return -1;
-	struct update_field *field = (struct update_field *)
-		rope_extract(update->rope, op->field_no);
+	struct update_field *field = rope_extract(update->rope, op->field_no);
 	if (field == NULL)
 		return -1;
 	/* Ignore the previous op, if any. */
@@ -579,8 +578,7 @@ do_op_arith(struct tuple_update *update, struct update_op *op)
 	if (op_adjust_field_no(update, op, rope_size(update->rope)))
 		return -1;
 
-	struct update_field *field = (struct update_field *)
-		rope_extract(update->rope, op->field_no);
+	struct update_field *field = rope_extract(update->rope, op->field_no);
 	if (field == NULL)
 		return -1;
 	if (field->op) {
@@ -609,8 +607,7 @@ do_op_bit(struct tuple_update *update, struct update_op *op)
 {
 	if (op_adjust_field_no(update, op, rope_size(update->rope)))
 		return -1;
-	struct update_field *field = (struct update_field *)
-		rope_extract(update->rope, op->field_no);
+	struct update_field *field = rope_extract(update->rope, op->field_no);
 	if (field == NULL)
 		return -1;
 	struct op_bit_arg *arg = &op->arg.bit;
@@ -647,8 +644,7 @@ do_op_splice(struct tuple_update *update, struct update_op *op)
 {
 	if (op_adjust_field_no(update, op, rope_size(update->rope)))
 		return -1;
-	struct update_field *field = (struct update_field *)
-		rope_extract(update->rope, op->field_no);
+	struct update_field *field = rope_extract(update->rope, op->field_no);
 	if (field == NULL)
 		return -1;
 	if (field->op) {
@@ -787,16 +783,14 @@ static const struct update_op_meta op_delete =
 /** Split a range of fields in two, allocating update_field
  * context for the new range.
  */
-static void *
-update_field_split(void *split_ctx, void *data, size_t size, size_t offset)
+static struct update_field *
+update_field_split(struct tuple_update *update, struct update_field *prev,
+		   size_t size, size_t offset)
 {
-	(void)size;
-	struct tuple_update *update = (struct tuple_update *) split_ctx;
-
-	struct update_field *prev = (struct update_field *) data;
-
-	struct update_field *next = (struct update_field *)
-			update_alloc(update->alloc_ctx, sizeof(*next));
+	(void) size;
+	struct update_field *next =
+		(struct update_field *) update_alloc(update->region,
+						     sizeof(*next));
 	if (next == NULL)
 		return NULL;
 	assert(offset > 0 && prev->tail_len > 0);
@@ -804,9 +798,8 @@ update_field_split(void *split_ctx, void *data, size_t size, size_t offset)
 	const char *field = prev->tail;
 	const char *end = field + prev->tail_len;
 
-	for (uint32_t i = 1; i < offset; i++) {
+	for (uint32_t i = 1; i < offset; i++)
 		mp_next(&field);
-	}
 
 	prev->tail_len = field - prev->tail;
 	const char *f = field;
@@ -832,14 +825,13 @@ int
 update_create_rope(struct tuple_update *update, const char *tuple_data,
 		   const char *tuple_data_end, uint32_t field_count)
 {
-	update->rope = rope_new(update_field_split, update, update_alloc,
-				update_free, update->alloc_ctx);
+	update->rope = rope_new(update, update->region);
 	if (update->rope == NULL)
 		return -1;
 	/* Initialize the rope with the old tuple. */
 
 	struct update_field *first = (struct update_field *)
-			update_alloc(update->alloc_ctx, sizeof(*first));
+			update_alloc(update->region, sizeof(*first));
 	if (first == NULL)
 		return -1;
 	const char *field = tuple_data;
@@ -863,8 +855,7 @@ update_calc_tuple_length(struct tuple_update *update)
 
 	rope_iter_create(&it, update->rope);
 	for (node = rope_iter_start(&it); node; node = rope_iter_next(&it)) {
-		struct update_field *field =
-				(struct update_field *) rope_leaf_data(node);
+		struct update_field *field = rope_leaf_data(node);
 		uint32_t field_len = (field->op ? field->op->new_field_len :
 				      (uint32_t)(field->tail - field->old));
 		res += field_len + field->tail_len;
@@ -888,8 +879,7 @@ update_write_tuple(struct tuple_update *update, char *buffer, char *buffer_end)
 
 	rope_iter_create(&it, update->rope);
 	for (node = rope_iter_start(&it); node; node = rope_iter_next(&it)) {
-		struct update_field *field = (struct update_field *)
-				rope_leaf_data(node);
+		struct update_field *field = rope_leaf_data(node);
 		uint32_t field_count = rope_leaf_size(node);
 		const char *old_field = field->old;
 		struct update_op *op = field->op;
@@ -978,7 +968,7 @@ update_read_ops(struct tuple_update *update, const char *expr,
 	}
 
 	/* Read update operations.  */
-	update->ops = (struct update_op *) update_alloc(update->alloc_ctx,
+	update->ops = (struct update_op *) update_alloc(update->region,
 				update->op_count * sizeof(struct update_op));
 	if (update->ops == NULL)
 		return -1;
@@ -1171,7 +1161,7 @@ static void
 update_init(struct tuple_update *update, int index_base)
 {
 	memset(update, 0, sizeof(*update));
-	update->alloc_ctx = &fiber()->gc;
+	update->region = &fiber()->gc;
 	/*
 	 * Base field offset, e.g. 0 for C and 1 for Lua. Used only for
 	 * error messages. All fields numbers must be zero-based!
@@ -1183,7 +1173,7 @@ const char *
 update_finish(struct tuple_update *update, uint32_t *p_tuple_len)
 {
 	uint32_t tuple_len = update_calc_tuple_length(update);
-	char *buffer = (char *) update_alloc(update->alloc_ctx, tuple_len);
+	char *buffer = (char *) update_alloc(update->region, tuple_len);
 	if (buffer == NULL)
 		return NULL;
 	*p_tuple_len = update_write_tuple(update, buffer, buffer + tuple_len);
diff --git a/src/lib/salad/CMakeLists.txt b/src/lib/salad/CMakeLists.txt
index 5263c16e4..41cb08898 100644
--- a/src/lib/salad/CMakeLists.txt
+++ b/src/lib/salad/CMakeLists.txt
@@ -1,3 +1,3 @@
-set(lib_sources rope.c rtree.c guava.c bloom.c)
+set(lib_sources rtree.c guava.c bloom.c)
 set_source_files_compile_flags(${lib_sources})
 add_library(salad STATIC ${lib_sources})
diff --git a/src/lib/salad/rope.c b/src/lib/salad/rope.c
deleted file mode 100644
index 06324ab65..000000000
--- a/src/lib/salad/rope.c
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
- * 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.
- */
-/*
- * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved.
- *
- * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
- * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
- *
- * Permission is hereby granted to use or copy this program
- * for any purpose,  provided the above notices are retained on all copies.
- * Permission to modify the code and to distribute modified code is granted,
- * provided the above notices are retained, and a notice that the code was
- * modified is included with the above copyright notice.
- *
- * Author: Hans-J. Boehm (boehm@parc.xerox.com)
- */
-/*
- * This is a rope implementation which uses AVL tree
- * balancing algorithm for rope tree balance.
- */
-#include "rope.h"
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-
-static inline int
-rope_node_height(struct rope_node *node)
-{
-	return node ? node->height : 0;
-}
-
-#if !defined(MAX)
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#endif /* MAX */
-
-static inline void
-rope_relink(struct rope_node *node)
-{
-	node->tree_size = (rope_node_size(node->link[0]) +
-			   rope_node_size(node->link[1]) +
-			   node->leaf_size);
-	node->height = MAX(rope_node_height(node->link[0]),
-			   rope_node_height(node->link[1])) + 1;
-}
-
-static inline struct rope_node *
-rope_node_new(struct rope *rope, void *data, rope_size_t size)
-{
-	struct rope_node *node =
-		(struct rope_node *) rope->alloc(rope->alloc_ctx,
-						 sizeof(struct rope_node));
-
-	if (node == NULL)
-		return NULL;
-
-	node->height = 1;
-	node->tree_size = node->leaf_size = size;
-	node->data = data;
-	node->link[0] = node->link[1] = NULL;
-
-	return node;
-}
-
-void
-rope_clear(struct rope *rope)
-{
-	struct rope_node *it = rope->root;
-	struct rope_node *save;
-
-	/* Destruction by rotation */
-	while (it != NULL) {
-		if (it->link[0] == NULL) {
-			/* Remove node */
-			save = it->link[1];
-			rope->free(rope->alloc_ctx, it);
-		} else {
-			/* Rotate right */
-			save = it->link[0];
-			it->link[0] = save->link[1];
-			save->link[1] = it;
-		}
-		it = save;
-	}
-	rope->root = NULL;
-}
-
-static struct rope_node *
-rope_node_split(struct rope *rope, struct rope_node *node, rope_size_t offset)
-{
-	rope_size_t old_size = node->leaf_size;
-	node->leaf_size = offset;
-
-	void *data = rope->split(rope->split_ctx, node->data, old_size, offset);
-	return rope_node_new(rope, data, old_size - offset);
-}
-
-static inline struct rope_node *
-avl_rotate_single(struct rope_node *parent, int direction)
-{
-	struct rope_node *save = parent->link[!direction];
-
-	parent->link[!direction] = save->link[direction];
-	save->link[direction] = parent;
-
-	/* First relink the parent, since it's now a child. */
-	rope_relink(parent);
-	rope_relink(save);
-
-	return save;
-}
-
-static inline struct rope_node *
-avl_rotate_double(struct rope_node *parent, int direction)
-{
-	parent->link[!direction] =
-		avl_rotate_single(parent->link[!direction], !direction);
-	return avl_rotate_single(parent, direction);
-}
-
-/** Rebalance the tree. */
-static inline void
-avl_rebalance_after_insert(struct rope_node ***path,
-			   struct rope_node ***p_end, int insert_height)
-{
-	while (p_end > path) {
-		struct rope_node *left = **p_end--;
-		struct rope_node *parent = **p_end;
-		/*
-		 * To use the same rotation functions, set mirror
-		 * to 1 if left is right and right is left.
-		 */
-		int mirror = left != parent->link[0];
-		struct rope_node *right = parent->link[!mirror];
-
-		int left_height = rope_node_height(left);
-		int right_height = rope_node_height(right);
-		parent->height = MAX(left_height, right_height) + 1;
-		/*
-		 * Rotations flattened the tree, so there is no
-		 * further changes in height up the insertion
-		 * path.
-		 */
-		if (left_height == right_height)
-			break;
-		/*
-		 * We've been adding a new child (children) to the
-		 * 'left' subtree, so it couldn't get shorter.
-		 * The old difference between subtrees was in the
-		 * range -1..1. So the new difference can only be
-		 * in the range -1..1 + height(new_node).
-		 */
-		if (left_height - right_height >= 2) {
-			struct rope_node *l_left = left->link[mirror];
-			struct rope_node *l_right = left->link[!mirror];
-			int l_left_height = rope_node_height(l_left);
-			int l_right_height = rope_node_height(l_right);
-			/*
-			 * Rotate in the direction, opposite to
-			 * the skew. E.g. if we have two left-left
-			 * nodes hanging off the tree, rotate the
-			 * parent clockwise. If we have a left
-			 * node with a right child, rotate the
-			 * child counterclockwise, and then the whole
-			 * thing clockwise.
-			 */
-			if (l_left_height >= l_right_height)
-				**p_end = avl_rotate_single(parent,
-							    !mirror);
-			else
-				**p_end = avl_rotate_double(parent,
-							    !mirror);
-			/*
-			 * If we inserted only one node, no more
-			 * than 1 rotation is required (see
-			 * D. Knuth, Introduction to Algorithms,
-			 * vol. 3.). For 2 nodes, its max
-			 * 2 rotations.
-			 */
-			if (l_left_height != l_right_height &&
-			    --insert_height == 0)
-				break;
-		}
-	}
-}
-
-/* This is a copy-cat of the previous loop,
- * with the exception that the heuristic to break
- * the loop is different.
- */
-static inline void
-avl_rebalance_after_delete(struct rope_node ***path,
-			   struct rope_node ***p_end)
-{
-	while (p_end > path) {
-		struct rope_node *left = **p_end--;
-		struct rope_node *parent = **p_end;
-
-		int mirror = left != parent->link[0];
-
-		struct rope_node *right = parent->link[!mirror];
-
-		int left_height = rope_node_height(left);
-		int right_height = rope_node_height(right);
-
-		parent->height = MAX(left_height, right_height) + 1;
-		/*
-		 * Right was taller, and we deleted from the left.
-		 * We can break the loop since there can be no
-		 * changes in height up in the route.
-		 */
-		if (left_height - right_height == -1)
-			break;
-
-		if (left_height - right_height <= -2) {
-			struct rope_node *r_left = right->link[mirror];
-			struct rope_node *r_right = right->link[!mirror];
-			int r_left_height = rope_node_height(r_left);
-			int r_right_height = rope_node_height(r_right);
-
-			if (r_left_height <= r_right_height)
-				**p_end = avl_rotate_single(parent, mirror);
-			else
-				**p_end = avl_rotate_double(parent, mirror);
-		}
-	}
-}
-
-/**
- * Find a rope node which contains the substring at offset,
- * adjusting tree size with adjust_size and saving the path
- * in path.
- *
- * @return the end of the route.
- */
-static inline struct rope_node ***
-avl_route_to_offset(struct rope_node ***path, rope_size_t *p_offset,
-		    ssize_t adjust_size)
-{
-	rope_size_t offset = *p_offset;
-	while (**path) {
-		struct rope_node *node = **path;
-
-		node->tree_size += adjust_size;
-
-		rope_size_t left_size = rope_node_size(node->link[0]);
-
-		if (offset < left_size) {
-			/* The offset lays in  the left subtree. */
-			*++path = &node->link[0];
-		} else {
-			/* Make the new offset relative to the parent. */
-			offset -= left_size;
-
-			if (offset < node->leaf_size) {
-				/* Found. */
-				break;
-			} else {
-				/*
-				 * Make the offset relative to the
-				 * leftmost node in the right subtree.
-				 */
-				offset -= node->leaf_size;
-			}
-			*++path = &node->link[1];
-		}
-	}
-	*p_offset = offset;
-	return path;
-}
-
-/**
- * Route to successor or predecessor node of the node
- * in **path. It's either the rightmost leaf of the left child
- * (previous node) or leftmost leaf of the right child.
- */
-static inline struct rope_node ***
-avl_route_to_next(struct rope_node ***path, int dir, rope_ssize_t adjust_size)
-{
-	struct rope_node *node = **path;
-	*++path = &node->link[dir];
-	while (**path) {
-		node = **path;
-		node->tree_size += adjust_size;
-		*++path = &node->link[!dir];
-	}
-	return path;
-}
-
-/**
- * A new node is always inserted at a leaf position.
- * If insertion unbalances the tree, the rebalancing
- * procedure may put the node into an intermediate position.
- *
- * While traversing the tree, we simultaneously update
- * tree sizes of all intermediate nodes, taking into account
- * the size of the new node.
- *
- * When insertion offset falls at the middle of an existing node,
- * we truncate this node and attach its tail to the left leaf
- * of the new node. This trim operation doesn't decrease the old
- * subtree height, and, while it does change subtree size
- * temporarily, as long as we attach the new node to the right
- * subtree of the truncated node, truncation has no effect on the
- * tree size either.
- *
- * Rebalancing, when it occurs, will correctly update subtree
- * heights and sizes of all modified nodes.
- */
-int
-rope_insert(struct rope *rope, rope_size_t offset, void *data, rope_size_t size)
-{
-	if (offset > rope_size(rope))
-		offset = rope_size(rope);
-
-	assert(size);
-
-	struct rope_node *new_node = rope_node_new(rope, data, size);
-
-	if (new_node == NULL)
-		return -1;
-
-	struct rope_node **path[ROPE_HEIGHT_MAX];
-	path[0] = &rope->root;
-
-	struct rope_node ***p_end = avl_route_to_offset(path, &offset, size);
-	if (**p_end != NULL) {
-		/*
-		 * The offset is inside an existing
-		 * substring in the rope. If offset is 0,
-		 * then insert the new node at the rightmost leaf
-		 * of the left child. Otherwise, cut the tail of
-		 * the substring, make it a prefix of the inserted
-		 * string, and insert the result at the leftmost
-		 * leaf of the right child.
-		 */
-		if (offset != 0) {
-			struct rope_node *split_node;
-			split_node = rope_node_split(rope, **p_end, offset);
-			if (split_node == NULL)
-				return -1;
-			split_node->link[0] = new_node;
-			split_node->height++;
-			split_node->tree_size += new_node->tree_size;
-			new_node = split_node;
-		}
-		p_end = avl_route_to_next(p_end, offset != 0,
-					  new_node->tree_size);
-	}
-	**p_end = new_node;
-	avl_rebalance_after_insert(path, p_end, new_node->height);
-	return 0;
-}
-
-/** Make sure there is a rope node at the given offset. */
-struct rope_node *
-rope_extract_node(struct rope *rope, rope_size_t offset)
-{
-	assert(offset < rope_size(rope));
-
-	struct rope_node **path[ROPE_HEIGHT_MAX];
-	path[0] = &rope->root;
-
-	struct rope_node ***p_end = avl_route_to_offset(path, &offset, 0);
-	if (offset == 0)
-		return **p_end;
-	struct rope_node *new_node = rope_node_split(rope, **p_end, offset);
-	if (new_node == NULL)
-		return NULL;
-	p_end = avl_route_to_next(p_end, 1, new_node->tree_size);
-	**p_end = new_node;
-	avl_rebalance_after_insert(path, p_end, new_node->height);
-	return new_node;
-}
-
-/**
- * Erase a single element from the rope.
- * This is a straightforward implementation for a single-element
- * deletion from a rope. A generic cut from a rope involves
- * 2 tree splits and one merge.
- *
- * When deleting a single element, 3 cases are possible:
- * - offset falls at a node with a single element. In this
- *   case we perform a normal AVL tree delete.
- * - offset falls at the end or the beginning of an existing node
- *   with leaf_size > 1. In that case we trim the existing node
- *   and return.
- * - offset falls inside an existing node. In that case
- *   we split the existing node at offset, and insert the tail.
- *
- * The implementation is a copycat of rope_insert(). If you're
- * trying to understand the code, it's recommended to start
- * from rope_insert().
- */
-int
-rope_erase(struct rope *rope, rope_size_t offset)
-{
-	assert(offset < rope_size(rope));
-
-	struct rope_node **path[ROPE_HEIGHT_MAX];
-	path[0] = &rope->root;
-
-	struct rope_node ***p_end = avl_route_to_offset(path, &offset, -1);
-
-	struct rope_node *node = **p_end;
-
-	if (node->leaf_size > 1) {
-		/* Check if we can simply trim the node. */
-		if (offset == 0) {
-			/* Cut the head. */
-			node->data = rope->split(rope->split_ctx, node->data,
-						 node->leaf_size, 1);
-			node->leaf_size -= 1;
-			return 0;
-		}
-		rope_size_t size = node->leaf_size;
-		/* Cut the tail */
-		void *next = rope->split(rope->split_ctx, node->data,
-					 node->leaf_size, offset);
-		node->leaf_size = offset;
-		if (offset == size - 1)
-			return 0; /* Trimmed the tail, nothing else to do */
-		/*
-		 * Offset falls inside a substring. Erase the
-		 * first field and insert the tail.
-		 */
-		next = rope->split(rope->split_ctx, next, size - offset, 1);
-		struct rope_node *new_node =
-			rope_node_new(rope, next, size - offset - 1);
-		if (new_node == NULL)
-			return -1;
-		/* Trim the old node. */
-		p_end = avl_route_to_next(p_end, 1, new_node->tree_size);
-		**p_end = new_node;
-		avl_rebalance_after_insert(path, p_end, new_node->height);
-		return 0;
-	}
-	/* We need to delete the node. */
-	assert(offset == 0);
-	int direction;
-	if (node->link[0] != NULL && node->link[1] != NULL) {
-		/*
-		 * The node has two non-NULL leaves. We can't
-		 * simply delete the node since in that case we
-		 * won't know what to do with one of the leaves.
-		 * Instead of deleting the node, store in it data
-		 * from the rightmost node in the left subtree, or
-		 * the leftmost node in the right subtree,
-		 * (depending on which subtree is taller), and
-		 * delete this leftmost/rightmost node instead.
-		 */
-		struct rope_node *save = node;
-		direction = node->link[1]->height > node->link[0]->height;
-		p_end = avl_route_to_next(p_end, direction, 0) - 1;
-		node = **p_end;
-		/* Move the data pointers. */
-		save->data = node->data;
-		save->leaf_size = node->leaf_size;
-		/*
-		 * Now follow the path again and update tree_size
-		 * in the parents of the moved child.
-	         */
-		save = save->link[direction];
-		while (save != node) {
-			save->tree_size -= node->leaf_size;
-			save = save->link[!direction];
-		}
-	} else {
-		/*
-		 * Left or right subtree are NULL, so we
-		 * can simply put the non-NULL leaf in place
-		 * of the parent.
-		 */
-		direction = node->link[0] == NULL;
-	}
-	**p_end = node->link[direction];
-	rope->free(rope, node);
-	avl_rebalance_after_delete(path, p_end);
-	return 0;
-}
-
-/**
- * Traverse left until the left subtree is NULL,
- * save the path in iter->path.
- * @pre iter->path[iter->top] is not NULL
- * @post iter->path[iter->top] is not NULL and points to the last
- * not-NULL node.
- */
-static inline void
-rope_iter_down_to_leaf(struct rope_iter *it)
-{
-	while (it->top[0]->link[0] != NULL) {
-		it->top[1] = it->top[0]->link[0];
-		it->top++;
-	}
-}
-
-struct rope_node *
-rope_iter_start(struct rope_iter *it)
-{
-	it->top = it->path;
-	it->top[0] = it->rope->root;
-
-	if (it->top[0] != NULL)
-		rope_iter_down_to_leaf(it);
-	return it->top[0];
-}
-
-struct rope_node *
-rope_iter_next(struct rope_iter *it)
-{
-	if (it->top[0]->link[1] != NULL) {
-		it->top[1] = it->top[0]->link[1];
-		it->top++;
-		rope_iter_down_to_leaf(it);
-	} else {
-		/*
-		 * Right subtree is NULL. Left subtree is fully
-		 * traversed (guaranteed by the order in which we
-		 * iterate). Pop up the path until the current
-		 * node points to a link we haven't visited
-		 * yet: this is the case when we return to the
-		 * parent from its left child.
-		 */
-		do {
-			/*
-			 * Returned to the root from the right
-			 * subtree: the tree is fully traversed.
-			 */
-			if (it->top == it->path) {
-				/*
-				 * Crash, rather than infinite loop
-				 * if next() is called beyond last.
-				 */
-				it->top[0] = NULL;
-				return NULL;
-			}
-			it->top--;
-		} while (it->top[1] == it->top[0]->link[1]);
-	}
-	return *it->top;
-}
-
-/** Apply visit_leaf function to every rope leaf. */
-void
-rope_traverse(struct rope *rope, void (*visit_leaf)(void *, size_t))
-{
-	struct rope_iter iter;
-	rope_iter_create(&iter, rope);
-
-	struct rope_node *leaf;
-
-	for (leaf = rope_iter_start(&iter);
-	     leaf != NULL;
-	     leaf = rope_iter_next(&iter)) {
-
-		visit_leaf(leaf->data, leaf->leaf_size);
-	}
-}
-
-void
-rope_check(struct rope *rope)
-{
-	struct rope_iter iter;
-	rope_iter_create(&iter, rope);
-
-	struct rope_node *node;
-
-	for (node = rope_iter_start(&iter);
-	     node != NULL;
-	     node = rope_iter_next(&iter)) {
-
-		assert(node->leaf_size != 0);
-
-		assert(node->tree_size == rope_node_size(node->link[0])
-		       + rope_node_size(node->link[1]) + node->leaf_size);
-
-		assert(node->height == (MAX(rope_node_height(node->link[0]),
-					    rope_node_height(node->link[1])) + 1));
-		if (node->leaf_size == 0 ||
-		    node->tree_size != (rope_node_size(node->link[0]) +
-					rope_node_size(node->link[1]) +
-					node->leaf_size) ||
-		    node->height != MAX(rope_node_height(node->link[0]),
-					rope_node_height(node->link[1])) + 1)
-			abort();
-	}
-}
-
-static void
-rope_node_print(struct rope_node *node,
-		void (*print)(void *, size_t),
-		const char *prefix, int dir)
-{
-	const char *conn[] = { "┌──", "└──" };
-
-	const char *padding[] = { "│   ", "   " };
-
-	rope_size_t child_prefix_len = strlen(prefix) + strlen(padding[0]) + 1;
-	char *child_prefix = malloc(child_prefix_len);
-
-	if (node && (node->link[0] || node->link[1])) {
-		snprintf(child_prefix, child_prefix_len - 1,
-			 "%s%s", prefix, padding[!dir]);
-		rope_node_print(node->link[0], print, child_prefix, 0);
-	}
-
-	snprintf(child_prefix, child_prefix_len - 1, "%s%s",
-		 prefix, padding[dir]);
-	printf("%s%s", prefix, conn[dir]);
-
-	if (node == NULL) {
-		printf("nil\n");
-	} else {
-
-		printf("{ len = %zu, height = %d, data = '",
-		       (size_t) node->leaf_size, node->height);
-		print(node->data, node->leaf_size);
-		printf("'}\n");
-
-		if (node->link[0] || node->link[1])
-			rope_node_print(node->link[1], print, child_prefix, 1);
-	}
-
-	free(child_prefix);
-}
-
-void
-rope_pretty_print(struct rope *rope, void (*print_leaf)(void *, size_t))
-{
-	printf("size = %zu\nstring = '", (size_t) rope_size(rope));
-	rope_traverse(rope, print_leaf);
-	printf("'\n");
-	rope_node_print(rope->root, print_leaf, "", true);
-	printf("\n");
-}
diff --git a/src/lib/salad/rope.h b/src/lib/salad/rope.h
index 719fd9c99..3e4585809 100644
--- a/src/lib/salad/rope.h
+++ b/src/lib/salad/rope.h
@@ -1,5 +1,3 @@
-#ifndef INCLUDES_TARANTOOL_ROPE_H
-#define INCLUDES_TARANTOOL_ROPE_H
 /*
  * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
  *
@@ -30,19 +28,52 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include <stdint.h>
-#include <stddef.h>
+/*
+ * Copyright (c) 1993-1994 by Xerox Corporation.  All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * Permission to modify the code and to distribute modified code is granted,
+ * provided the above notices are retained, and a notice that the code was
+ * modified is included with the above copyright notice.
+ *
+ * Author: Hans-J. Boehm (boehm@parc.xerox.com)
+ */
+/*
+ * This is a rope implementation which uses AVL tree
+ * balancing algorithm for rope tree balance.
+ */
 #include <stdbool.h>
-
-#if defined(__cplusplus)
-extern "C" {
-#endif /* defined(__cplusplus) */
+#include <stdio.h>
+#include <assert.h>
 
 typedef uint32_t rope_size_t;
 typedef int32_t rope_ssize_t;
-typedef void *(*rope_split_func)(void *, void *, size_t, size_t);
-typedef void *(*rope_alloc_func)(void *, size_t);
-typedef void (*rope_free_func)(void *, void *);
+
+/**
+ * Needed definitions:
+ *
+ * rope_data_t
+ * rope_split_ctx_t
+ * rope_alloc_ctx_t
+ * ROPE_SPLIT_F
+ * ROPE_ALLOC_F
+ * ROPE_FREE_F [optional]
+ */
+
+#ifndef ROPE_FREE_F
+#define ROPE_FREE_F_IMPL(a, b) do { (void) a; (void) b; } while(0)
+#else
+#define ROPE_FREE_F_IMPL(a, b) ROPE_FREE_F(a, b)
+#endif /* ROPE_FREE_F */
+
+#ifndef MAX
+#define NEED_UNDEF_MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* MAX */
 
 /** Tallest allowable tree, 1.44*log(2^32) */
 enum { ROPE_HEIGHT_MAX = 46 };
@@ -55,7 +86,7 @@ struct rope_node {
 	/* Substring size. */
 	rope_size_t leaf_size;
 	/* Substring. */
-	void *data;
+	rope_data_t data;
 	/* Left (0) and right (1) links */
 	struct rope_node *link[2];
 };
@@ -64,15 +95,10 @@ struct rope {
 	/** Top of the tree */
 	struct rope_node *root;
 	/** Memory management context. */
-	void *alloc_ctx;
+	rope_alloc_ctx_t alloc_ctx;
 	/** Get a sequence tail, given offset. */
-	rope_split_func split;
 	/** Split function context. */
-	void *split_ctx;
-	/** Allocate memory (context, size). */
-	void *(*alloc)(void *, size_t);
-	/** Free memory (context, pointer) */
-	void (*free)(void *, void *);
+	rope_split_ctx_t split_ctx;
 };
 
 struct rope_iter {
@@ -84,6 +110,34 @@ struct rope_iter {
 	struct rope_node *path[ROPE_HEIGHT_MAX];
 };
 
+/** Public API {{{ */
+
+/** Initialize an empty rope. */
+static inline void
+rope_create(struct rope *rope, rope_split_ctx_t split_ctx,
+	    rope_alloc_ctx_t alloc_ctx)
+{
+	rope->root = NULL;
+	rope->split_ctx = split_ctx;
+	rope->alloc_ctx = alloc_ctx;
+}
+
+/** Create a new empty rope.
+ * @param alloc_ctx  allocator context
+ *
+ * @return  an empty rope, or NULL if failed
+ *          to allocate memory
+ */
+static inline struct rope *
+rope_new(rope_split_ctx_t split_ctx, rope_alloc_ctx_t alloc_ctx)
+{
+	struct rope *rope=
+		(struct rope *) ROPE_ALLOC_F(alloc_ctx, sizeof(*rope));
+	if (rope != NULL)
+		rope_create(rope, split_ctx, alloc_ctx);
+	return rope;
+}
+
 static inline rope_size_t
 rope_node_size(struct rope_node *node)
 {
@@ -96,7 +150,7 @@ rope_leaf_size(struct rope_node *node)
 	return node->leaf_size;
 }
 
-static inline void *
+static inline rope_data_t
 rope_leaf_data(struct rope_node *node)
 {
 	return node->data;
@@ -108,63 +162,47 @@ rope_size(struct rope *rope)
 	return rope_node_size(rope->root);
 }
 
-/** Initialize an empty rope. */
-static inline void
-rope_create(struct rope *rope, rope_split_func split_func, void *split_ctx,
-	    rope_alloc_func alloc_func, rope_free_func free_func,
-	    void *alloc_ctx)
-{
-	rope->root = NULL;
-	rope->split = split_func;
-	rope->split_ctx = split_ctx;
-	rope->alloc = alloc_func;
-	rope->free = free_func;
-	rope->alloc_ctx = alloc_ctx;
-}
-
-/** Create a new empty rope.
- * @param split_func a function which returns
- *                  a pointer to substring
- *                  given an offset. Used
- *                  to split substrings when
- *                  inserting into a rope.
- * @param alloc_func used to allocate memory
- * @param free_func  used to free memory
- * @param alloc_ctx  allocator context
- *
- * @return  an empty rope, or NULL if failed
- *          to allocate memory
- */
-static inline struct rope *
-rope_new(rope_split_func split_func, void *split_ctx,
-	 rope_alloc_func alloc_func, rope_free_func free_func, void *alloc_ctx)
-{
-	struct rope *rope= (struct rope *) alloc_func(alloc_ctx,
-						      sizeof(struct rope));
-	if (rope == NULL)
-		return NULL;
-	rope_create(rope, split_func, split_ctx, alloc_func, free_func, alloc_ctx);
-	return rope;
-}
-
-
-/** Delete rope contents. Can also be used
+/**
+ * Delete rope contents. Can also be used
  * to free a rope which is allocated on stack.
  * Doesn't delete rope substrings, only
  * rope nodes.
  */
 void
-rope_clear(struct rope *rope);
+rope_clear(struct rope *rope)
+{
+#ifdef ROPE_FREE_F
+	struct rope_node *it = rope->root;
+	struct rope_node *save;
+
+	/* Destruction by rotation */
+	while (it != NULL) {
+		if (it->link[0] == NULL) {
+			/* Remove node */
+			save = it->link[1];
+			ROPE_FREE_F(rope->alloc_ctx, it);
+		} else {
+			/* Rotate right */
+			save = it->link[0];
+			it->link[0] = save->link[1];
+			save->link[1] = it;
+		}
+		it = save;
+	}
+#endif /* ROPE_FREE_F */
+	rope->root = NULL;
+}
 
 /** Delete a rope allocated with rope_new() */
 static inline void
 rope_delete(struct rope *rope)
 {
 	rope_clear(rope);
-	rope->free(rope->alloc_ctx, rope);
+	ROPE_FREE_F_IMPL(rope->alloc_ctx, rope);
 }
 
-/** Insert a substring into a rope at the given
+/**
+ * Insert a substring into a rope at the given
  * offset.
  * If offset is greater than rope size, insertion
  * happens at the end.
@@ -174,17 +212,18 @@ rope_delete(struct rope *rope)
  *             tree node
  */
 int
-rope_insert(struct rope *rope, rope_size_t offset, void *data,
+rope_insert(struct rope *rope, rope_size_t offset, rope_data_t data,
 	    rope_size_t size);
 
 /** Append a substring at rope tail. */
 static inline int
-rope_append(struct rope *rope, void *data, size_t size)
+rope_append(struct rope *rope, rope_data_t data, size_t size)
 {
 	return rope_insert(rope, rope_size(rope), data, size);
 }
 
-/** Make sure there is a rope node which has a substring
+/**
+ * Make sure there is a rope node which has a substring
  * which starts at the given offset. Useful when
  * rope substrings carry additional information.
  *
@@ -194,7 +233,7 @@ rope_append(struct rope *rope, void *data, size_t size)
 struct rope_node *
 rope_extract_node(struct rope *rope, rope_size_t offset);
 
-static inline void *
+static inline rope_data_t
 rope_extract(struct rope *rope, rope_size_t offset)
 {
 	return rope_leaf_data(rope_extract_node(rope, offset));
@@ -220,12 +259,10 @@ rope_iter_create(struct rope_iter *it, struct rope *rope)
 static inline struct rope_iter *
 rope_iter_new(struct rope *rope)
 {
-	struct rope_iter *it = (struct rope_iter *)
-			rope->alloc(rope->alloc_ctx, sizeof(struct rope_iter));
-
-	if (it == NULL)
-		return NULL;
-	rope_iter_create(it, rope);
+	struct rope_iter *it =
+		(struct rope_iter *) ROPE_ALLOC_F(rope->alloc_ctx, sizeof(*it));
+	if (it != NULL)
+		rope_iter_create(it, rope);
 	return it;
 }
 
@@ -250,12 +287,12 @@ rope_iter_next(struct rope_iter *it);
 static inline void
 rope_iter_delete(struct rope_iter *it)
 {
-	it->rope->free(it->rope->alloc_ctx, it);
+	ROPE_FREE_F_IMPL(it->rope->alloc_ctx, it);
 }
 
 /** Apply visit_leaf function to every rope leaf. */
 void
-rope_traverse(struct rope *rope, void (*visit_leaf)(void *, size_t));
+rope_traverse(struct rope *rope, void (*visit_leaf)(rope_data_t, size_t));
 
 /** Check AVL tree consistency. */
 void
@@ -263,10 +300,603 @@ rope_check(struct rope *rope);
 
 /** Pretty print a rope. */
 void
-rope_pretty_print(struct rope *rope, void (*print_leaf)(void *, size_t));
+rope_pretty_print(struct rope *rope, void (*print_leaf)(rope_data_t, size_t));
+
+/** }}} */
+
+static inline int
+rope_node_height(struct rope_node *node)
+{
+	return node ? node->height : 0;
+}
+
+static inline void
+rope_relink(struct rope_node *node)
+{
+	node->tree_size = (rope_node_size(node->link[0]) +
+			   rope_node_size(node->link[1]) +
+			   node->leaf_size);
+	node->height = MAX(rope_node_height(node->link[0]),
+			   rope_node_height(node->link[1])) + 1;
+}
+
+static inline struct rope_node *
+rope_node_new(struct rope *rope, rope_data_t data, rope_size_t size)
+{
+	struct rope_node *node =
+		(struct rope_node *) ROPE_ALLOC_F(rope->alloc_ctx,
+						  sizeof(*node));
+	if (node == NULL)
+		return NULL;
+	node->height = 1;
+	node->tree_size = node->leaf_size = size;
+	node->data = data;
+	node->link[0] = node->link[1] = NULL;
+
+	return node;
+}
+
+static struct rope_node *
+rope_node_split(struct rope *rope, struct rope_node *node, rope_size_t offset)
+{
+	rope_size_t old_size = node->leaf_size;
+	node->leaf_size = offset;
+
+	rope_data_t data = ROPE_SPLIT_F(rope->split_ctx, node->data, old_size,
+					offset);
+
+	return rope_node_new(rope, data, old_size - offset);
+}
+
+static inline struct rope_node *
+avl_rotate_single(struct rope_node *parent, int direction)
+{
+	struct rope_node *save = parent->link[!direction];
+
+	parent->link[!direction] = save->link[direction];
+	save->link[direction] = parent;
+
+	/* First relink the parent, since it's now a child. */
+	rope_relink(parent);
+	rope_relink(save);
+
+	return save;
+}
+
+static inline struct rope_node *
+avl_rotate_double(struct rope_node *parent, int direction)
+{
+	parent->link[!direction] =
+		avl_rotate_single(parent->link[!direction], !direction);
+	return avl_rotate_single(parent, direction);
+}
+
+/** Rebalance the tree. */
+static inline void
+avl_rebalance_after_insert(struct rope_node ***path,
+			   struct rope_node ***p_end, int insert_height)
+{
+	while (p_end > path) {
+		struct rope_node *left = **p_end--;
+		struct rope_node *parent = **p_end;
+		/*
+		 * To use the same rotation functions, set mirror
+		 * to 1 if left is right and right is left.
+		 */
+		int mirror = left != parent->link[0];
+		struct rope_node *right = parent->link[!mirror];
+
+		int left_height = rope_node_height(left);
+		int right_height = rope_node_height(right);
+		parent->height = MAX(left_height, right_height) + 1;
+		/*
+		 * Rotations flattened the tree, so there is no
+		 * further changes in height up the insertion
+		 * path.
+		 */
+		if (left_height == right_height)
+			break;
+		/*
+		 * We've been adding a new child (children) to the
+		 * 'left' subtree, so it couldn't get shorter.
+		 * The old difference between subtrees was in the
+		 * range -1..1. So the new difference can only be
+		 * in the range -1..1 + height(new_node).
+		 */
+		if (left_height - right_height >= 2) {
+			struct rope_node *l_left = left->link[mirror];
+			struct rope_node *l_right = left->link[!mirror];
+			int l_left_height = rope_node_height(l_left);
+			int l_right_height = rope_node_height(l_right);
+			/*
+			 * Rotate in the direction, opposite to
+			 * the skew. E.g. if we have two left-left
+			 * nodes hanging off the tree, rotate the
+			 * parent clockwise. If we have a left
+			 * node with a right child, rotate the
+			 * child counterclockwise, and then the whole
+			 * thing clockwise.
+			 */
+			if (l_left_height >= l_right_height)
+				**p_end = avl_rotate_single(parent,
+							    !mirror);
+			else
+				**p_end = avl_rotate_double(parent,
+							    !mirror);
+			/*
+			 * If we inserted only one node, no more
+			 * than 1 rotation is required (see
+			 * D. Knuth, Introduction to Algorithms,
+			 * vol. 3.). For 2 nodes, its max
+			 * 2 rotations.
+			 */
+			if (l_left_height != l_right_height &&
+			    --insert_height == 0)
+				break;
+		}
+	}
+}
+
+/* This is a copy-cat of the previous loop,
+ * with the exception that the heuristic to break
+ * the loop is different.
+ */
+static inline void
+avl_rebalance_after_delete(struct rope_node ***path,
+			   struct rope_node ***p_end)
+{
+	while (p_end > path) {
+		struct rope_node *left = **p_end--;
+		struct rope_node *parent = **p_end;
+
+		int mirror = left != parent->link[0];
+
+		struct rope_node *right = parent->link[!mirror];
+
+		int left_height = rope_node_height(left);
+		int right_height = rope_node_height(right);
+
+		parent->height = MAX(left_height, right_height) + 1;
+		/*
+		 * Right was taller, and we deleted from the left.
+		 * We can break the loop since there can be no
+		 * changes in height up in the route.
+		 */
+		if (left_height - right_height == -1)
+			break;
+
+		if (left_height - right_height <= -2) {
+			struct rope_node *r_left = right->link[mirror];
+			struct rope_node *r_right = right->link[!mirror];
+			int r_left_height = rope_node_height(r_left);
+			int r_right_height = rope_node_height(r_right);
+
+			if (r_left_height <= r_right_height)
+				**p_end = avl_rotate_single(parent, mirror);
+			else
+				**p_end = avl_rotate_double(parent, mirror);
+		}
+	}
+}
+
+/**
+ * Find a rope node which contains the substring at offset,
+ * adjusting tree size with adjust_size and saving the path
+ * in path.
+ *
+ * @return the end of the route.
+ */
+static inline struct rope_node ***
+avl_route_to_offset(struct rope_node ***path, rope_size_t *p_offset,
+		    ssize_t adjust_size)
+{
+	rope_size_t offset = *p_offset;
+	while (**path) {
+		struct rope_node *node = **path;
+
+		node->tree_size += adjust_size;
+
+		rope_size_t left_size = rope_node_size(node->link[0]);
+
+		if (offset < left_size) {
+			/* The offset lays in  the left subtree. */
+			*++path = &node->link[0];
+		} else {
+			/* Make the new offset relative to the parent. */
+			offset -= left_size;
+
+			if (offset < node->leaf_size) {
+				/* Found. */
+				break;
+			} else {
+				/*
+				 * Make the offset relative to the
+				 * leftmost node in the right subtree.
+				 */
+				offset -= node->leaf_size;
+			}
+			*++path = &node->link[1];
+		}
+	}
+	*p_offset = offset;
+	return path;
+}
+
+/**
+ * Route to successor or predecessor node of the node
+ * in **path. It's either the rightmost leaf of the left child
+ * (previous node) or leftmost leaf of the right child.
+ */
+static inline struct rope_node ***
+avl_route_to_next(struct rope_node ***path, int dir, rope_ssize_t adjust_size)
+{
+	struct rope_node *node = **path;
+	*++path = &node->link[dir];
+	while (**path) {
+		node = **path;
+		node->tree_size += adjust_size;
+		*++path = &node->link[!dir];
+	}
+	return path;
+}
+
+/**
+ * A new node is always inserted at a leaf position.
+ * If insertion unbalances the tree, the rebalancing
+ * procedure may put the node into an intermediate position.
+ *
+ * While traversing the tree, we simultaneously update
+ * tree sizes of all intermediate nodes, taking into account
+ * the size of the new node.
+ *
+ * When insertion offset falls at the middle of an existing node,
+ * we truncate this node and attach its tail to the left leaf
+ * of the new node. This trim operation doesn't decrease the old
+ * subtree height, and, while it does change subtree size
+ * temporarily, as long as we attach the new node to the right
+ * subtree of the truncated node, truncation has no effect on the
+ * tree size either.
+ *
+ * Rebalancing, when it occurs, will correctly update subtree
+ * heights and sizes of all modified nodes.
+ */
+int
+rope_insert(struct rope *rope, rope_size_t offset, rope_data_t data,
+	    rope_size_t size)
+{
+	if (offset > rope_size(rope))
+		offset = rope_size(rope);
+
+	assert(size);
+
+	struct rope_node *new_node = rope_node_new(rope, data, size);
+
+	if (new_node == NULL)
+		return -1;
+
+	struct rope_node **path[ROPE_HEIGHT_MAX];
+	path[0] = &rope->root;
+
+	struct rope_node ***p_end = avl_route_to_offset(path, &offset, size);
+	if (**p_end != NULL) {
+		/*
+		 * The offset is inside an existing
+		 * substring in the rope. If offset is 0,
+		 * then insert the new node at the rightmost leaf
+		 * of the left child. Otherwise, cut the tail of
+		 * the substring, make it a prefix of the inserted
+		 * string, and insert the result at the leftmost
+		 * leaf of the right child.
+		 */
+		if (offset != 0) {
+			struct rope_node *split_node;
+			split_node = rope_node_split(rope, **p_end, offset);
+			if (split_node == NULL)
+				return -1;
+			split_node->link[0] = new_node;
+			split_node->height++;
+			split_node->tree_size += new_node->tree_size;
+			new_node = split_node;
+		}
+		p_end = avl_route_to_next(p_end, offset != 0,
+					  new_node->tree_size);
+	}
+	**p_end = new_node;
+	avl_rebalance_after_insert(path, p_end, new_node->height);
+	return 0;
+}
+
+/** Make sure there is a rope node at the given offset. */
+struct rope_node *
+rope_extract_node(struct rope *rope, rope_size_t offset)
+{
+	assert(offset < rope_size(rope));
+
+	struct rope_node **path[ROPE_HEIGHT_MAX];
+	path[0] = &rope->root;
+
+	struct rope_node ***p_end = avl_route_to_offset(path, &offset, 0);
+	if (offset == 0)
+		return **p_end;
+	struct rope_node *new_node = rope_node_split(rope, **p_end, offset);
+	if (new_node == NULL)
+		return NULL;
+	p_end = avl_route_to_next(p_end, 1, new_node->tree_size);
+	**p_end = new_node;
+	avl_rebalance_after_insert(path, p_end, new_node->height);
+	return new_node;
+}
+
+/**
+ * Erase a single element from the rope.
+ * This is a straightforward implementation for a single-element
+ * deletion from a rope. A generic cut from a rope involves
+ * 2 tree splits and one merge.
+ *
+ * When deleting a single element, 3 cases are possible:
+ * - offset falls at a node with a single element. In this
+ *   case we perform a normal AVL tree delete.
+ * - offset falls at the end or the beginning of an existing node
+ *   with leaf_size > 1. In that case we trim the existing node
+ *   and return.
+ * - offset falls inside an existing node. In that case
+ *   we split the existing node at offset, and insert the tail.
+ *
+ * The implementation is a copycat of rope_insert(). If you're
+ * trying to understand the code, it's recommended to start
+ * from rope_insert().
+ */
+int
+rope_erase(struct rope *rope, rope_size_t offset)
+{
+	assert(offset < rope_size(rope));
+
+	struct rope_node **path[ROPE_HEIGHT_MAX];
+	path[0] = &rope->root;
+
+	struct rope_node ***p_end = avl_route_to_offset(path, &offset, -1);
+
+	struct rope_node *node = **p_end;
+
+	if (node->leaf_size > 1) {
+		/* Check if we can simply trim the node. */
+		if (offset == 0) {
+			/* Cut the head. */
+			node->data = ROPE_SPLIT_F(rope->split_ctx, node->data,
+						  node->leaf_size, 1);
+			node->leaf_size -= 1;
+			return 0;
+		}
+		rope_size_t size = node->leaf_size;
+		/* Cut the tail */
+		rope_data_t next = ROPE_SPLIT_F(rope->split_ctx, node->data,
+						node->leaf_size, offset);
+		node->leaf_size = offset;
+		if (offset == size - 1)
+			return 0; /* Trimmed the tail, nothing else to do */
+		/*
+		 * Offset falls inside a substring. Erase the
+		 * first field and insert the tail.
+		 */
+		next = ROPE_SPLIT_F(rope->split_ctx, next, size - offset, 1);
+		struct rope_node *new_node =
+			rope_node_new(rope, next, size - offset - 1);
+		if (new_node == NULL)
+			return -1;
+		/* Trim the old node. */
+		p_end = avl_route_to_next(p_end, 1, new_node->tree_size);
+		**p_end = new_node;
+		avl_rebalance_after_insert(path, p_end, new_node->height);
+		return 0;
+	}
+	/* We need to delete the node. */
+	assert(offset == 0);
+	int direction;
+	if (node->link[0] != NULL && node->link[1] != NULL) {
+		/*
+		 * The node has two non-NULL leaves. We can't
+		 * simply delete the node since in that case we
+		 * won't know what to do with one of the leaves.
+		 * Instead of deleting the node, store in it data
+		 * from the rightmost node in the left subtree, or
+		 * the leftmost node in the right subtree,
+		 * (depending on which subtree is taller), and
+		 * delete this leftmost/rightmost node instead.
+		 */
+		struct rope_node *save = node;
+		direction = node->link[1]->height > node->link[0]->height;
+		p_end = avl_route_to_next(p_end, direction, 0) - 1;
+		node = **p_end;
+		/* Move the data pointers. */
+		save->data = node->data;
+		save->leaf_size = node->leaf_size;
+		/*
+		 * Now follow the path again and update tree_size
+		 * in the parents of the moved child.
+	         */
+		save = save->link[direction];
+		while (save != node) {
+			save->tree_size -= node->leaf_size;
+			save = save->link[!direction];
+		}
+	} else {
+		/*
+		 * Left or right subtree are NULL, so we
+		 * can simply put the non-NULL leaf in place
+		 * of the parent.
+		 */
+		direction = node->link[0] == NULL;
+	}
+	**p_end = node->link[direction];
+	ROPE_FREE_F_IMPL(rope->alloc_ctx, node);
+	avl_rebalance_after_delete(path, p_end);
+	return 0;
+}
+
+/**
+ * Traverse left until the left subtree is NULL,
+ * save the path in iter->path.
+ * @pre iter->path[iter->top] is not NULL
+ * @post iter->path[iter->top] is not NULL and points to the last
+ * not-NULL node.
+ */
+static inline void
+rope_iter_down_to_leaf(struct rope_iter *it)
+{
+	while (it->top[0]->link[0] != NULL) {
+		it->top[1] = it->top[0]->link[0];
+		it->top++;
+	}
+}
+
+struct rope_node *
+rope_iter_start(struct rope_iter *it)
+{
+	it->top = it->path;
+	it->top[0] = it->rope->root;
+
+	if (it->top[0] != NULL)
+		rope_iter_down_to_leaf(it);
+	return it->top[0];
+}
+
+struct rope_node *
+rope_iter_next(struct rope_iter *it)
+{
+	if (it->top[0]->link[1] != NULL) {
+		it->top[1] = it->top[0]->link[1];
+		it->top++;
+		rope_iter_down_to_leaf(it);
+	} else {
+		/*
+		 * Right subtree is NULL. Left subtree is fully
+		 * traversed (guaranteed by the order in which we
+		 * iterate). Pop up the path until the current
+		 * node points to a link we haven't visited
+		 * yet: this is the case when we return to the
+		 * parent from its left child.
+		 */
+		do {
+			/*
+			 * Returned to the root from the right
+			 * subtree: the tree is fully traversed.
+			 */
+			if (it->top == it->path) {
+				/*
+				 * Crash, rather than infinite loop
+				 * if next() is called beyond last.
+				 */
+				it->top[0] = NULL;
+				return NULL;
+			}
+			it->top--;
+		} while (it->top[1] == it->top[0]->link[1]);
+	}
+	return *it->top;
+}
+
+/** Apply visit_leaf function to every rope leaf. */
+void
+rope_traverse(struct rope *rope, void (*visit_leaf)(rope_data_t, size_t))
+{
+	struct rope_iter iter;
+	rope_iter_create(&iter, rope);
+
+	struct rope_node *leaf;
+
+	for (leaf = rope_iter_start(&iter);
+	     leaf != NULL;
+	     leaf = rope_iter_next(&iter)) {
+
+		visit_leaf(leaf->data, leaf->leaf_size);
+	}
+}
+
+void
+rope_check(struct rope *rope)
+{
+	struct rope_iter iter;
+	rope_iter_create(&iter, rope);
+
+	struct rope_node *node;
+
+	for (node = rope_iter_start(&iter);
+	     node != NULL;
+	     node = rope_iter_next(&iter)) {
 
-#if defined(__cplusplus)
+		assert(node->leaf_size != 0);
+
+		assert(node->tree_size == rope_node_size(node->link[0])
+		       + rope_node_size(node->link[1]) + node->leaf_size);
+
+		assert(node->height == (MAX(rope_node_height(node->link[0]),
+					    rope_node_height(node->link[1])) + 1));
+		if (node->leaf_size == 0 ||
+		    node->tree_size != (rope_node_size(node->link[0]) +
+					rope_node_size(node->link[1]) +
+					node->leaf_size) ||
+		    node->height != MAX(rope_node_height(node->link[0]),
+					rope_node_height(node->link[1])) + 1)
+			abort();
+	}
 }
-#endif /* defined(__cplusplus) */
 
-#endif /* INCLUDES_TARANTOOL_ROPE_H */
+static void
+rope_node_print(struct rope_node *node, void (*print)(rope_data_t, size_t),
+		const char *prefix, int dir)
+{
+	const char *conn[] = { "┌──", "└──" };
+
+	const char *padding[] = { "│   ", "   " };
+
+	rope_size_t child_prefix_len = strlen(prefix) + strlen(padding[0]) + 1;
+	char *child_prefix = malloc(child_prefix_len);
+
+	if (node && (node->link[0] || node->link[1])) {
+		snprintf(child_prefix, child_prefix_len - 1,
+			 "%s%s", prefix, padding[!dir]);
+		rope_node_print(node->link[0], print, child_prefix, 0);
+	}
+
+	snprintf(child_prefix, child_prefix_len - 1, "%s%s",
+		 prefix, padding[dir]);
+	printf("%s%s", prefix, conn[dir]);
+
+	if (node == NULL) {
+		printf("nil\n");
+	} else {
+
+		printf("{ len = %zu, height = %d, data = '",
+		       (size_t) node->leaf_size, node->height);
+		print(node->data, node->leaf_size);
+		printf("'}\n");
+
+		if (node->link[0] || node->link[1])
+			rope_node_print(node->link[1], print, child_prefix, 1);
+	}
+
+	free(child_prefix);
+}
+
+void
+rope_pretty_print(struct rope *rope, void (*print_leaf)(rope_data_t, size_t))
+{
+	printf("size = %zu\nstring = '", (size_t) rope_size(rope));
+	rope_traverse(rope, print_leaf);
+	printf("'\n");
+	rope_node_print(rope->root, print_leaf, "", true);
+	printf("\n");
+}
+
+#undef rope_alloc_ctx_t
+#undef rope_split_ctx_t
+#undef rope_data_t
+#undef ROPE_SPLIT_F
+#undef ROPE_ALLOC_F
+#undef ROPE_FREE_F
+#undef ROPE_FREE_F_IMPL
+
+#ifdef NEED_UNDEF_MAX
+#undef NEED_UNDEF_MAX
+#undef MAX
+#endif
diff --git a/test/unit/rope.c b/test/unit/rope.c
index e19dcaa10..5ab4c51f7 100644
--- a/test/unit/rope.c
+++ b/test/unit/rope.c
@@ -1,4 +1,3 @@
-#include "salad/rope.h"
 #include "unit.h"
 #include "rope_common.h"
 
diff --git a/test/unit/rope_avl.c b/test/unit/rope_avl.c
index 9e978f46c..b095fb1a6 100644
--- a/test/unit/rope_avl.c
+++ b/test/unit/rope_avl.c
@@ -1,4 +1,3 @@
-#include "salad/rope.h"
 #include "unit.h"
 #include "rope_common.h"
 
diff --git a/test/unit/rope_basic.c b/test/unit/rope_basic.c
index b4a0e7876..d9e116292 100644
--- a/test/unit/rope_basic.c
+++ b/test/unit/rope_basic.c
@@ -1,4 +1,3 @@
-#include "salad/rope.h"
 #include "unit.h"
 #include "rope_common.h"
 
diff --git a/test/unit/rope_common.h b/test/unit/rope_common.h
index 6c52153b2..3b3823ced 100644
--- a/test/unit/rope_common.h
+++ b/test/unit/rope_common.h
@@ -3,17 +3,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-static inline void *
-str_getn(void *ctx, void *data, size_t size, size_t offset)
+static inline char *
+str_getn(void *ctx, char *data, size_t size, size_t offset)
 {
 	(void) ctx;
-	return (char *) data + offset;
+	return data + offset;
 }
 
 static inline void
-str_print(void *data, size_t n)
+str_print(char *data, size_t n)
 {
-	printf("%.*s", (int) n, (char *) data);
+	printf("%.*s", (int) n, data);
 }
 
 static inline void *
@@ -30,10 +30,19 @@ mem_free(void *data, void *ptr)
 	free(ptr);
 }
 
+#define ROPE_ALLOC_F mem_alloc
+#define ROPE_FREE_F mem_free
+#define ROPE_SPLIT_F str_getn
+#define rope_data_t char *
+#define rope_alloc_ctx_t void *
+#define rope_split_ctx_t void *
+
+#include "salad/rope.h"
+
 static inline struct rope *
 test_rope_new()
 {
-	return rope_new(str_getn, NULL, mem_alloc, mem_free, NULL);
+	return rope_new(NULL, NULL);
 }
 
 static inline void
diff --git a/test/unit/rope_stress.c b/test/unit/rope_stress.c
index dc3f2817e..bc312d7c0 100644
--- a/test/unit/rope_stress.c
+++ b/test/unit/rope_stress.c
@@ -1,4 +1,3 @@
-#include "salad/rope.h"
 #include <time.h>
 #include "unit.h"
 #include "rope_common.h"
@@ -10,7 +9,7 @@ test_rope_stress_small()
 {
 	header();
 
-	struct rope *rope = rope_new(str_getn, NULL, mem_alloc, mem_free, NULL);
+	struct rope *rope = test_rope_new();
 	const int iterations = 500;
 	int i = 0;
 	for (i = 0; i < iterations; ++i) {
@@ -39,7 +38,7 @@ test_rope_stress_large()
 {
 	header();
 
-	struct rope *rope = rope_new(str_getn, NULL, mem_alloc, mem_free, NULL);
+	struct rope *rope = test_rope_new();
 	const int iterations = 50000;
 	int i = 0;
 	for (i = 0; i < iterations; ++i) {
-- 
2.15.1 (Apple Git-101)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tarantool-patches] Re: [PATCH 1/3] vinyl: remove vy_apply_upsert_ops
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 1/3] vinyl: remove vy_apply_upsert_ops Vladislav Shpilevoy
@ 2018-05-08 20:18   ` Konstantin Osipov
  0 siblings, 0 replies; 7+ messages in thread
From: Konstantin Osipov @ 2018-05-08 20:18 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

* Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/04/28 08:50]:
> Function vy_apply_upsert_opts originaly appears in this commit:
> 5627e53bf020bf83b8f74d3d04b93047bdf221b4, where it is a
> refactored version of a sophia upsertion. But when a vy_stmt was
> introduced, vinyl_apply_upsert_ops works just like ordinary
> tuple_upsert_execute. Remove this useless wrapper.
> ---
>  src/box/vy_upsert.c | 43 +++++--------------------------------------
>  1 file changed, 5 insertions(+), 38 deletions(-)

Pushed.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tarantool-patches] Re: [PATCH 2/3] tuple_update: remove alloc and alloc_ctx args
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 2/3] tuple_update: remove alloc and alloc_ctx args Vladislav Shpilevoy
@ 2018-05-08 20:18   ` Konstantin Osipov
  0 siblings, 0 replies; 7+ messages in thread
From: Konstantin Osipov @ 2018-05-08 20:18 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

* Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/04/28 08:50]:
> They are always region_aligned_alloc and region pointer. Lets use
> them always inside tuple_update.c, with no necessity to pass via
> args.

I think it's bikeshed, but I can push it if you like.


-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tarantool-patches] Re: [PATCH 3/3] rope: make rope library be C template using macros
  2018-04-27 22:36 ` [tarantool-patches] [PATCH 3/3] rope: make rope library be C template using macros Vladislav Shpilevoy
@ 2018-05-08 20:22   ` Konstantin Osipov
  0 siblings, 0 replies; 7+ messages in thread
From: Konstantin Osipov @ 2018-05-08 20:22 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

* Vladislav Shpilevoy <v.shpilevoy@tarantool.org> [18/04/28 08:50]:
> Rope library stores alloc, split and free functions by pointer,
> that is a huge slog at performance. There is no reason, why the
> rope library can not become a template.

I believe you still should keep some "slowpath" code in rope.c -
smaller binary footprint is usually faster.

I'm not sure though it'd be easy to extract.

In any case I'd wait with pushing this patch until we see the
rest.

-- 
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2018-05-08 20:22 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-27 22:36 [tarantool-patches] [PATCH 0/3] Optimize and simplify some tuple_update things Vladislav Shpilevoy
2018-04-27 22:36 ` [tarantool-patches] [PATCH 1/3] vinyl: remove vy_apply_upsert_ops Vladislav Shpilevoy
2018-05-08 20:18   ` [tarantool-patches] " Konstantin Osipov
2018-04-27 22:36 ` [tarantool-patches] [PATCH 2/3] tuple_update: remove alloc and alloc_ctx args Vladislav Shpilevoy
2018-05-08 20:18   ` [tarantool-patches] " Konstantin Osipov
2018-04-27 22:36 ` [tarantool-patches] [PATCH 3/3] rope: make rope library be C template using macros Vladislav Shpilevoy
2018-05-08 20:22   ` [tarantool-patches] " Konstantin Osipov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox