Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gskatlasallocator.c

  Sprache: C
 

#include "config.h"

#include "gskatlasallocatorprivate.h"

typedef struct _GskAtlasSlot GskAtlasSlot;

typedef enum {
  /* memory not in use */
  GSK_ATLAS_FREE,
  /* memory not in use but still enqueued in emptylist */
  GSK_ATLAS_FREELIST,
  /* area free for use, enqueued in emptylist */
  GSK_ATLAS_EMPTY,
  /* manages a bunch of children */
  GSK_ATLAS_CONTAINER,
  /* alive and used by a cached item */
  GSK_ATLAS_USED,
} GskAtlasSlotType;

/**
 * lifetime of a slot:
 *
 *                     new slot added to array
 *                                
 *                                
 *           FREE
 *                               
 *    when removed         used to represent
 *   from epmptylist       an area after split
 *                               
 *      FREELIST                  
 *                                                                   (plus a new slot)   
 *                                                                                      
 *       merged   EMPTY  split  in same direction     
 *                                                                   as siblings         
 *                                             in opposite direction                      
 *                            allocated                                                   
 *                                                 CONTAINER                              
 *                                              (with 2 children)                         
 *                               USED                                                     
 *                                                                                       
 *                                          once all children are merged                  
 *                           deallocated                                                  
 *                                
 *
 */

struct _GskAtlasSlot
{
  GskAtlasSlotType type;
  gsize prev; /* or parent if first item */
  gsize next;
  union {
    /* FREE and EMPTY */
    gsize emptylist_next;
    /* USED */
    gpointer user_data;
    /* CONTAINER */
    gsize first_child;
  };
  cairo_rectangle_int_t area;
};

#define GDK_ARRAY_NAME gsk_atlas_slots
#define GDK_ARRAY_TYPE_NAME GskAtlasSlots
#define GDK_ARRAY_ELEMENT_TYPE GskAtlasSlot
#define GDK_ARRAY_BY_VALUE 1
#define GDK_ARRAY_PREALLOC 32
#include "gdk/gdkarrayimpl.c"

#define N_EMPTYLISTS 8

struct _GskAtlasAllocator
{
  GskAtlasSlots slots;
  gsize root;
  gsize first_free_slot;
  gsize emptylists[N_EMPTYLISTS];
};

void
gsk_atlas_allocator_free (GskAtlasAllocator *self)
{
  gsk_atlas_slots_clear (&self->slots);

  g_free (self);
}

static gboolean
is_first_child (GskAtlasAllocator *self,
                gsize              pos)
{
  GskAtlasSlot *slot, *prev;

  slot = gsk_atlas_slots_get (&self->slots, pos);
  if (slot->prev == G_MAXSIZE)
    return TRUE;

  prev = gsk_atlas_slots_get (&self->slots, slot->prev);
  if (prev->type != GSK_ATLAS_CONTAINER)
    return FALSE;

  return prev->first_child == pos;
}

static void
gsk_atlas_allocator_dump (GskAtlasAllocator *self,
                          const char        *format,
                          ...) G_GNUC_PRINTF (23);
#if 0
static const char *type_names[] = {
  "FREE",
  "FREELIST",
  "EMPTY",
  "CONTAINER",
  "USED",
};

