diff --git a/msgpuck.h b/msgpuck.h index f7546df5..db282503 100644 --- a/msgpuck.h +++ b/msgpuck.h @@ -866,15 +866,33 @@ MP_PROTO size_t mp_vformat(char *data, size_t data_size, const char *format, va_list args); /** - * \brief print to \a file msgpacked data in JSON format. - * MP_EXT is printed as "EXT" only + * \brief print MsgPack data \a file using JSON-like format. + * MP_EXT is printed as "undefined" * \param file - pointer to file (or NULL for stdout) * \param data - pointer to buffer containing msgpack object - * \retval 0 - success - * \retval -1 - wrong msgpack + * \retval >=0 - the number of bytes printed + * \retval -1 - error + * \sa fprintf() */ MP_PROTO int -mp_fprint(FILE* file, const char *data); +mp_fprint(FILE *file, const char *data); + +/** + * \brief format MsgPack data to \a buf using JSON-like format. + * \sa mp_fprint() + * \param buf - buffer to use + * \param size - buffer size. This function write at most size bytes + * (including the terminating null byte ('\0'). + * \param data - pointer to buffer containing msgpack object + * \retval =size - the number of characters (excluding the null byte), + * which would have been written to the final string if + * enough space had been available. + * \retval -1 - error + * \sa snprintf() + */ +MP_PROTO int +mp_snprint(char *buf, int size, const char *data); /** * \brief Check that \a cur buffer has enough bytes to decode a string header @@ -2163,93 +2181,106 @@ mp_format(char *data, size_t data_size, const char *format, ...) return res; } +#define MP_PRINT(SELF, PRINTF) \ +{ \ + switch (mp_typeof(**data)) { \ + case MP_NIL: \ + mp_decode_nil(data); \ + PRINTF("null"); \ + break; \ + case MP_UINT: \ + PRINTF("%llu", (unsigned long long) mp_decode_uint(data)); \ + break; \ + case MP_INT: \ + PRINTF("%lld", (long long) mp_decode_int(data)); \ + break; \ + case MP_STR: \ + case MP_BIN: \ + { \ + uint32_t len = mp_typeof(**data) == MP_STR ? \ + mp_decode_strl(data) : mp_decode_binl(data); \ + PRINTF("\""); \ + const char *s; \ + for (s = *data; s < *data + len; s++) { \ + unsigned char c = (unsigned char ) *s; \ + if (c < 128 && mp_char2escape[c] != NULL) { \ + /* Escape character */ \ + PRINTF("%s", mp_char2escape[c]); \ + } else { \ + PRINTF("%c", c); \ + } \ + } \ + PRINTF("\""); \ + *data += len; \ + break; \ + } \ + case MP_ARRAY: \ + { \ + uint32_t count = mp_decode_array(data); \ + PRINTF("["); \ + uint32_t i; \ + for (i = 0; i < count; i++) { \ + if (i) \ + PRINTF(", "); \ + SELF(data); \ + } \ + PRINTF("]"); \ + break; \ + } \ + case MP_MAP: \ + { \ + uint32_t count = mp_decode_map(data); \ + PRINTF("{"); \ + uint32_t i; \ + for (i = 0; i < count; i++) { \ + if (i) \ + PRINTF(", "); \ + SELF(data); \ + PRINTF(": "); \ + SELF(data); \ + } \ + PRINTF("}"); \ + break; \ + } \ + case MP_BOOL: \ + PRINTF(mp_decode_bool(data) ? "true" : "false"); \ + break; \ + case MP_FLOAT: \ + PRINTF("%g", mp_decode_float(data)); \ + break; \ + case MP_DOUBLE: \ + PRINTF("%lg", mp_decode_double(data)); \ + break; \ + case MP_EXT: \ + mp_next(data); \ + PRINTF("undefined"); \ + break; \ + default: \ + mp_unreachable(); \ + return -1; \ + } \ +} + MP_PROTO int mp_fprint_internal(FILE *file, const char **data); MP_IMPL int mp_fprint_internal(FILE *file, const char **data) { -#define _CHECK_RC(exp) do { if (mp_unlikely((exp) < 0)) return -1; } while(0) - switch (mp_typeof(**data)) { - case MP_NIL: - mp_decode_nil(data); - _CHECK_RC(fputs("null", file)); - break; - case MP_UINT: - _CHECK_RC(fprintf(file, "%llu", (unsigned long long) - mp_decode_uint(data))); - break; - case MP_INT: - _CHECK_RC(fprintf(file, "%lld", (long long) - mp_decode_int(data))); - break; - case MP_STR: - case MP_BIN: - { - uint32_t len = mp_typeof(**data) == MP_STR ? - mp_decode_strl(data) : mp_decode_binl(data); - _CHECK_RC(fputc('"', file)); - const char *s; - for (s = *data; s < *data + len; s++) { - unsigned char c = (unsigned char ) *s; - if (c < 128 && mp_char2escape[c] != NULL) { - /* Escape character */ - _CHECK_RC(fputs(mp_char2escape[c], file)); - } else { - _CHECK_RC(fputc(c, file)); - } - } - _CHECK_RC(fputc('"', file)); - *data += len; - break; - } - case MP_ARRAY: - { - uint32_t size = mp_decode_array(data); - _CHECK_RC(fputc('[', file)); - uint32_t i; - for (i = 0; i < size; i++) { - if (i) - _CHECK_RC(fputs(", ", file)); - _CHECK_RC(mp_fprint_internal(file, data)); - } - _CHECK_RC(fputc(']', file)); - break; - } - case MP_MAP: - { - uint32_t size = mp_decode_map(data); - _CHECK_RC(fputc('{', file)); - uint32_t i; - for (i = 0; i < size; i++) { - if (i) - _CHECK_RC(fprintf(file, ", ")); - _CHECK_RC(mp_fprint_internal(file, data)); - _CHECK_RC(fputs(": ", file)); - _CHECK_RC(mp_fprint_internal(file, data)); - } - _CHECK_RC(fputc('}', file)); - break; - } - case MP_BOOL: - _CHECK_RC(fputs(mp_decode_bool(data) ? "true" : "false", file)); - break; - case MP_FLOAT: - _CHECK_RC(fprintf(file, "%g", mp_decode_float(data))); - break; - case MP_DOUBLE: - _CHECK_RC(fprintf(file, "%lg", mp_decode_double(data))); - break; - case MP_EXT: - mp_next(data); - _CHECK_RC(fputs("undefined", file)); - break; - default: - mp_unreachable(); - return -1; - } - return 0; -#undef _CHECK_RC + int total_bytes = 0; +#define HANDLE(FUN, ...) do { \ + int bytes = FUN(file, __VA_ARGS__); \ + if (mp_unlikely(bytes < 0)) \ + return -1; \ + total_bytes += bytes; \ +} while (0) +#define PRINT(...) HANDLE(fprintf, __VA_ARGS__) +#define SELF(...) HANDLE(mp_fprint_internal, __VA_ARGS__) +MP_PRINT(SELF, PRINT) +#undef HANDLE +#undef SELF +#undef PRINT + return total_bytes; } MP_IMPL int @@ -2261,6 +2292,43 @@ mp_fprint(FILE *file, const char *data) return res; } +MP_PROTO int +mp_snprint_internal(char *buf, int size, const char **data); + +MP_IMPL int +mp_snprint_internal(char *buf, int size, const char **data) +{ + int total_bytes = 0; +#define HANDLE(FUN, ...) do { \ + int bytes = FUN(buf, size, __VA_ARGS__); \ + if (mp_unlikely(bytes < 0)) \ + return -1; \ + total_bytes += bytes; \ + if (bytes < size) { \ + buf += bytes; \ + size -= bytes; \ + } else { \ + /* Calculate the number of bytes needed */ \ + buf = NULL; \ + size = 0; \ + } \ +} while (0) +#define PRINT(...) HANDLE(snprintf, __VA_ARGS__) +#define SELF(...) HANDLE(mp_snprint_internal, __VA_ARGS__) +MP_PRINT(SELF, PRINT) +#undef HANDLE +#undef SELF +#undef PRINT + return total_bytes; +} + +MP_IMPL int +mp_snprint(char *buf, int size, const char *data) +{ + return mp_snprint_internal(buf, size, &data); +} + +#undef MP_PRINT /** \endcond */ /* diff --git a/test/msgpuck.c b/test/msgpuck.c index a8c40e5c..ec8d99a5 100644 --- a/test/msgpuck.c +++ b/test/msgpuck.c @@ -723,12 +723,11 @@ test_format(void) int test_mp_print() { - plan(1); + plan(10); header(); - char data[512]; - - char *d = data; + char msgpack[128]; + char *d = msgpack; d = mp_encode_array(d, 6); d = mp_encode_int(d, -5); d = mp_encode_uint(d, 42); @@ -751,22 +750,53 @@ test_mp_print() *d++ = 0; char bin[] = "\x12test\x34\b\t\n\"bla\\-bla\"\f\r"; d = mp_encode_bin(d, bin, sizeof(bin)); + assert(d <= msgpack + sizeof(msgpack)); const char *expected = "[-5, 42, \"kill bill\", " "{\"bool true\": true, \"bool false\": false, \"null\": null, " "\"float\": 3.14, \"double\": 3.14, 100: 500}, undefined, " "\"\\u0012test4\\b\\t\\n\\\"bla\\\\-bla\\\"\\f\\r\\u0000\"]"; + int esize = strlen(expected); + + char result[256]; + + int fsize = mp_snprint(result, sizeof(result), msgpack); + ok(fsize == esize, "mp_snprint return value"); + ok(strcmp(result, expected) == 0, "mp_snprint result"); + + fsize = mp_snprint(NULL, 0, msgpack); + ok(fsize == esize, "mp_snprint limit = 0"); + + fsize = mp_snprint(result, 1, msgpack); + ok(fsize == esize && result[0] == '\0', "mp_snprint limit = 1"); + + fsize = mp_snprint(result, 2, msgpack); + ok(fsize == esize && result[1] == '\0', "mp_snprint limit = 2"); + + fsize = mp_snprint(result, esize, msgpack); + ok(fsize == esize && result[esize - 1] == '\0', + "mp_snprint limit = expected"); + + fsize = mp_snprint(result, esize + 1, msgpack); + ok(fsize == esize && result[esize] == '\0', + "mp_snprint limit = expected + 1"); + FILE *tmpf = tmpfile(); if (tmpf != NULL) { - mp_fprint(tmpf, data); + int fsize = mp_fprint(tmpf, msgpack); + ok(fsize == esize, "mp_fprint return value"); (void) rewind(tmpf); - memset(data, 0, sizeof(data)); - if (fgets(data, sizeof(data), tmpf) != NULL) { - ok(strcmp(data, expected) == 0, "identical"); - } + int rsize = fread(result, 1, sizeof(result), tmpf); + ok(rsize == esize && memcmp(result, expected, esize) == 0, + "mp_fprint result"); fclose(tmpf); } + + /* stdin is read-only */ + int rc = mp_fprint(stdin, msgpack); + is(rc, -1, "mp_fprint I/O error"); + footer(); return check_plan(); } @@ -774,7 +804,6 @@ test_mp_print() int main() { plan(17); - test_uints(); test_ints(); test_bools();