diff --git a/src/sentry_value.c b/src/sentry_value.c index 93237979b..2f10e77eb 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -1636,3 +1636,108 @@ sentry_event_value_add_stacktrace(sentry_value_t event, void **ips, size_t len) sentry_value_set_stacktrace(thread, ips, len); sentry_event_add_thread(event, thread); } + +static sentry_value_t +value_from_mpack(mpack_node_t node) +{ + switch (mpack_node_type(node)) { + case mpack_type_nil: + return sentry_value_new_null(); + case mpack_type_bool: + return sentry_value_new_bool(mpack_node_bool(node)); + case mpack_type_int: { + int64_t i64_val = mpack_node_i64(node); + if (i64_val >= INT32_MIN && i64_val <= INT32_MAX) { + return sentry_value_new_int32((int32_t)i64_val); + } else { + return sentry_value_new_int64(i64_val); + } + } + case mpack_type_uint: { + uint64_t u64_val = mpack_node_u64(node); + if (u64_val <= INT32_MAX) { + return sentry_value_new_int32((int32_t)u64_val); + } else if (u64_val <= INT64_MAX) { + return sentry_value_new_int64((int64_t)u64_val); + } else { + return sentry_value_new_uint64(u64_val); + } + } + case mpack_type_float: + case mpack_type_double: + return sentry_value_new_double(mpack_node_double(node)); + case mpack_type_str: { + size_t str_len = mpack_node_strlen(node); + return sentry_value_new_string_n(mpack_node_str(node), str_len); + } + case mpack_type_array: { + size_t arr_len = mpack_node_array_length(node); + sentry_value_t arr = sentry_value_new_list(); + for (size_t i = 0; i < arr_len; i++) { + sentry_value_append( + arr, value_from_mpack(mpack_node_array_at(node, i))); + } + return arr; + } + case mpack_type_map: { + size_t map_len = mpack_node_map_count(node); + sentry_value_t obj = sentry_value_new_object(); + for (size_t i = 0; i < map_len; i++) { + mpack_node_t key_node = mpack_node_map_key_at(node, i); + if (mpack_node_type(key_node) != mpack_type_str) { + continue; // skip non-string keys + } + mpack_node_t val_node = mpack_node_map_value_at(node, i); + size_t key_len = mpack_node_strlen(key_node); + sentry_value_set_by_key_n(obj, mpack_node_str(key_node), key_len, + value_from_mpack(val_node)); + } + return obj; + } + case mpack_type_missing: + case mpack_type_bin: + default: + return sentry_value_new_null(); + } +} + +sentry_value_t +sentry__value_from_msgpack(const char *buf, size_t buf_len) +{ + if (!buf || buf_len == 0) { + return sentry_value_new_null(); + } + + size_t offset = 0; + sentry_value_t result = sentry_value_new_null(); + + while (offset < buf_len) { + mpack_tree_t tree; + mpack_tree_init_data(&tree, buf + offset, buf_len - offset); + mpack_tree_parse(&tree); + + if (mpack_tree_error(&tree) != mpack_ok) { + mpack_tree_destroy(&tree); + break; + } + + size_t size = mpack_tree_size(&tree); + sentry_value_t value = value_from_mpack(mpack_tree_root(&tree)); + mpack_tree_destroy(&tree); + + if (offset == 0 && sentry_value_is_null(result)) { + if (offset + size < buf_len) { + result = sentry_value_new_list(); + sentry_value_append(result, value); + } else { + result = value; + } + } else { + sentry_value_append(result, value); + } + + offset += size; + } + + return result; +} diff --git a/src/sentry_value.h b/src/sentry_value.h index 9bb864cf4..b2928ce52 100644 --- a/src/sentry_value.h +++ b/src/sentry_value.h @@ -115,4 +115,14 @@ void sentry__jsonwriter_write_value( void sentry__value_add_attribute(sentry_value_t attributes, sentry_value_t value, const char *type, const char *name); +/** + * Deserialize a sentry value from msgpack. + * + * If the buffer contains multiple sequential msgpack values (as in flat buffers + * like breadcrumb files), they are automatically wrapped in a list. + * + * The returned value must be released with `sentry_value_decref`. + */ +sentry_value_t sentry__value_from_msgpack(const char *buf, size_t buf_len); + #endif diff --git a/tests/unit/test_value.c b/tests/unit/test_value.c index fd2d012a9..8073105e3 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -4,6 +4,7 @@ #include #include #include +#include SENTRY_TEST(value_null) { @@ -1257,3 +1258,309 @@ SENTRY_TEST(event_with_id) sentry_value_decref(event); } + +SENTRY_TEST(value_from_msgpack_empty) +{ + TEST_CHECK(sentry_value_is_null(sentry__value_from_msgpack(NULL, 0))); + TEST_CHECK(sentry_value_is_null(sentry__value_from_msgpack("", 0))); +} + +SENTRY_TEST(value_from_msgpack_null) +{ + sentry_value_t val = sentry_value_new_null(); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK(sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_NULL); + TEST_CHECK(sentry_value_is_null(deserialized)); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); +} + +SENTRY_TEST(value_from_msgpack_bool) +{ + { + sentry_value_t val = sentry_value_new_bool(true); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK( + sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_BOOL); + TEST_CHECK(sentry_value_is_true(deserialized)); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); + } + { + sentry_value_t val = sentry_value_new_bool(false); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK( + sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_BOOL); + TEST_CHECK(!sentry_value_is_true(deserialized)); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); + } +} + +SENTRY_TEST(value_from_msgpack_int32) +{ + { + sentry_value_t val = sentry_value_new_int32(42); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK( + sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_INT32); + TEST_CHECK(sentry_value_as_int32(deserialized) == 42); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); + } + { + sentry_value_t val = sentry_value_new_int32(-123); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK( + sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_INT32); + TEST_CHECK(sentry_value_as_int32(deserialized) == -123); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); + } +} + +SENTRY_TEST(value_from_msgpack_int64) +{ + { + sentry_value_t val = sentry_value_new_int64((int64_t)INT32_MIN - 1); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK( + sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_INT64); + TEST_CHECK( + sentry_value_as_int64(deserialized) == (int64_t)INT32_MIN - 1); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); + } + { + sentry_value_t val = sentry_value_new_int64(INT64_MIN); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK( + sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_INT64); + TEST_CHECK(sentry_value_as_int64(deserialized) == INT64_MIN); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); + } + { + sentry_value_t val = sentry_value_new_int64((int64_t)INT32_MAX + 1); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK( + sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_INT64); + TEST_CHECK( + sentry_value_as_int64(deserialized) == (int64_t)INT32_MAX + 1); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); + } + { + sentry_value_t val = sentry_value_new_int64(INT64_MAX); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK( + sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_INT64); + TEST_CHECK(sentry_value_as_int64(deserialized) == INT64_MAX); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); + } +} + +SENTRY_TEST(value_from_msgpack_uint64) +{ + sentry_value_t val = sentry_value_new_uint64(UINT64_MAX); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK(sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_UINT64); + TEST_CHECK(sentry_value_as_uint64(deserialized) == UINT64_MAX); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); +} + +SENTRY_TEST(value_from_msgpack_double) +{ + sentry_value_t val = sentry_value_new_double(3.14159); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK(sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_DOUBLE); + double d = sentry_value_as_double(deserialized); + TEST_CHECK(d > 3.14 && d < 3.15); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); +} + +SENTRY_TEST(value_from_msgpack_string) +{ + sentry_value_t val = sentry_value_new_string("ล‘รกโ€ฆโ€“๐Ÿคฎ๐Ÿš€ยฟ ํ•œ๊ธ€ ํ…Œ์ŠคํŠธ \a\v"); + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK(sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_STRING); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(deserialized), "ล‘รกโ€ฆโ€“๐Ÿคฎ๐Ÿš€ยฟ ํ•œ๊ธ€ ํ…Œ์ŠคํŠธ \a\v"); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); +} + +SENTRY_TEST(value_from_msgpack_list) +{ + sentry_value_t inner = sentry_value_new_list(); + sentry_value_append(inner, sentry_value_new_int32(1)); + sentry_value_append(inner, sentry_value_new_int32(2)); + + sentry_value_t val = sentry_value_new_list(); + sentry_value_append(val, inner); + sentry_value_append(val, sentry_value_new_string("outer")); + + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK(sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_LIST); + TEST_CHECK(sentry_value_get_length(deserialized) == 2); + + sentry_value_t nested = sentry_value_get_by_index(deserialized, 0); + TEST_CHECK(sentry_value_get_type(nested) == SENTRY_VALUE_TYPE_LIST); + TEST_CHECK(sentry_value_get_length(nested) == 2); + + sentry_value_t nested_elem0 = sentry_value_get_by_index(nested, 0); + TEST_CHECK(sentry_value_as_int32(nested_elem0) == 1); + + sentry_value_t nested_elem1 = sentry_value_get_by_index(nested, 1); + TEST_CHECK(sentry_value_as_int32(nested_elem1) == 2); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); +} + +SENTRY_TEST(value_from_msgpack_object) +{ + sentry_value_t inner = sentry_value_new_object(); + sentry_value_set_by_key(inner, "x", sentry_value_new_int32(10)); + sentry_value_set_by_key(inner, "y", sentry_value_new_int32(20)); + + sentry_value_t val = sentry_value_new_object(); + sentry_value_set_by_key(val, "position", inner); + sentry_value_set_by_key(val, "label", sentry_value_new_string("point")); + + size_t size = 0; + char *buf = sentry_value_to_msgpack(val, &size); + + sentry_value_t deserialized = sentry__value_from_msgpack(buf, size); + TEST_CHECK(sentry_value_get_type(deserialized) == SENTRY_VALUE_TYPE_OBJECT); + + sentry_value_t position = sentry_value_get_by_key(deserialized, "position"); + TEST_CHECK(sentry_value_get_type(position) == SENTRY_VALUE_TYPE_OBJECT); + + sentry_value_t x = sentry_value_get_by_key(position, "x"); + TEST_CHECK(sentry_value_as_int32(x) == 10); + + sentry_value_t y = sentry_value_get_by_key(position, "y"); + TEST_CHECK(sentry_value_as_int32(y) == 20); + + sentry_free(buf); + sentry_value_decref(val); + sentry_value_decref(deserialized); +} + +SENTRY_TEST(value_from_msgpack_flat_buffer) +{ + sentry_value_t val1 = sentry_value_new_list(); + sentry_value_append(val1, sentry_value_new_int32(1)); + sentry_value_append(val1, sentry_value_new_int32(2)); + size_t size1 = 0; + char *buf1 = sentry_value_to_msgpack(val1, &size1); + + sentry_value_t val2 = sentry_value_new_int32(2); + size_t size2 = 0; + char *buf2 = sentry_value_to_msgpack(val2, &size2); + + sentry_value_t val3 = sentry_value_new_string("three"); + size_t size3 = 0; + char *buf3 = sentry_value_to_msgpack(val3, &size3); + + char combined[256]; + size_t combined_size = 0; + memcpy(combined + combined_size, buf1, size1); + combined_size += size1; + memcpy(combined + combined_size, buf2, size2); + combined_size += size2; + memcpy(combined + combined_size, buf3, size3); + combined_size += size3; + + sentry_value_t result = sentry__value_from_msgpack(combined, combined_size); + TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST); + TEST_CHECK(sentry_value_get_length(result) == 3); + + sentry_value_t elem0 = sentry_value_get_by_index(result, 0); + TEST_CHECK(sentry_value_get_type(elem0) == SENTRY_VALUE_TYPE_LIST); + TEST_CHECK(sentry_value_get_length(elem0) == 2); + TEST_CHECK(sentry_value_as_int32(sentry_value_get_by_index(elem0, 0)) == 1); + TEST_CHECK(sentry_value_as_int32(sentry_value_get_by_index(elem0, 1)) == 2); + + sentry_value_t elem1 = sentry_value_get_by_index(result, 1); + TEST_CHECK(sentry_value_as_int32(elem1) == 2); + + sentry_value_t elem2 = sentry_value_get_by_index(result, 2); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(elem2), "three"); + + sentry_free(buf1); + sentry_free(buf2); + sentry_free(buf3); + sentry_value_decref(val1); + sentry_value_decref(val2); + sentry_value_decref(val3); + sentry_value_decref(result); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 945f9274e..4941e29ab 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -207,6 +207,17 @@ XX(value_attribute) XX(value_bool) XX(value_double) XX(value_freezing) +XX(value_from_msgpack_bool) +XX(value_from_msgpack_double) +XX(value_from_msgpack_empty) +XX(value_from_msgpack_flat_buffer) +XX(value_from_msgpack_int32) +XX(value_from_msgpack_int64) +XX(value_from_msgpack_list) +XX(value_from_msgpack_null) +XX(value_from_msgpack_object) +XX(value_from_msgpack_string) +XX(value_from_msgpack_uint64) XX(value_get_by_null_key) XX(value_int32) XX(value_int64)