static void
gsk_atlas_allocator_dump_depth (GskAtlasAllocator *self,
                                gsize              pos,
                                gsize              depth)
{
  gsize prev;
  GskAtlasSlot *slot;
  cairo_region_t *region;

  slot = gsk_atlas_slots_get (&self->slots, pos);
  if (slot->prev == G_MAXSIZE)
    {
      g_assert (depth == 0);
      region = cairo_region_create_rectangle (&slot->area);
    }
  else
    {
      GskAtlasSlot *prev_slot = gsk_atlas_slots_get (&self->slots, slot->prev);
      g_assert (is_first_child (self, pos));
      region = cairo_region_create_rectangle (&prev_slot->area);
    }
  prev = slot->prev;

  while (pos != G_MAXSIZE)
    {
      slot = gsk_atlas_slots_get (&self->slots, pos);
      g_assert_cmpuint (slot->prev, ==, prev);
      g_assert (slot->type != GSK_ATLAS_FREE && slot->type != GSK_ATLAS_FREELIST);
      g_assert_cmpint (cairo_region_intersect_rectangle (region, &slot->area), ==, CAIRO_REGION_OVERLAP_IN);
      cairo_region_subtract_rectangle (region, &slot->area);
      g_assert_cmpint (cairo_region_num_rectangles (region), <=, 1);
      g_print ("%*s%s (%zu) %d %d %d %d\n",
               2 * (int) depth, "",
               type_names[slot->type],
               pos,
               slot->area.x, slot->area.y,
               slot->area.width, slot->area.height);
      if (slot->type == GSK_ATLAS_CONTAINER)
        gsk_atlas_allocator_dump_depth (self, slot->first_child, depth + 1);

      prev = pos;
      pos = slot->next;
    }

  g_assert_true (cairo_region_is_empty (region));
  cairo_region_destroy (region);
}

static void
gsk_atlas_allocator_dump (GskAtlasAllocator *self,
                          const char        *format,
                          ...)
{
  gsize i, j;
  va_list args;
  char *s;

  va_start (args, format);
  s = g_strdup_vprintf (format, args);
  g_print ("%s\n", s);
  va_end (args);

  gsk_atlas_allocator_dump_depth (self, self->root, 0);

  for (i = 0; i < G_N_ELEMENTS (self->emptylists); i++)
    {
      g_print ("emptylist %zu: ", i);
      for (j = self->emptylists[i]; j != G_MAXSIZE; )
        {
          GskAtlasSlot *slot;
      
          slot = gsk_atlas_slots_get (&self->slots, j);
          g_assert (slot->type == GSK_ATLAS_EMPTY || slot->type == GSK_ATLAS_FREELIST);
          if (j != self->emptylists[i])
            g_print (", ");
          g_print ("%zu%s", j, slot->type == GSK_ATLAS_FREELIST ? "*" : "");
          j = slot->emptylist_next;
        }
      g_print ("\n");
    }
}
#else
static void
gsk_atlas_allocator_dump (GskAtlasAllocator *self,
                          const char        *format,
                          ...)
{
}
#endif

static gsize
get_emptylist_for_size (gsize width,
                        gsize height)
{
  return MIN (g_bit_storage (MIN (width, height)), N_EMPTYLISTS) - 1;
}

GskAtlasAllocator *
gsk_atlas_allocator_new (gsize width,
                         gsize height)
{
  GskAtlasAllocator *self;
  gsize i;

  self = g_new0 (GskAtlasAllocator, 1);
  gsk_atlas_slots_init (&self->slots);

  gsk_atlas_slots_append (&self->slots,
                          &(GskAtlasSlot) {
                              .type = GSK_ATLAS_EMPTY,
                              .prev = G_MAXSIZE,
                              .next = G_MAXSIZE,
                              .emptylist_next = G_MAXSIZE,
                              .area = (cairo_rectangle_int_t) { 00, width, height }
                          });
  self->root = 0;
  for (i = 0; i < G_N_ELEMENTS (self->emptylists); i++)
    self->emptylists[i] = G_MAXSIZE;
  self->emptylists[get_emptylist_for_size (width, height)] = 0;

  return self;
}

static void
gsk_atlas_allocator_free_slot (GskAtlasAllocator *self,
                               gsize              pos)
{
  GskAtlasSlot *slot;
  
  slot = gsk_atlas_slots_get (&self->slots, pos);
  slot->type = GSK_ATLAS_FREE;
  if (pos < self->first_free_slot)
    self->first_free_slot = pos;
}

#if 0
/* This rounds up to the next number that has <= 2 bits set:
 * 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, ...
 * That is roughly sqrt(2), so it should limit waste
 */

static gsize 
round_up_atlas_size (gsize num)
{
  gsize storage = g_bit_storage (num);

  num = num + (((1 << storage) - 1) >> 2);
  num &= (((gsize) 7) << storage) >> 2;

  return num;
}
#endif

