Commit 6044dfc3 authored by Neil Roberts's avatar Neil Roberts Committed by Benjamin Otte

bitmask: Don't allocate memory for small bitmasks

Code taken more or less verbatim from CoglBitmask.
parent 27eb83a4
...@@ -31,6 +31,15 @@ struct _GtkBitmask { ...@@ -31,6 +31,15 @@ struct _GtkBitmask {
VALUE_TYPE data[1]; VALUE_TYPE data[1];
}; };
#define ENSURE_ALLOCATED(mask, heap_mask) G_STMT_START { \
if (!_gtk_bitmask_is_allocated (mask)) \
{ \
heap_mask.data[0] = _gtk_bitmask_to_bits (mask); \
heap_mask.len = heap_mask.data[0] ? 1 : 0; \
mask = &heap_mask; \
} \
} G_STMT_END
static GtkBitmask * static GtkBitmask *
gtk_allocated_bitmask_resize (GtkBitmask *mask, gtk_allocated_bitmask_resize (GtkBitmask *mask,
gsize size) G_GNUC_WARN_UNUSED_RESULT; gsize size) G_GNUC_WARN_UNUSED_RESULT;
...@@ -40,7 +49,7 @@ gtk_allocated_bitmask_resize (GtkBitmask *mask, ...@@ -40,7 +49,7 @@ gtk_allocated_bitmask_resize (GtkBitmask *mask,
{ {
gsize i; gsize i;
mask = g_realloc (mask, sizeof (GtkBitmask) + sizeof(VALUE_TYPE) * (MAX (size, 1) - 1)); mask = g_realloc (mask, sizeof (GtkBitmask) + sizeof(VALUE_TYPE) * (size - 1));
for (i = mask->len; i < size; i++) for (i = mask->len; i < size; i++)
mask->data[i] = 0; mask->data[i] = 0;
...@@ -50,10 +59,25 @@ gtk_allocated_bitmask_resize (GtkBitmask *mask, ...@@ -50,10 +59,25 @@ gtk_allocated_bitmask_resize (GtkBitmask *mask,
return mask; return mask;
} }
GtkBitmask * static GtkBitmask *
_gtk_allocated_bitmask_new (void) gtk_allocated_bitmask_new_for_bits (gsize bits)
{
GtkBitmask *mask;
mask = g_malloc (sizeof (GtkBitmask));
mask->len = bits ? 1 : 0;
mask->data[0] = bits;
return mask;
}
static GtkBitmask *
gtk_bitmask_ensure_allocated (GtkBitmask *mask)
{ {
return g_malloc0 (sizeof (GtkBitmask)); if (_gtk_bitmask_is_allocated (mask))
return mask;
else
return gtk_allocated_bitmask_new_for_bits (_gtk_bitmask_to_bits (mask));
} }
GtkBitmask * GtkBitmask *
...@@ -63,7 +87,7 @@ _gtk_allocated_bitmask_copy (const GtkBitmask *mask) ...@@ -63,7 +87,7 @@ _gtk_allocated_bitmask_copy (const GtkBitmask *mask)
g_return_val_if_fail (mask != NULL, NULL); g_return_val_if_fail (mask != NULL, NULL);
copy = _gtk_allocated_bitmask_new (); copy = gtk_allocated_bitmask_new_for_bits (0);
return _gtk_allocated_bitmask_union (copy, mask); return _gtk_allocated_bitmask_union (copy, mask);
} }
...@@ -80,11 +104,14 @@ void ...@@ -80,11 +104,14 @@ void
_gtk_allocated_bitmask_print (const GtkBitmask *mask, _gtk_allocated_bitmask_print (const GtkBitmask *mask,
GString *string) GString *string)
{ {
GtkBitmask mask_allocated;
int i; int i;
g_return_if_fail (mask != NULL); g_return_if_fail (mask != NULL);
g_return_if_fail (string != NULL); g_return_if_fail (string != NULL);
ENSURE_ALLOCATED (mask, mask_allocated);
for (i = mask->len * VALUE_SIZE_BITS - 1; i >= 0; i--) for (i = mask->len * VALUE_SIZE_BITS - 1; i >= 0; i--)
{ {
if (_gtk_allocated_bitmask_get (mask, i)) if (_gtk_allocated_bitmask_get (mask, i))
...@@ -103,16 +130,6 @@ _gtk_allocated_bitmask_print (const GtkBitmask *mask, ...@@ -103,16 +130,6 @@ _gtk_allocated_bitmask_print (const GtkBitmask *mask,
} }
} }
char *
_gtk_allocated_bitmask_to_string (const GtkBitmask *mask)
{
GString *string;
string = g_string_new (NULL);
_gtk_allocated_bitmask_print (mask, string);
return g_string_free (string, FALSE);
}
/* NB: Call this function whenever the /* NB: Call this function whenever the
* array might have become too large. * array might have become too large.
* _gtk_allocated_bitmask_is_empty() depends on this. * _gtk_allocated_bitmask_is_empty() depends on this.
...@@ -130,6 +147,14 @@ gtk_allocated_bitmask_shrink (GtkBitmask *mask) ...@@ -130,6 +147,14 @@ gtk_allocated_bitmask_shrink (GtkBitmask *mask)
break; break;
} }
if (i == 0 ||
(i == 1 && mask->data[0] < VALUE_BIT (GTK_BITMASK_N_DIRECT_BITS)))
{
GtkBitmask *result = _gtk_bitmask_from_bits (i == 0 ? 0 : mask->data[0]);
_gtk_allocated_bitmask_free (mask);
return result;
}
return gtk_allocated_bitmask_resize (mask, i); return gtk_allocated_bitmask_resize (mask, i);
} }
...@@ -137,11 +162,15 @@ GtkBitmask * ...@@ -137,11 +162,15 @@ GtkBitmask *
_gtk_allocated_bitmask_intersect (GtkBitmask *mask, _gtk_allocated_bitmask_intersect (GtkBitmask *mask,
const GtkBitmask *other) const GtkBitmask *other)
{ {
GtkBitmask other_allocated;
guint i; guint i;
g_return_val_if_fail (mask != NULL, NULL); g_return_val_if_fail (mask != NULL, NULL);
g_return_val_if_fail (other != NULL, NULL); g_return_val_if_fail (other != NULL, NULL);
mask = gtk_bitmask_ensure_allocated (mask);
ENSURE_ALLOCATED (other, other_allocated);
mask = gtk_allocated_bitmask_resize (mask, MIN (mask->len, other->len)); mask = gtk_allocated_bitmask_resize (mask, MIN (mask->len, other->len));
for (i = 0; i < mask->len; i++) for (i = 0; i < mask->len; i++)
{ {
...@@ -155,11 +184,15 @@ GtkBitmask * ...@@ -155,11 +184,15 @@ GtkBitmask *
_gtk_allocated_bitmask_union (GtkBitmask *mask, _gtk_allocated_bitmask_union (GtkBitmask *mask,
const GtkBitmask *other) const GtkBitmask *other)
{ {
GtkBitmask other_allocated;
guint i; guint i;
g_return_val_if_fail (mask != NULL, NULL); g_return_val_if_fail (mask != NULL, NULL);
g_return_val_if_fail (other != NULL, NULL); g_return_val_if_fail (other != NULL, NULL);
mask = gtk_bitmask_ensure_allocated (mask);
ENSURE_ALLOCATED (other, other_allocated);
mask = gtk_allocated_bitmask_resize (mask, MAX (mask->len, other->len)); mask = gtk_allocated_bitmask_resize (mask, MAX (mask->len, other->len));
for (i = 0; i < other->len; i++) for (i = 0; i < other->len; i++)
{ {
...@@ -173,11 +206,15 @@ GtkBitmask * ...@@ -173,11 +206,15 @@ GtkBitmask *
_gtk_allocated_bitmask_subtract (GtkBitmask *mask, _gtk_allocated_bitmask_subtract (GtkBitmask *mask,
const GtkBitmask *other) const GtkBitmask *other)
{ {
GtkBitmask other_allocated;
guint i; guint i;
g_return_val_if_fail (mask != NULL, NULL); g_return_val_if_fail (mask != NULL, NULL);
g_return_val_if_fail (other != NULL, NULL); g_return_val_if_fail (other != NULL, NULL);
mask = gtk_bitmask_ensure_allocated (mask);
ENSURE_ALLOCATED (other, other_allocated);
for (i = 0; i < other->len; i++) for (i = 0; i < other->len; i++)
{ {
mask->data[i] |= ~other->data[i]; mask->data[i] |= ~other->data[i];
...@@ -220,6 +257,7 @@ _gtk_allocated_bitmask_set (GtkBitmask *mask, ...@@ -220,6 +257,7 @@ _gtk_allocated_bitmask_set (GtkBitmask *mask,
g_return_val_if_fail (mask != NULL, NULL); g_return_val_if_fail (mask != NULL, NULL);
mask = gtk_bitmask_ensure_allocated (mask);
gtk_allocated_bitmask_indexes (index_, &array_index, &bit_index); gtk_allocated_bitmask_indexes (index_, &array_index, &bit_index);
if (value) if (value)
...@@ -251,22 +289,15 @@ _gtk_allocated_bitmask_invert_range (GtkBitmask *mask, ...@@ -251,22 +289,15 @@ _gtk_allocated_bitmask_invert_range (GtkBitmask *mask,
g_return_val_if_fail (mask != NULL, NULL); g_return_val_if_fail (mask != NULL, NULL);
g_return_val_if_fail (start < end, NULL); g_return_val_if_fail (start < end, NULL);
/* I CAN HAS SPEEDUP? */ mask = gtk_bitmask_ensure_allocated (mask);
/* I CAN HAS SPEEDUP? */
for (i = start; i < end; i++) for (i = start; i < end; i++)
mask = _gtk_allocated_bitmask_set (mask, i, !_gtk_allocated_bitmask_get (mask, i)); mask = _gtk_allocated_bitmask_set (mask, i, !_gtk_allocated_bitmask_get (mask, i));
return mask; return mask;
} }
gboolean
_gtk_allocated_bitmask_is_empty (const GtkBitmask *mask)
{
g_return_val_if_fail (mask != NULL, FALSE);
return mask->len == 0;
}
gboolean gboolean
_gtk_allocated_bitmask_equals (const GtkBitmask *mask, _gtk_allocated_bitmask_equals (const GtkBitmask *mask,
const GtkBitmask *other) const GtkBitmask *other)
...@@ -292,11 +323,15 @@ gboolean ...@@ -292,11 +323,15 @@ gboolean
_gtk_allocated_bitmask_intersects (const GtkBitmask *mask, _gtk_allocated_bitmask_intersects (const GtkBitmask *mask,
const GtkBitmask *other) const GtkBitmask *other)
{ {
GtkBitmask mask_allocated, other_allocated;
int i; int i;
g_return_val_if_fail (mask != NULL, FALSE); g_return_val_if_fail (mask != NULL, FALSE);
g_return_val_if_fail (other != NULL, FALSE); g_return_val_if_fail (other != NULL, FALSE);
ENSURE_ALLOCATED (mask, mask_allocated);
ENSURE_ALLOCATED (other, other_allocated);
for (i = MIN (mask->len, other->len) - 1; i >= 0; i--) for (i = MIN (mask->len, other->len) - 1; i >= 0; i--)
{ {
if (mask->data[i] & other->data[i]) if (mask->data[i] & other->data[i])
......
...@@ -27,12 +27,21 @@ G_BEGIN_DECLS ...@@ -27,12 +27,21 @@ G_BEGIN_DECLS
typedef struct _GtkBitmask GtkBitmask; typedef struct _GtkBitmask GtkBitmask;
#define _gtk_bitmask_to_bits(mask) \
(GPOINTER_TO_SIZE (mask) >> ((gsize) 1))
#define _gtk_bitmask_from_bits(bits) \
GSIZE_TO_POINTER ((((gsize) (bits)) << 1) | 1)
#define _gtk_bitmask_is_allocated(mask) \
(!(GPOINTER_TO_SIZE (mask) & 1))
#define GTK_BITMASK_N_DIRECT_BITS (sizeof (gsize) * 8 - 1)
GtkBitmask * _gtk_allocated_bitmask_new (void);
GtkBitmask * _gtk_allocated_bitmask_copy (const GtkBitmask *mask); GtkBitmask * _gtk_allocated_bitmask_copy (const GtkBitmask *mask);
void _gtk_allocated_bitmask_free (GtkBitmask *mask); void _gtk_allocated_bitmask_free (GtkBitmask *mask);
char * _gtk_allocated_bitmask_to_string (const GtkBitmask *mask);
void _gtk_allocated_bitmask_print (const GtkBitmask *mask, void _gtk_allocated_bitmask_print (const GtkBitmask *mask,
GString *string); GString *string);
...@@ -53,7 +62,6 @@ GtkBitmask * _gtk_allocated_bitmask_invert_range (GtkBitmask *mask ...@@ -53,7 +62,6 @@ GtkBitmask * _gtk_allocated_bitmask_invert_range (GtkBitmask *mask
guint start, guint start,
guint end) G_GNUC_WARN_UNUSED_RESULT; guint end) G_GNUC_WARN_UNUSED_RESULT;
gboolean _gtk_allocated_bitmask_is_empty (const GtkBitmask *mask);
gboolean _gtk_allocated_bitmask_equals (const GtkBitmask *mask, gboolean _gtk_allocated_bitmask_equals (const GtkBitmask *mask,
const GtkBitmask *other); const GtkBitmask *other);
gboolean _gtk_allocated_bitmask_intersects (const GtkBitmask *mask, gboolean _gtk_allocated_bitmask_intersects (const GtkBitmask *mask,
......
...@@ -23,25 +23,33 @@ ...@@ -23,25 +23,33 @@
static inline GtkBitmask * static inline GtkBitmask *
_gtk_bitmask_new (void) _gtk_bitmask_new (void)
{ {
return _gtk_allocated_bitmask_new (); return _gtk_bitmask_from_bits (0);
} }
static inline GtkBitmask * static inline GtkBitmask *
_gtk_bitmask_copy (const GtkBitmask *mask) _gtk_bitmask_copy (const GtkBitmask *mask)
{ {
return _gtk_allocated_bitmask_copy (mask); if (_gtk_bitmask_is_allocated (mask))
return _gtk_allocated_bitmask_copy (mask);
else
return (GtkBitmask *) mask;
} }
static inline void static inline void
_gtk_bitmask_free (GtkBitmask *mask) _gtk_bitmask_free (GtkBitmask *mask)
{ {
return _gtk_allocated_bitmask_free (mask); if (_gtk_bitmask_is_allocated (mask))
return _gtk_allocated_bitmask_free (mask);
} }
static inline char * static inline char *
_gtk_bitmask_to_string (const GtkBitmask *mask) _gtk_bitmask_to_string (const GtkBitmask *mask)
{ {
return _gtk_allocated_bitmask_to_string (mask); GString *string;
string = g_string_new (NULL);
_gtk_allocated_bitmask_print (mask, string);
return g_string_free (string, FALSE);
} }
static inline void static inline void
...@@ -62,7 +70,12 @@ static inline GtkBitmask * ...@@ -62,7 +70,12 @@ static inline GtkBitmask *
_gtk_bitmask_union (GtkBitmask *mask, _gtk_bitmask_union (GtkBitmask *mask,
const GtkBitmask *other) const GtkBitmask *other)
{ {
return _gtk_allocated_bitmask_union (mask, other); if (_gtk_bitmask_is_allocated (mask) ||
_gtk_bitmask_is_allocated (other))
return _gtk_allocated_bitmask_union (mask, other);
else
return _gtk_bitmask_from_bits (_gtk_bitmask_to_bits (mask)
| _gtk_bitmask_to_bits (other));
} }
static inline GtkBitmask * static inline GtkBitmask *
...@@ -76,7 +89,12 @@ static inline gboolean ...@@ -76,7 +89,12 @@ static inline gboolean
_gtk_bitmask_get (const GtkBitmask *mask, _gtk_bitmask_get (const GtkBitmask *mask,
guint index_) guint index_)
{ {
return _gtk_allocated_bitmask_get (mask, index_); if (_gtk_bitmask_is_allocated (mask))
return _gtk_allocated_bitmask_get (mask, index_);
else
return index_ < GTK_BITMASK_N_DIRECT_BITS
? !!(_gtk_bitmask_to_bits (mask) & (((size_t) 1) << index_))
: FALSE;
} }
static inline GtkBitmask * static inline GtkBitmask *
...@@ -84,7 +102,22 @@ _gtk_bitmask_set (GtkBitmask *mask, ...@@ -84,7 +102,22 @@ _gtk_bitmask_set (GtkBitmask *mask,
guint index_, guint index_,
gboolean value) gboolean value)
{ {
return _gtk_allocated_bitmask_set (mask, index_, value); if (_gtk_bitmask_is_allocated (mask) ||
(index_ >= GTK_BITMASK_N_DIRECT_BITS && value))
return _gtk_allocated_bitmask_set (mask, index_, value);
else if (index_ < GTK_BITMASK_N_DIRECT_BITS)
{
gsize bits = _gtk_bitmask_to_bits (mask);
if (value)
bits |= ((size_t) 1) << index_;
else
bits &= ~(((size_t) 1) << index_);
return _gtk_bitmask_from_bits (bits);
}
else
return mask;
} }
static inline GtkBitmask * static inline GtkBitmask *
...@@ -92,19 +125,34 @@ _gtk_bitmask_invert_range (GtkBitmask *mask, ...@@ -92,19 +125,34 @@ _gtk_bitmask_invert_range (GtkBitmask *mask,
guint start, guint start,
guint end) guint end)
{ {
return _gtk_allocated_bitmask_invert_range (mask, start, end); if (_gtk_bitmask_is_allocated (mask) ||
(end > GTK_BITMASK_N_DIRECT_BITS))
return _gtk_allocated_bitmask_invert_range (mask, start, end);
else
{
size_t invert = (((size_t) 1) << end) - (((size_t) 1) << start);
return _gtk_bitmask_from_bits (_gtk_bitmask_to_bits (mask) ^ invert);
}
} }
static inline gboolean static inline gboolean
_gtk_bitmask_is_empty (const GtkBitmask *mask) _gtk_bitmask_is_empty (const GtkBitmask *mask)
{ {
return _gtk_allocated_bitmask_is_empty (mask); return mask == _gtk_bitmask_from_bits (0);
} }
static inline gboolean static inline gboolean
_gtk_bitmask_equals (const GtkBitmask *mask, _gtk_bitmask_equals (const GtkBitmask *mask,
const GtkBitmask *other) const GtkBitmask *other)
{ {
if (mask == other)
return TRUE;
if (!_gtk_bitmask_is_allocated (mask) ||
!_gtk_bitmask_is_allocated (other))
return FALSE;
return _gtk_allocated_bitmask_equals (mask, other); return _gtk_allocated_bitmask_equals (mask, other);
} }
...@@ -112,5 +160,9 @@ static inline gboolean ...@@ -112,5 +160,9 @@ static inline gboolean
_gtk_bitmask_intersects (const GtkBitmask *mask, _gtk_bitmask_intersects (const GtkBitmask *mask,
const GtkBitmask *other) const GtkBitmask *other)
{ {
return _gtk_allocated_bitmask_intersects (mask, other); if (!_gtk_bitmask_is_allocated (mask) ||
!_gtk_bitmask_is_allocated (other))
return _gtk_allocated_bitmask_intersects (mask, other);
else
return _gtk_bitmask_to_bits (mask) & _gtk_bitmask_to_bits (other);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment