Merge branch 'wip/chergert/gbytes-avoid-alloc' into 'main'

glib/gbytes: save small byte buffers inline

See merge request GNOME/glib!4290
This commit is contained in:
Philip Withnall 2024-09-25 10:49:20 +00:00
commit 67d5c59c5a
2 changed files with 65 additions and 10 deletions

View File

@ -28,7 +28,6 @@
#include <glib/garray.h>
#include <glib/gstrfuncs.h>
#include <glib/gatomic.h>
#include <glib/gslice.h>
#include <glib/gtestutils.h>
#include <glib/gmem.h>
#include <glib/gmessages.h>
@ -36,6 +35,12 @@
#include <string.h>
#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

View File

@ -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)