[Tarantool-patches] [PATCH msgpuck 2/2] Make MP_EXT mp_snprint() and mp_fprint() customizable

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Tue May 12 02:46:02 MSK 2020


MP_EXT in these functions was skipped and printed as 'undefined'.
In Tarantool there are already 3 extensions - decimals, UUID,
errors. It is time to make them being nicely printed too.

The patch makes it possible via virtual MP_EXT serializer, which
by default does the same as before. But in Tarantool is
substituted with correct printer for the mentioned extensions.

Part of https://github.com/tarantool/tarantool/issues/4719
---
 msgpuck.c      | 31 ++++++++++++++++---
 msgpuck.h      | 50 ++++++++++++++++++++++++++++++
 test/msgpuck.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 159 insertions(+), 5 deletions(-)

diff --git a/msgpuck.c b/msgpuck.c
index 7ea86e8..1ee234b 100644
--- a/msgpuck.c
+++ b/msgpuck.c
@@ -33,6 +33,26 @@
 #define MP_LIBRARY 1
 #include "msgpuck.h"
 
+int
+mp_fprint_ext_default(FILE *file, const char **data, int depth)
+{
+	(void) depth;
+	mp_next(data);
+	return fprintf(file, "undefined");
+}
+
+int
+mp_snprint_ext_default(char *buf, int size, const char **data, int depth)
+{
+	(void) depth;
+	mp_next(data);
+	return snprintf(buf, size, "undefined");
+}
+
+mp_fprint_ext_f mp_fprint_ext = mp_fprint_ext_default;
+
+mp_snprint_ext_f mp_snprint_ext = mp_snprint_ext_default;
+
 size_t
 mp_vformat(char *data, size_t data_size, const char *format, va_list vl)
 {
@@ -316,8 +336,7 @@ mp_format(char *data, size_t data_size, const char *format, ...)
 		PRINTF("%lg", mp_decode_double(data));				\
 		break;								\
 	case MP_EXT:								\
-		mp_next(data);							\
-		PRINTF("undefined");						\
+		PRINT_EXT(data);						\
 		break;								\
 	default:								\
 		mp_unreachable();						\
@@ -325,7 +344,7 @@ mp_format(char *data, size_t data_size, const char *format, ...)
 	}									\
 }
 
-static int
+int
 mp_fprint_recursion(FILE *file, const char **data, int depth)
 {
 	int total_bytes = 0;
@@ -335,12 +354,14 @@ mp_fprint_recursion(FILE *file, const char **data, int depth)
 		return -1;							\
 	total_bytes += bytes;							\
 } while (0)
+#define PRINT_EXT(...) HANDLE(mp_fprint_ext, __VA_ARGS__, depth)
 #define PRINT(...) HANDLE(fprintf, __VA_ARGS__)
 #define SELF(...) HANDLE(mp_fprint_recursion, __VA_ARGS__, depth)
 MP_PRINT(SELF, PRINT)
 #undef HANDLE
 #undef SELF
 #undef PRINT
+#undef PRINT_EXT
 	return total_bytes;
 }
 
@@ -353,7 +374,7 @@ mp_fprint(FILE *file, const char *data)
 	return res;
 }
 
-static int
+int
 mp_snprint_recursion(char *buf, int size, const char **data, int depth)
 {
 	int total_bytes = 0;
@@ -371,12 +392,14 @@ mp_snprint_recursion(char *buf, int size, const char **data, int depth)
 		size = 0;							\
 	}									\
 } while (0)
+#define PRINT_EXT(...) HANDLE(mp_snprint_ext, __VA_ARGS__, depth)
 #define PRINT(...) HANDLE(snprintf, __VA_ARGS__)
 #define SELF(...) HANDLE(mp_snprint_recursion, __VA_ARGS__, depth)
 MP_PRINT(SELF, PRINT)
 #undef HANDLE
 #undef SELF
 #undef PRINT
+#undef PRINT_EXT
 	return total_bytes;
 }
 #undef MP_PRINT
diff --git a/msgpuck.h b/msgpuck.h
index 5dfbbd9..91c7693 100644
--- a/msgpuck.h
+++ b/msgpuck.h
@@ -980,6 +980,30 @@ mp_vformat(char *data, size_t data_size, const char *format, va_list args);
 int
 mp_fprint(FILE *file, const char *data);
 
+/**
+ * \brief Print MsgPack data to \a file using JSON-like format.
+ * Works exactly like \sa mp_fprint(), but allows to specify max
+ * depth, and changes \a data parameter. Intended to be used for
+ * MsgPack serialization recursion.
+ */
+int
+mp_fprint_recursion(FILE *file, const char **data, int depth);
+
+typedef int (*mp_fprint_ext_f)(FILE *file, const char **data, int depth);
+
+/**
+ * \brief Function called when need to serialize MP_EXT into a
+ * file.
+ */
+extern mp_fprint_ext_f mp_fprint_ext;
+
+/**
+ * \brief Default MP_EXT serializer into a file. Skips the object,
+ * ignores all the other arguments, and writes 'undefined'.
+ */
+int
+mp_fprint_ext_default(FILE *file, const char **data, int depth);
+
 /**
  * \brief format MsgPack data to \a buf using JSON-like format.
  * \sa mp_fprint()
@@ -997,6 +1021,32 @@ mp_fprint(FILE *file, const char *data);
 int
 mp_snprint(char *buf, int size, const char *data);
 
+/**
+ * \brief Format MsgPack data to \a buf using JSON-like format.
+ * Works exactly like \sa mp_snprint(), but allows to specify max
+ * depth, and changes \a data parameter. Intended to be used for
+ * MsgPack serialization recursion.
+ */
+int
+mp_snprint_recursion(char *buf, int size, const char **data, int depth);
+
+typedef int (*mp_snprint_ext_f)(char *buf, int size, const char **data,
+				int depth);
+
+/**
+ * \brief Function called when need to serialize MP_EXT into a
+ * string.
+ */
+extern mp_snprint_ext_f mp_snprint_ext;
+
+/**
+ * \brief Default MP_EXT serializer into a string. Skips the
+ * object, ignores all the other arguments, and prints
+ * 'undefined'.
+ */
+int
+mp_snprint_ext_default(char *buf, int size, const char **data, int depth);
+
 /**
  * \brief Check that \a cur buffer has enough bytes to decode a string header
  * \param cur buffer
diff --git a/test/msgpuck.c b/test/msgpuck.c
index ecc2b26..1d8c726 100644
--- a/test/msgpuck.c
+++ b/test/msgpuck.c
@@ -969,6 +969,86 @@ test_mp_print()
 	return check_plan();
 }
 
+enum mp_ext_test_type {
+	MP_EXT_TEST_PLAIN,
+	MP_EXT_TEST_MSGPACK,
+};
+
+static int
+mp_fprint_ext_test(FILE *file, const char **data, int depth)
+{
+	int8_t type;
+	uint32_t len = mp_decode_extl(data, &type);
+	const char *ext = *data;
+	*data += len;
+	switch(type) {
+	case MP_EXT_TEST_PLAIN:
+		return fprintf(file, "%.*s", len, ext);
+	case MP_EXT_TEST_MSGPACK:
+		return mp_fprint_recursion(file, &ext, depth);
+	}
+	return fprintf(file, "undefined");
+}
+
+static int
+mp_snprint_ext_test(char *buf, int size, const char **data, int depth)
+{
+	int8_t type;
+	uint32_t len = mp_decode_extl(data, &type);
+	const char *ext = *data;
+	*data += len;
+	switch(type) {
+	case MP_EXT_TEST_PLAIN:
+		return snprintf(buf, size, "%.*s", len, ext);
+	case MP_EXT_TEST_MSGPACK:
+		return mp_snprint_recursion(buf, size, &ext, depth);
+	}
+	return snprintf(buf, size, "undefined");
+}
+
+static int
+test_mp_print_ext(void)
+{
+	plan(5);
+	header();
+	mp_snprint_ext = mp_snprint_ext_test;
+	mp_fprint_ext = mp_fprint_ext_test;
+
+	char *pos = buf;
+	const char *plain = "plain-str";
+	size_t plain_len = strlen(plain);
+	pos = mp_encode_array(pos, 4);
+	pos = mp_encode_uint(pos, 100);
+	pos = mp_encode_ext(pos, MP_EXT_TEST_PLAIN, plain, plain_len);
+	pos = mp_encode_extl(pos, MP_EXT_TEST_MSGPACK,
+			     mp_sizeof_str(plain_len));
+	pos = mp_encode_str(pos, plain, plain_len);
+	pos = mp_encode_uint(pos, 200);
+
+	int size = mp_snprint(NULL, 0, buf);
+	int real_size = mp_snprint(str, sizeof(str), buf);
+	is(size, real_size, "mp_snrpint size match");
+	const char *expected = "[100, plain-str, \"plain-str\", 200]";
+	is(strcmp(str, expected), 0, "str is correct");
+
+	FILE *tmpf = tmpfile();
+	if (tmpf == NULL)
+		abort();
+	real_size = mp_fprint(tmpf, buf);
+	is(size, real_size, "mp_fprint size match");
+	rewind(tmpf);
+	real_size = (int) fread(str, 1, sizeof(str), tmpf);
+	is(real_size, size, "mp_fprint written correct number of bytes");
+	str[real_size] = 0;
+	is(strcmp(str, expected), 0, "str is correct");
+	fclose(tmpf);
+
+	mp_snprint_ext = mp_snprint_ext_default;
+	mp_fprint_ext = mp_fprint_ext_default;
+	footer();
+	return check_plan();
+}
+
 int
 test_mp_check()
 {
@@ -1226,7 +1306,7 @@ test_overflow()
 
 int main()
 {
-	plan(22);
+	plan(23);
 	test_uints();
 	test_ints();
 	test_bools();
@@ -1246,6 +1326,7 @@ int main()
 	test_compare_uints();
 	test_format();
 	test_mp_print();
+	test_mp_print_ext();
 	test_mp_check();
 	test_numbers();
 	test_overflow();
-- 
2.21.1 (Apple Git-122.3)



More information about the Tarantool-patches mailing list