diff --git a/glib/gbytes.c b/glib/gbytes.c index 3c6436371..5c844899f 100644 --- a/glib/gbytes.c +++ b/glib/gbytes.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +35,12 @@ #include +#if GLIB_SIZEOF_VOID_P == 8 +# define G_BYTES_MAX_INLINE (128 - sizeof(GBytesInline)) +#else +# define G_BYTES_MAX_INLINE (64 - sizeof(GBytesInline)) +#endif + /** * GBytes: (copy-func g_bytes_ref) (free-func g_bytes_unref) * @@ -77,6 +82,21 @@ struct _GBytes gpointer user_data; }; +typedef struct +{ + GBytes bytes; + /* Despite no guarantee about alignment in GBytes, it is nice to + * provide that to ensure that any code which predates support + * for inline data continues to work without disruption. malloc() + * on glibc systems would guarantee 2*sizeof(void*) aligned + * allocations and this matches that. + */ + gsize padding; + guint8 inline_data[]; +} GBytesInline; + +G_STATIC_ASSERT (G_STRUCT_OFFSET (GBytesInline, inline_data) == (6 * GLIB_SIZEOF_VOID_P)); + /** * g_bytes_new: * @data: (transfer none) (array length=size) (element-type guint8) (nullable): @@ -87,6 +107,9 @@ struct _GBytes * * @data is copied. If @size is 0, @data may be %NULL. * + * As an optimization, g_bytes_new() may avoid an extra allocation by copying + * the data within the resulting bytes structure if sufficiently small (since GLib 2.84). + * * Returns: (transfer full): a new #GBytes * * Since: 2.32 @@ -97,6 +120,22 @@ g_bytes_new (gconstpointer data, { g_return_val_if_fail (data != NULL || size == 0, NULL); + if (size <= G_BYTES_MAX_INLINE) + { + GBytesInline *bytes; + + bytes = g_malloc (sizeof *bytes + size); + bytes->bytes.data = bytes->inline_data; + bytes->bytes.size = size; + bytes->bytes.free_func = NULL; + bytes->bytes.user_data = NULL; + g_atomic_ref_count_init (&bytes->bytes.ref_count); + + memcpy (bytes->inline_data, data, size); + + return (GBytes *)bytes; + } + return g_bytes_new_take (g_memdup2 (data, size), size); } @@ -183,7 +222,7 @@ g_bytes_new_with_free_func (gconstpointer data, g_return_val_if_fail (data != NULL || size == 0, NULL); - bytes = g_slice_new (GBytes); + bytes = g_new (GBytes, 1); bytes->data = data; bytes->size = size; bytes->free_func = free_func; @@ -335,7 +374,7 @@ g_bytes_unref (GBytes *bytes) { if (bytes->free_func != NULL) bytes->free_func (bytes->user_data); - g_slice_free (GBytes, bytes); + g_free (bytes); } } @@ -451,7 +490,7 @@ try_steal_and_unref (GBytes *bytes, { *size = bytes->size; result = (gpointer)bytes->data; - g_slice_free (GBytes, bytes); + g_free (bytes); return result; } @@ -469,8 +508,9 @@ try_steal_and_unref (GBytes *bytes, * * As an optimization, the byte data is returned without copying if this was * the last reference to bytes and bytes was created with g_bytes_new(), - * g_bytes_new_take() or g_byte_array_free_to_bytes(). In all other cases the - * data is copied. + * g_bytes_new_take() or g_byte_array_free_to_bytes() and the buffer was larger + * than the size #GBytes may internalize within its allocation. In all other + * cases the data is copied. * * Returns: (transfer full) (array length=size) (element-type guint8) * (not nullable): a pointer to the same byte data, which should be @@ -516,8 +556,9 @@ g_bytes_unref_to_data (GBytes *bytes, * * As an optimization, the byte data is transferred to the array without copying * if this was the last reference to bytes and bytes was created with - * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes(). In all - * other cases the data is copied. + * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes() and the + * buffer was larger than the size #GBytes may internalize within its allocation. + * In all other cases the data is copied. * * Do not use it if @bytes contains more than %G_MAXUINT * bytes. #GByteArray stores the length of its data in #guint, which diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c index e81a5f40e..16a08e222 100644 --- a/glib/tests/bytes.c +++ b/glib/tests/bytes.c @@ -28,8 +28,22 @@ struct _GBytes gpointer user_data; }; -static const gchar *NYAN = "nyannyan"; -static const gsize N_NYAN = 8; +static const gchar NYAN[128] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127 +}; +#define N_NYAN sizeof(NYAN) static void test_new (void)