From 0a7ca4b2ed70f0f8c903a73e2a0293ef4ea7b92e Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tarantool.org>
Date: Tue, 20 Jun 2017 14:42:42 +0300
Subject: [PATCH] Review fixes for tuple_to_yaml()

* Fix handling of encoded data
* Remove \n added by libyaml
* Add a test case

Follow up #128
---
 src/box/tuple_convert.c | 47 +++++++++++++++++++++++++----------------
 test/box/tuple.result   | 30 ++++++++++++++++++++++++++
 test/box/tuple.test.lua | 13 +++++++++++-
 3 files changed, 71 insertions(+), 19 deletions(-)

diff --git a/src/box/tuple_convert.c b/src/box/tuple_convert.c
index 6cb79e8013..300ca8918b 100644
--- a/src/box/tuple_convert.c
+++ b/src/box/tuple_convert.c
@@ -64,12 +64,13 @@ int
 append_output(void *arg, unsigned char *buf, size_t len)
 {
 	(void) arg;
-	char *buf_out = region_alloc(&fiber()->gc, len);
+	char *buf_out = region_alloc(&fiber()->gc, len + 1);
 	if (!buf_out) {
-		diag_set(OutOfMemory, len , "region_alloc", "append_output");
+		diag_set(OutOfMemory, len , "region", "tuple_to_yaml");
 		return 0;
 	}
 	memcpy(buf_out, buf, len);
+	buf_out[len] = '\0';
 	return 1;
 }
 
@@ -132,17 +133,19 @@ encode_array(yaml_emitter_t *emitter, const char **data)
 	return 1;
 }
 