static gsize
gsk_atlas_allocator_allocate_pos (GskAtlasAllocator *self,
                                  gsize              width,
                                  gsize              height)
{
  GskAtlasSlot *prev, *best_prev, *best_slot, *slot;
  gsize list, pos, best_list, best_pos, best_size;

  best_prev = NULL;
  best_slot = NULL;
  best_pos = G_MAXSIZE;
  best_size = G_MAXSIZE;
  best_list = G_MAXSIZE;

  for (list = get_emptylist_for_size (width, height); list < N_EMPTYLISTS && best_pos == G_MAXSIZE; list++)
    {
      prev = NULL;

      for (pos = self->emptylists[list]; pos < gsk_atlas_slots_get_size (&self->slots); /* done in loop */)
        {
          slot = gsk_atlas_slots_get (&self->slots, pos);
          if (slot->type == GSK_ATLAS_FREELIST)
            {
              gsize next_pos = slot->emptylist_next;
              if (prev)
                prev->emptylist_next = slot->emptylist_next;
              else
                self->emptylists[list] = slot->emptylist_next;
              gsk_atlas_allocator_free_slot (self, pos);
              pos = next_pos;
              continue;
            }

          if (slot->area.width >= width &&
              slot->area.height >= height &&
              slot->area.width * slot->area.height < best_size)
            {
              best_prev = prev;
              best_list = list;
              best_slot = slot;
              best_pos = pos;
              best_size = slot->area.width * slot->area.height;
            }
          prev = slot;
          pos = slot->emptylist_next;
        }
    }

  if (best_pos >= gsk_atlas_slots_get_size (&self->slots))
    return G_MAXSIZE;

  if (best_prev)
    {
      best_prev->emptylist_next = best_slot->emptylist_next;
      best_slot->emptylist_next = G_MAXSIZE;
    }
  else
    self->emptylists[best_list] = best_slot->emptylist_next;

  return best_pos;
}

static void
gsk_atlas_allocator_enqueue_empty (GskAtlasAllocator *self,
                                   gsize              pos)
{
  GskAtlasSlot *slot;
  gsize list;

  slot = gsk_atlas_slots_get (&self->slots, pos);

  g_assert (slot->type == GSK_ATLAS_EMPTY);

  list = get_emptylist_for_size (slot->area.width, slot->area.height);
  slot->emptylist_next = self->emptylists[list];
  self->emptylists[list] = pos;
}

static void
rectangle_union (const cairo_rectangle_int_t *src1,
   const cairo_rectangle_int_t *src2,
   cairo_rectangle_int_t       *dest)
{
  int dest_x, dest_y;
  
  g_return_if_fail (src1 != NULL);
  g_return_if_fail (src2 != NULL);
  g_return_if_fail (dest != NULL);

  dest_x = MIN (src1->x, src2->x);
  dest_y = MIN (src1->y, src2->y);
  dest->width = MAX (src1->x + src1->width, src2->x + src2->width) - dest_x;
  dest->height = MAX (src1->y + src1->height, src2->y + src2->height) - dest_y;
  dest->x = dest_x;
  dest->y = dest_y;
}

