[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