+#define LUAYAML_TAG_PREFIX "tag:yaml.org,2002:"
+
 static int
 encode_node(yaml_emitter_t *emitter, const char **data)
 {
 	size_t len = 0;
 	const char *str = "";
+	size_t binlen = 0;
+	char *bin = NULL;
 	yaml_char_t *tag = NULL;
 	yaml_event_t ev;
 	yaml_scalar_style_t style = YAML_PLAIN_SCALAR_STYLE;
-	int is_binary = 0;
 	char buf[FPCONV_G_FMT_BUFSIZE];
-	char *binary_encode = NULL;
 	int type = mp_typeof(**data);
 	switch(type) {
 	case MP_UINT:
@@ -184,16 +187,17 @@ encode_node(yaml_emitter_t *emitter, const char **data)
 		}
 		style = YAML_ANY_SCALAR_STYLE;
 		/* Binary or not UTF8 */
-		is_binary = 1;
-		binary_encode = (char *) malloc(base64_bufsize(len));
-		if (binary_encode == NULL) {
-			diag_set(OutOfMemory, base64_bufsize(len),
-				 "malloc", "tuple_to_yaml");
+		binlen = base64_bufsize(len);
+		bin = (char *) malloc(binlen);
+		if (bin == NULL) {
+			diag_set(OutOfMemory, binlen, "malloc",
+				 "tuple_to_yaml");
 			return 0;
 		}
-		base64_encode(str, len, binary_encode, base64_bufsize(len));
-		str = binary_encode;
-		tag = (yaml_char_t *) "binary";
+		binlen = base64_encode(str, len, bin, binlen);
+		str = bin;
+		len = binlen;
+		tag = (yaml_char_t *) LUAYAML_TAG_PREFIX "binary";
 		break;
 	case MP_BOOL:
 		if (mp_decode_bool(data)) {
@@ -206,7 +210,11 @@ encode_node(yaml_emitter_t *emitter, const char **data)
 		break;
 	case MP_NIL:
 	case MP_EXT:
-		mp_decode_nil(data);
+		if (type == MP_NIL) {
+			mp_decode_nil(data);
+		} else {
+			mp_next(data);
+		}
 		style = YAML_PLAIN_SCALAR_STYLE;
 		str = "null";
 		len = 4;
@@ -217,14 +225,14 @@ encode_node(yaml_emitter_t *emitter, const char **data)
 
 	int rc = 1;
 	if (!yaml_scalar_event_initialize(&ev, NULL, tag, (unsigned char *)str,
-					  len, !is_binary, !is_binary,
+					  len, bin == NULL, bin == NULL,
 					  style) ||
 	    !yaml_emitter_emit(emitter, &ev)) {
 		diag_set(OutOfMemory, len, "malloc", "tuple_to_yaml");
 		rc = 0;
 	}
-	if (is_binary)
-		free(binary_encode);
+	if (bin != NULL)
+		free(bin);
 
 	return rc;
 }
@@ -244,8 +252,7 @@ tuple_to_yaml(const struct tuple *tuple)
 	}
 	yaml_emitter_set_unicode(&emitter, 1);
 	yaml_emitter_set_indent(&emitter, 2);
-	yaml_emitter_set_width(&emitter, 2);
-	yaml_emitter_set_break(&emitter, YAML_LN_BREAK);
+	yaml_emitter_set_width(&emitter, INT_MAX);
 	yaml_emitter_set_output(&emitter, &append_output, NULL);
 
 	if (!yaml_stream_start_event_initialize(&ev, YAML_UTF8_ENCODING) ||
@@ -275,6 +282,10 @@ tuple_to_yaml(const struct tuple *tuple)
 		diag_set(OutOfMemory, total_len, "region", "tuple_to_yaml");
 		return NULL;
 	}
+	/* Remove trailing "\n\0" added by libyaml */
+	assert(total_len > 2);
+	assert(buf[total_len - 1] == '\0' && buf[total_len - 2] == '\n');
+	buf[total_len - 2] = '\0';
 	return buf;
 error:
 	yaml_emitter_delete(&emitter);
diff --git a/test/box/tuple.result b/test/box/tuple.result
index 7f119c8235..7e0fd7b059 100644
--- a/test/box/tuple.result
+++ b/test/box/tuple.result
@@ -978,6 +978,36 @@ collectgarbage('collect') -- collect huge string
 ---
 - 0
 ...
+-- testing tostring
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+null = nil
+t = box.tuple.new({1, -2, 1.2, -1.2}, 'x', 'y', 'z', null, true, false,
+    {bin = "\x08\x5c\xc2\x80\x12\x2f",
+    big_num = tonumber64('18446744073709551615'),
+    map = {key = "value"},
+    double=1.0000000001,
+    utf8="Кудыкины горы"});
+---
+...
+tostring(t);
+---
+- '[[1, -2, 1.2, -1.2], ''x'', ''y'', ''z'', null, true, false, {''big_num'': 18446744073709551615,
+  ''double'': 1.0000000001, ''utf8'': ''Кудыкины горы'', ''bin'': !!binary CFzCgBIv,
+  ''map'': {''key'': ''value''}}]'
+...
+t;
+---
+- [[1, -2, 1.2, -1.2], 'x', 'y', 'z', null, true, false, {'big_num': 18446744073709551615,
+    'double': 1.0000000001, 'utf8': 'Кудыкины горы', 'bin': !!binary CFzCgBIv, 'map': {
+      'key': 'value'}}]
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
 test_run:cmd("clear filter")
 ---
 - true
diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
index f7d700990d..a006b3f7b3 100644
--- a/test/box/tuple.test.lua
+++ b/test/box/tuple.test.lua
@@ -208,7 +208,6 @@ test_run:cmd("setopt delimiter ';'")
 t = box.tuple.new({'a','b','c','a', -1, 0, 1, 2, true, 9223372036854775807ULL,
     -9223372036854775807LL});
 test_run:cmd("setopt delimiter ''");
-
 t:find('a')
 t:find(1, 'a')
 t:find('c')
@@ -321,4 +320,16 @@ ffi.typeof('struct tuple')
 box.tuple.new(string.rep('x', 100 * 1024 * 1024)) == nil
 collectgarbage('collect') -- collect huge string
 
+-- testing tostring
+test_run:cmd("setopt delimiter ';'")
+null = nil
+t = box.tuple.new({1, -2, 1.2, -1.2}, 'x', 'y', 'z', null, true, false,
+    {bin = "\x08\x5c\xc2\x80\x12\x2f",
+    big_num = tonumber64('18446744073709551615'),
+    map = {key = "value"},
+    double=1.0000000001,
+    utf8="Кудыкины горы"});
+tostring(t);
+t;
+test_run:cmd("setopt delimiter ''");
 test_run:cmd("clear filter")
-- 
GitLab