static void
gsk_atlas_allocator_make_empty (GskAtlasAllocator *self,
                                gsize              pos)
{
  GskAtlasSlot *slot;
  gsize tmp;

  slot = gsk_atlas_slots_get (&self->slots, pos);

  if (!is_first_child (self, pos))
    {
      GskAtlasSlot *prev = gsk_atlas_slots_get (&self->slots, slot->prev);
      if (prev->type == GSK_ATLAS_EMPTY)
        {
          rectangle_union (&slot->area, &prev->area, &slot->area);
          if (is_first_child (self, slot->prev))
            {
              if (prev->prev != G_MAXSIZE)
                {
                  GskAtlasSlot *parent = gsk_atlas_slots_get (&self->slots, prev->prev);
                  parent->first_child = pos;
                }
              else
                {
                  self->root = pos;
                }
            }
          else
            {
              GskAtlasSlot *prev_prev = gsk_atlas_slots_get (&self->slots, prev->prev);
              prev_prev->next = pos;
            }
          slot->prev = prev->prev;
          prev->type = GSK_ATLAS_FREELIST;
        }
    }
  if (slot->next != G_MAXSIZE)
    {
      GskAtlasSlot *next = gsk_atlas_slots_get (&self->slots, slot->next);
      if (next->type == GSK_ATLAS_EMPTY)
        {
          rectangle_union (&slot->area, &next->area, &slot->area);
          if (next->next != G_MAXSIZE)
            {
              GskAtlasSlot *next_next = gsk_atlas_slots_get (&self->slots, next->next);
              next_next->prev = pos;
            }
          slot->next = next->next;
          next->type = GSK_ATLAS_FREELIST;
        }
    }
  if (is_first_child (self, pos) && slot->prev != G_MAXSIZE && slot->next == G_MAXSIZE)
    {
      tmp = slot->prev;
      /* only item in this level, merge with parent */
      gsk_atlas_allocator_free_slot (self, pos);
      gsk_atlas_allocator_make_empty (self, tmp);
    }
  else
    {
      slot->type = GSK_ATLAS_EMPTY;
      gsk_atlas_allocator_enqueue_empty (self, pos);
    }
}

void
gsk_atlas_allocator_deallocate (GskAtlasAllocator *self,
                                gsize              pos)
{
#ifndef G_DISABLE_ASSERT
  GskAtlasSlot *slot;

  slot = gsk_atlas_slots_get (&self->slots, pos);
  g_assert (slot->type == GSK_ATLAS_USED);
#endif

  gsk_atlas_allocator_make_empty (self, pos);

  gsk_atlas_allocator_dump (self, "DEALLOCATION:");
}

static gsize
gsk_atlas_allocator_allocate_slot (GskAtlasAllocator *self)
{
  gsize pos;

  for (pos = self->first_free_slot; pos < gsk_atlas_slots_get_size (&self->slots); pos++)
    {
      GskAtlasSlot *slot;

      slot = gsk_atlas_slots_get (&self->slots, pos);
      if (slot->type == GSK_ATLAS_FREE)
        {
          self->first_free_slot = pos + 1;
          return pos;
        }
    }
  gsk_atlas_slots_set_size (&self->slots, pos + 1);
  self->first_free_slot = pos + 1;

  return pos;
}

static gsize
gsk_atlas_allocator_resize_slot (GskAtlasAllocator *self,
                                 gsize              pos,
                                 gboolean           horizontal,
                                 gboolean           opposite,
                                 gsize              size)
{
  GskAtlasSlot *slot, *split;
  gsize split_pos;

  slot = gsk_atlas_slots_get (&self->slots, pos);
  if ((horizontal && slot->area.width <= size) ||
      (!horizontal && slot->area.height <= size))
    {
      if (horizontal)
        {
          g_assert (slot->area.width == size);
        }
      else
        {
          g_assert (slot->area.height == size);
        }
      return pos;
    }

  if (opposite)
    {
      GskAtlasSlot *child;
      gsize child_pos;

      child_pos = gsk_atlas_allocator_allocate_slot (self);
      slot = gsk_atlas_slots_get (&self->slots, pos);
      child = gsk_atlas_slots_get (&self->slots, child_pos);
      child->type = GSK_ATLAS_EMPTY;
      child->prev = pos;
      child->next = G_MAXSIZE;
      child->area = slot->area;

      slot->type = GSK_ATLAS_CONTAINER;
      slot->first_child = child_pos;

      slot = child;
      pos = child_pos;
    }

  split_pos = gsk_atlas_allocator_allocate_slot (self);
  slot = gsk_atlas_slots_get (&self->slots, pos);
  split = gsk_atlas_slots_get (&self->slots, split_pos);
  *split = *slot;
  split->prev = pos;
  if (slot->next != G_MAXSIZE)
    {
      GskAtlasSlot *next = gsk_atlas_slots_get (&self->slots, slot->next);
      next->prev = split_pos;
    }
  slot->next = split_pos;
  if (horizontal)
    {
      slot->area.width = size;
      split->area.x += size;
      split->area.width -= size;
    }
  else
    {
      slot->area.height = size;
      split->area.y += size;
      split->area.height -= size;
    }

  gsk_atlas_allocator_enqueue_empty (self, split_pos);

  return pos;
}

