| /* |
| * Copyright 2011 Collabora Ltd. |
| * |
| * SPDX-License-Identifier: LGPL-2.1-or-later |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * See the included COPYING file for more information. |
| */ |
| |
| #undef G_DISABLE_ASSERT |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "glib.h" |
| |
| /* Keep in sync with glib/gbytes.c */ |
| struct _GBytes |
| { |
| gconstpointer data; |
| gsize size; |
| gint ref_count; |
| GDestroyNotify free_func; |
| gpointer user_data; |
| }; |
| |
| static const gchar *NYAN = "nyannyan"; |
| static const gsize N_NYAN = 8; |
| |
| static void |
| test_new (void) |
| { |
| const gchar *data; |
| GBytes *bytes; |
| gsize size; |
| |
| data = "test"; |
| bytes = g_bytes_new (data, 4); |
| g_assert_nonnull (bytes); |
| g_assert_true (g_bytes_get_data (bytes, &size) != data); |
| g_assert_cmpuint (size, ==, 4); |
| g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4); |
| g_assert_cmpmem (data, 4, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)); |
| |
| g_bytes_unref (bytes); |
| } |
| |
| static void |
| test_new_take (void) |
| { |
| gchar *data; |
| GBytes *bytes; |
| gsize size; |
| |
| data = g_strdup ("test"); |
| bytes = g_bytes_new_take (data, 4); |
| g_assert_nonnull (bytes); |
| g_assert_true (g_bytes_get_data (bytes, &size) == data); |
| g_assert_cmpuint (size, ==, 4); |
| g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4); |
| |
| g_bytes_unref (bytes); |
| } |
| |
| static void |
| test_new_static (void) |
| { |
| const gchar *data; |
| GBytes *bytes; |
| gsize size; |
| |
| data = "test"; |
| bytes = g_bytes_new_static (data, 4); |
| g_assert_nonnull (bytes); |
| g_assert_true (g_bytes_get_data (bytes, &size) == data); |
| g_assert_cmpuint (size, ==, 4); |
| g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4); |
| |
| g_bytes_unref (bytes); |
| } |
| |
| static void |
| test_new_from_bytes (void) |
| { |
| const gchar *data = "smile and wave"; |
| GBytes *bytes; |
| GBytes *sub; |
| |
| bytes = g_bytes_new (data, 14); |
| sub = g_bytes_new_from_bytes (bytes, 10, 4); |
| |
| g_assert_nonnull (sub); |
| g_assert_true (g_bytes_get_data (sub, NULL) == ((gchar *)g_bytes_get_data (bytes, NULL)) + 10); |
| g_bytes_unref (bytes); |
| |
| g_assert_cmpmem (g_bytes_get_data (sub, NULL), g_bytes_get_size (sub), "wave", 4); |
| g_bytes_unref (sub); |
| } |
| |
| /* Verify that creating slices of GBytes reference the top-most bytes |
| * at the correct offset. Ensure that intermediate GBytes are not referenced. |
| */ |
| static void |
| test_new_from_bytes_slice (void) |
| { |
| GBytes *bytes = g_bytes_new_static ("Some stupid data", strlen ("Some stupid data") + 1); |
| GBytes *bytes1 = g_bytes_new_from_bytes (bytes, 4, 13); |
| GBytes *bytes2 = g_bytes_new_from_bytes (bytes1, 1, 12); |
| GBytes *bytes3 = g_bytes_new_from_bytes (bytes2, 0, 6); |
| |
| g_assert_cmpint (bytes->ref_count, ==, 4); |
| g_assert_cmpint (bytes1->ref_count, ==, 1); |
| g_assert_cmpint (bytes2->ref_count, ==, 1); |
| g_assert_cmpint (bytes3->ref_count, ==, 1); |
| |
| g_assert_null (bytes->user_data); |
| g_assert_true (bytes1->user_data == bytes); |
| g_assert_true (bytes2->user_data == bytes); |
| g_assert_true (bytes3->user_data == bytes); |
| |
| g_assert_cmpint (17, ==, g_bytes_get_size (bytes)); |
| g_assert_cmpint (13, ==, g_bytes_get_size (bytes1)); |
| g_assert_cmpint (12, ==, g_bytes_get_size (bytes2)); |
| g_assert_cmpint (6, ==, g_bytes_get_size (bytes3)); |
| |
| g_assert_cmpint (0, ==, strncmp ("Some stupid data", (gchar *)bytes->data, 17)); |
| g_assert_cmpint (0, ==, strncmp (" stupid data", (gchar *)bytes1->data, 13)); |
| g_assert_cmpint (0, ==, strncmp ("stupid data", (gchar *)bytes2->data, 12)); |
| g_assert_cmpint (0, ==, strncmp ("stupid", (gchar *)bytes3->data, 6)); |
| |
| g_bytes_unref (bytes); |
| g_bytes_unref (bytes1); |
| g_bytes_unref (bytes2); |
| g_bytes_unref (bytes3); |
| } |
| |
| /* Ensure that referencing an entire GBytes just returns the same bytes |
| * instance (with incremented reference count) instead of a new instance. |
| */ |
| static void |
| test_new_from_bytes_shared_ref (void) |
| { |
| GBytes *bytes = g_bytes_new_static ("Some data", strlen ("Some data") + 1); |
| GBytes *other = g_bytes_new_from_bytes (bytes, 0, g_bytes_get_size (bytes)); |
| |
| g_assert_true (bytes == other); |
| g_assert_cmpint (bytes->ref_count, ==, 2); |
| |
| g_bytes_unref (bytes); |
| g_bytes_unref (other); |
| } |
| |
| static void |
| on_destroy_increment (gpointer data) |
| { |
| gint *count = data; |
| g_assert_nonnull (count); |
| (*count)++; |
| } |
| |
| static void |
| test_new_with_free_func (void) |
| { |
| GBytes *bytes; |
| gchar *data; |
| gint count = 0; |
| gsize size; |
| |
| data = "test"; |
| bytes = g_bytes_new_with_free_func (data, 4, on_destroy_increment, &count); |
| g_assert_nonnull (bytes); |
| g_assert_cmpint (count, ==, 0); |
| g_assert_true (g_bytes_get_data (bytes, &size) == data); |
| g_assert_cmpuint (size, ==, 4); |
| g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4); |
| |
| g_bytes_unref (bytes); |
| g_assert_cmpuint (count, ==, 1); |
| } |
| |
| static void |
| test_hash (void) |
| { |
| GBytes *bytes1; |
| GBytes *bytes2; |
| guint hash1; |
| guint hash2; |
| |
| bytes1 = g_bytes_new ("blah", 4); |
| bytes2 = g_bytes_new ("blah", 4); |
| |
| hash1 = g_bytes_hash (bytes1); |
| hash2 = g_bytes_hash (bytes2); |
| g_assert_cmpuint (hash1, ==, hash2); |
| |
| g_bytes_unref (bytes1); |
| g_bytes_unref (bytes2); |
| } |
| |
| static void |
| test_equal (void) |
| { |
| GBytes *bytes; |
| GBytes *bytes2; |
| |
| bytes = g_bytes_new ("blah", 4); |
| |
| bytes2 = g_bytes_new ("blah", 4); |
| g_assert_true (g_bytes_equal (bytes, bytes2)); |
| g_assert_true (g_bytes_equal (bytes2, bytes)); |
| g_bytes_unref (bytes2); |
| |
| bytes2 = g_bytes_new ("bla", 3); |
| g_assert_false (g_bytes_equal (bytes, bytes2)); |
| g_assert_false (g_bytes_equal (bytes2, bytes)); |
| g_bytes_unref (bytes2); |
| |
| bytes2 = g_bytes_new ("true", 4); |
| g_assert_false (g_bytes_equal (bytes, bytes2)); |
| g_assert_false (g_bytes_equal (bytes2, bytes)); |
| g_bytes_unref (bytes2); |
| |
| g_bytes_unref (bytes); |
| } |
| |
| static void |
| test_compare (void) |
| { |
| GBytes *bytes; |
| GBytes *bytes2; |
| |
| bytes = g_bytes_new ("blah", 4); |
| |
| bytes2 = g_bytes_new ("blah", 4); |
| g_assert_cmpint (g_bytes_compare (bytes, bytes2), ==, 0); |
| g_bytes_unref (bytes2); |
| |
| bytes2 = g_bytes_new ("bla", 3); |
| g_assert_cmpint (g_bytes_compare (bytes, bytes2), >, 0); |
| g_bytes_unref (bytes2); |
| |
| bytes2 = g_bytes_new ("abcd", 4); |
| g_assert_cmpint (g_bytes_compare (bytes, bytes2), >, 0); |
| g_bytes_unref (bytes2); |
| |
| bytes2 = g_bytes_new ("blahblah", 8); |
| g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0); |
| g_bytes_unref (bytes2); |
| |
| bytes2 = g_bytes_new ("zyx", 3); |
| g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0); |
| g_bytes_unref (bytes2); |
| |
| bytes2 = g_bytes_new ("zyxw", 4); |
| g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0); |
| g_bytes_unref (bytes2); |
| |
| g_bytes_unref (bytes); |
| } |
| |
| static void |
| test_to_data_transferred (void) |
| { |
| gconstpointer memory; |
| gpointer data; |
| gsize size; |
| GBytes *bytes; |
| |
| /* Memory transferred: one reference, and allocated with g_malloc */ |
| bytes = g_bytes_new (NYAN, N_NYAN); |
| memory = g_bytes_get_data (bytes, NULL); |
| data = g_bytes_unref_to_data (bytes, &size); |
| g_assert_true (data == memory); |
| g_assert_cmpmem (data, size, NYAN, N_NYAN); |
| g_free (data); |
| } |
| |
| static void |
| test_to_data_two_refs (void) |
| { |
| gconstpointer memory; |
| gpointer data; |
| gsize size; |
| GBytes *bytes; |
| |
| /* Memory copied: two references */ |
| bytes = g_bytes_new (NYAN, N_NYAN); |
| bytes = g_bytes_ref (bytes); |
| memory = g_bytes_get_data (bytes, NULL); |
| data = g_bytes_unref_to_data (bytes, &size); |
| g_assert_true (data != memory); |
| g_assert_cmpmem (data, size, NYAN, N_NYAN); |
| g_free (data); |
| g_assert_true (g_bytes_get_data (bytes, &size) == memory); |
| g_assert_cmpuint (size, ==, N_NYAN); |
| g_assert_cmpuint (g_bytes_get_size (bytes), ==, N_NYAN); |
| g_bytes_unref (bytes); |
| } |
| |
| static void |
| test_to_data_non_malloc (void) |
| { |
| gpointer data; |
| gsize size; |
| GBytes *bytes; |
| |
| /* Memory copied: non malloc memory */ |
| bytes = g_bytes_new_static (NYAN, N_NYAN); |
| g_assert_true (g_bytes_get_data (bytes, NULL) == NYAN); |
| data = g_bytes_unref_to_data (bytes, &size); |
| g_assert_true (data != (gpointer)NYAN); |
| g_assert_cmpmem (data, size, NYAN, N_NYAN); |
| g_free (data); |
| } |
| |
| static void |
| test_to_data_different_free_func (void) |
| { |
| gpointer data; |
| gsize size; |
| GBytes *bytes; |
| gchar *sentinel = g_strdup ("hello"); |
| |
| /* Memory copied: free func and user_data don’t point to the bytes data */ |
| bytes = g_bytes_new_with_free_func (NYAN, N_NYAN, g_free, sentinel); |
| g_assert_true (g_bytes_get_data (bytes, NULL) == NYAN); |
| |
| data = g_bytes_unref_to_data (bytes, &size); |
| g_assert_true (data != (gpointer)NYAN); |
| g_assert_cmpmem (data, size, NYAN, N_NYAN); |
| g_free (data); |
| |
| /* @sentinel should not be leaked; testing that requires this test to be run |
| * under valgrind. We can’t use a custom free func to check it isn’t leaked, |
| * as the point of this test is to hit a condition in `try_steal_and_unref()` |
| * which is short-circuited if the free func isn’t g_free(). |
| * See discussion in https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2152 */ |
| } |
| |
| static void |
| test_to_array_transferred (void) |
| { |
| gconstpointer memory; |
| GByteArray *array; |
| GBytes *bytes; |
| |
| /* Memory transferred: one reference, and allocated with g_malloc */ |
| bytes = g_bytes_new (NYAN, N_NYAN); |
| memory = g_bytes_get_data (bytes, NULL); |
| array = g_bytes_unref_to_array (bytes); |
| g_assert_nonnull (array); |
| g_assert_true (array->data == memory); |
| g_assert_cmpmem (array->data, array->len, NYAN, N_NYAN); |
| g_byte_array_unref (array); |
| } |
| |
| static void |
| test_to_array_transferred_oversize (void) |
| { |
| g_test_message ("g_bytes_unref_to_array() can only take GBytes up to " |
| "G_MAXUINT in length; test that longer ones are rejected"); |
| |
| if (sizeof (guint) >= sizeof (gsize)) |
| { |
| g_test_skip ("Skipping test as guint is not smaller than gsize"); |
| } |
| else if (g_test_undefined ()) |
| { |
| GByteArray *array = NULL; |
| GBytes *bytes = NULL; |
| gpointer data = g_memdup2 (NYAN, N_NYAN); |
| gsize len = ((gsize) G_MAXUINT) + 1; |
| |
| bytes = g_bytes_new_take (data, len); |
| g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, |
| "g_byte_array_new_take: assertion 'len <= G_MAXUINT' failed"); |
| array = g_bytes_unref_to_array (g_steal_pointer (&bytes)); |
| g_test_assert_expected_messages (); |
| g_assert_null (array); |
| |
| g_free (data); |
| } |
| else |
| { |
| g_test_skip ("Skipping test as testing undefined behaviour is disabled"); |
| } |
| } |
| |
| static void |
| test_to_array_two_refs (void) |
| { |
| gconstpointer memory; |
| GByteArray *array; |
| GBytes *bytes; |
| gsize size; |
| |
| /* Memory copied: two references */ |
| bytes = g_bytes_new (NYAN, N_NYAN); |
| bytes = g_bytes_ref (bytes); |
| memory = g_bytes_get_data (bytes, NULL); |
| array = g_bytes_unref_to_array (bytes); |
| g_assert_nonnull (array); |
| g_assert_true (array->data != memory); |
| g_assert_cmpmem (array->data, array->len, NYAN, N_NYAN); |
| g_byte_array_unref (array); |
| g_assert_true (g_bytes_get_data (bytes, &size) == memory); |
| g_assert_cmpuint (size, ==, N_NYAN); |
| g_assert_cmpuint (g_bytes_get_size (bytes), ==, N_NYAN); |
| g_bytes_unref (bytes); |
| } |
| |
| static void |
| test_to_array_non_malloc (void) |
| { |
| GByteArray *array; |
| GBytes *bytes; |
| |
| /* Memory copied: non malloc memory */ |
| bytes = g_bytes_new_static (NYAN, N_NYAN); |
| g_assert_true (g_bytes_get_data (bytes, NULL) == NYAN); |
| array = g_bytes_unref_to_array (bytes); |
| g_assert_nonnull (array); |
| g_assert_true (array->data != (gpointer)NYAN); |
| g_assert_cmpmem (array->data, array->len, NYAN, N_NYAN); |
| g_byte_array_unref (array); |
| } |
| |
| static void |
| test_null (void) |
| { |
| GBytes *bytes; |
| gpointer data; |
| gsize size; |
| |
| bytes = g_bytes_new (NULL, 0); |
| |
| data = g_bytes_unref_to_data (bytes, &size); |
| |
| g_assert_null (data); |
| g_assert_cmpuint (size, ==, 0); |
| } |
| |
| static void |
| test_get_region (void) |
| { |
| GBytes *bytes; |
| |
| bytes = g_bytes_new_static (NYAN, N_NYAN); |
| |
| /* simple valid gets at the start */ |
| g_assert_true (g_bytes_get_region (bytes, 1, 0, 1) == NYAN); |
| g_assert_true (g_bytes_get_region (bytes, 1, 0, N_NYAN) == NYAN); |
| |
| /* an invalid get because the range is too wide */ |
| g_assert_true (g_bytes_get_region (bytes, 1, 0, N_NYAN + 1) == NULL); |
| |
| /* an valid get, but of a zero-byte range at the end */ |
| g_assert_true (g_bytes_get_region (bytes, 1, N_NYAN, 0) == NYAN + N_NYAN); |
| |
| /* not a valid get because it overlap ones byte */ |
| g_assert_true (g_bytes_get_region (bytes, 1, N_NYAN, 1) == NULL); |
| |
| /* let's try some multiplication overflow now */ |
| g_assert_true (g_bytes_get_region (bytes, 32, 0, G_MAXSIZE / 32 + 1) == NULL); |
| g_assert_true (g_bytes_get_region (bytes, G_MAXSIZE / 32 + 1, 0, 32) == NULL); |
| |
| /* and some addition overflow */ |
| g_assert_true (g_bytes_get_region (bytes, 1, G_MAXSIZE, -G_MAXSIZE) == NULL); |
| g_assert_true (g_bytes_get_region (bytes, 1, G_MAXSSIZE, ((gsize) G_MAXSSIZE) + 1) == NULL); |
| g_assert_true (g_bytes_get_region (bytes, 1, G_MAXSIZE, 1) == NULL); |
| |
| g_bytes_unref (bytes); |
| } |
| |
| static void |
| test_unref_null (void) |
| { |
| g_test_summary ("Test that calling g_bytes_unref() on NULL is a no-op"); |
| g_bytes_unref (NULL); |
| } |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| g_test_init (&argc, &argv, NULL); |
| |
| g_test_add_func ("/bytes/new", test_new); |
| g_test_add_func ("/bytes/new-take", test_new_take); |
| g_test_add_func ("/bytes/new-static", test_new_static); |
| g_test_add_func ("/bytes/new-with-free-func", test_new_with_free_func); |
| g_test_add_func ("/bytes/new-from-bytes", test_new_from_bytes); |
| g_test_add_func ("/bytes/new-from-bytes-slice", test_new_from_bytes_slice); |
| g_test_add_func ("/bytes/new-from-bytes-shared-ref", test_new_from_bytes_shared_ref); |
| g_test_add_func ("/bytes/hash", test_hash); |
| g_test_add_func ("/bytes/equal", test_equal); |
| g_test_add_func ("/bytes/compare", test_compare); |
| g_test_add_func ("/bytes/to-data/transferred", test_to_data_transferred); |
| g_test_add_func ("/bytes/to-data/two-refs", test_to_data_two_refs); |
| g_test_add_func ("/bytes/to-data/non-malloc", test_to_data_non_malloc); |
| g_test_add_func ("/bytes/to-data/different-free-func", test_to_data_different_free_func); |
| g_test_add_func ("/bytes/to-array/transferred", test_to_array_transferred); |
| g_test_add_func ("/bytes/to-array/transferred/oversize", test_to_array_transferred_oversize); |
| g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs); |
| g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc); |
| g_test_add_func ("/bytes/null", test_null); |
| g_test_add_func ("/bytes/get-region", test_get_region); |
| g_test_add_func ("/bytes/unref-null", test_unref_null); |
| |
| return g_test_run (); |
| } |