gsize
gsk_atlas_allocator_allocate (GskAtlasAllocator *self,
                              gsize              width,
                              gsize              height)
{
  GskAtlasSlot *slot, *other;
  gsize pos;

  pos = gsk_atlas_allocator_allocate_pos (self, width, height);
  if (pos == G_MAXSIZE)
    return G_MAXSIZE;

  slot = gsk_atlas_slots_get (&self->slots, pos);
  if (!is_first_child (self, pos))
    other = gsk_atlas_slots_get (&self->slots, slot->prev);
  else if (slot->next < G_MAXSIZE)
    other = gsk_atlas_slots_get (&self->slots, slot->next);
  else
    other = NULL;

  if (other == NULL || other->area.y == slot->area.y)
    {
      pos = gsk_atlas_allocator_resize_slot (self, pos, TRUEFALSE, width);
      pos = gsk_atlas_allocator_resize_slot (self, pos, FALSETRUE, height);
    }
  else
    {
      pos = gsk_atlas_allocator_resize_slot (self, pos, FALSEFALSE, height);
      pos = gsk_atlas_allocator_resize_slot (self, pos, TRUETRUE, width);
    }

  slot = gsk_atlas_slots_get (&self->slots, pos);
  slot->type = GSK_ATLAS_USED;
  slot->user_data = NULL;

  gsk_atlas_allocator_dump (self, "ALLOCATION %zux%zu:", width, height);

  return pos;
}

const cairo_rectangle_int_t *
gsk_atlas_allocator_get_area (GskAtlasAllocator *self,
                              gsize              pos)
{
  GskAtlasSlot *slot;

  g_assert (pos < gsk_atlas_slots_get_size (&self->slots));
  slot = gsk_atlas_slots_get (&self->slots, pos);
  g_assert (slot->type == GSK_ATLAS_USED);

  return &slot->area;
}

void
gsk_atlas_allocator_set_user_data (GskAtlasAllocator *self,
                                   gsize              pos,
                                   gpointer           user_data)
{
  GskAtlasSlot *slot;

  g_assert (pos < gsk_atlas_slots_get_size (&self->slots));
  slot = gsk_atlas_slots_get (&self->slots, pos);
  g_assert (slot->type == GSK_ATLAS_USED);

  slot->user_data = user_data;
}

gpointer
gsk_atlas_allocator_get_user_data (GskAtlasAllocator *self,
                                   gsize              pos)
{
  GskAtlasSlot *slot;

  g_assert (pos < gsk_atlas_slots_get_size (&self->slots));
  slot = gsk_atlas_slots_get (&self->slots, pos);
  g_assert (slot->type == GSK_ATLAS_USED);

  return slot->user_data;
}

void
gsk_atlas_allocator_iter_init (GskAtlasAllocator     *self,
                               GskAtlasAllocatorIter *iter)
{
  *iter = GSIZE_TO_POINTER (0);
}

gsize
gsk_atlas_allocator_iter_next (GskAtlasAllocator     *self,
                               GskAtlasAllocatorIter *iter)
{
  gsize pos;

  for (pos = GPOINTER_TO_SIZE (*iter); pos < gsk_atlas_slots_get_size (&self->slots); pos++)
    {
      GskAtlasSlot *slot;

      slot = gsk_atlas_slots_get (&self->slots, pos);
      if (slot->type == GSK_ATLAS_USED)
        break;
    }

  if (pos >= gsk_atlas_slots_get_size (&self->slots))
    {
      pos = G_MAXSIZE;
      *iter = GSIZE_TO_POINTER (pos);
    }
  else
    { 
      *iter = GSIZE_TO_POINTER (pos + 1);
    }

  return pos;
}

Messung V0.5 in Prozent
C=98 H=95 G=96

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-07-02) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik