Blame gegl/graph/gegl-visitor.c

Packit bc1512
/* This file is part of GEGL
Packit bc1512
 *
Packit bc1512
 * GEGL is free software; you can redistribute it and/or
Packit bc1512
 * modify it under the terms of the GNU Lesser General Public
Packit bc1512
 * License as published by the Free Software Foundation; either
Packit bc1512
 * version 3 of the License, or (at your option) any later version.
Packit bc1512
 *
Packit bc1512
 * GEGL is distributed in the hope that it will be useful,
Packit bc1512
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit bc1512
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit bc1512
 * Lesser General Public License for more details.
Packit bc1512
 *
Packit bc1512
 * You should have received a copy of the GNU Lesser General Public
Packit bc1512
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
Packit bc1512
 *
Packit bc1512
 * Copyright 2003 Calvin Williamson
Packit bc1512
 */
Packit bc1512
Packit bc1512
#include "config.h"
Packit bc1512
Packit bc1512
#include <glib-object.h>
Packit bc1512
Packit bc1512
#include "gegl-types-internal.h"
Packit bc1512
Packit bc1512
#include "gegl.h"
Packit bc1512
#include "graph/gegl-node.h"
Packit bc1512
#include "graph/gegl-pad.h"
Packit bc1512
#include "gegl-visitor.h"
Packit bc1512
#include "gegl-visitable.h"
Packit bc1512
Packit bc1512
Packit bc1512
typedef struct _GeglVisitInfo GeglVisitInfo;
Packit bc1512
Packit bc1512
struct _GeglVisitInfo
Packit bc1512
{
Packit bc1512
  gboolean visited;
Packit bc1512
  gboolean discovered;
Packit bc1512
  gint     shared_count;
Packit bc1512
};
Packit bc1512
Packit bc1512
enum
Packit bc1512
{
Packit bc1512
  PROP_0,
Packit bc1512
  PROP_ID
Packit bc1512
};
Packit bc1512
Packit bc1512
Packit bc1512
static void           gegl_visitor_class_init  (GeglVisitorClass *klass);
Packit bc1512
static void           gegl_visitor_init        (GeglVisitor      *self);
Packit bc1512
static void           finalize                 (GObject          *gobject);
Packit bc1512
static void           init_dfs_traversal       (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable);
Packit bc1512
static void           dfs_traverse             (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable);
Packit bc1512
static void           init_bfs_traversal       (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable);
Packit bc1512
static void           visit_info_destroy       (GeglVisitInfo    *visit_info);
Packit bc1512
static void           insert                   (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable);
Packit bc1512
static GeglVisitInfo* lookup                   (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable);
Packit bc1512
static gboolean       get_visited              (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable);
Packit bc1512
static void           set_visited              (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable,
Packit bc1512
                                                gboolean          visited);
Packit bc1512
static gboolean       get_discovered           (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable);
Packit bc1512
static void           set_discovered           (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable,
Packit bc1512
                                                gboolean          discovered);
Packit bc1512
static gint           get_shared_count         (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable);
Packit bc1512
static void           set_shared_count         (GeglVisitor      *self,
Packit bc1512
                                                GeglVisitable    *visitable,
Packit bc1512
                                                gint              shared_count);
Packit bc1512
static void           visit_pad                (GeglVisitor      *self,
Packit bc1512
                                                GeglPad          *pad);
Packit bc1512
static void           visit_node               (GeglVisitor      *self,
Packit bc1512
                                                GeglNode         *node);
Packit bc1512
static void           set_property             (GObject          *gobject,
Packit bc1512
                                                guint             prop_id,
Packit bc1512
                                                const GValue     *value,
Packit bc1512
                                                GParamSpec       *pspec);
Packit bc1512
static void           get_property             (GObject          *gobject,
Packit bc1512
                                                guint             prop_id,
Packit bc1512
                                                GValue           *value,
Packit bc1512
                                                GParamSpec       *pspec);
Packit bc1512
Packit bc1512
Packit bc1512
Packit bc1512
G_DEFINE_TYPE (GeglVisitor, gegl_visitor, G_TYPE_OBJECT)
Packit bc1512
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_visitor_class_init (GeglVisitorClass *klass)
Packit bc1512
{
Packit bc1512
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit bc1512
Packit bc1512
  gobject_class->finalize = finalize;
Packit bc1512
Packit bc1512
  klass->visit_pad            = visit_pad;
Packit bc1512
  klass->visit_node           = visit_node;
Packit bc1512
  gobject_class->set_property = set_property;
Packit bc1512
  gobject_class->get_property = get_property;
Packit bc1512
Packit bc1512
  g_object_class_install_property (gobject_class, PROP_ID,
Packit bc1512
                                   g_param_spec_pointer ("id",
Packit bc1512
                                                         "evaluation-id",
Packit bc1512
                                                         "The identifier for the evaluation context",
Packit bc1512
                                                         G_PARAM_CONSTRUCT |
Packit bc1512
                                                         G_PARAM_READWRITE));
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
static void
Packit bc1512
set_property (GObject      *gobject,
Packit bc1512
              guint         property_id,
Packit bc1512
              const GValue *value,
Packit bc1512
              GParamSpec   *pspec)
Packit bc1512
{
Packit bc1512
  GeglVisitor *self = GEGL_VISITOR (gobject);
Packit bc1512
Packit bc1512
  switch (property_id)
Packit bc1512
    {
Packit bc1512
      case PROP_ID:
Packit bc1512
        self->context_id = g_value_get_pointer (value);
Packit bc1512
        break;
Packit bc1512
Packit bc1512
      default:
Packit bc1512
        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
Packit bc1512
        break;
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
get_property (GObject    *gobject,
Packit bc1512
              guint       property_id,
Packit bc1512
              GValue     *value,
Packit bc1512
              GParamSpec *pspec)
Packit bc1512
{
Packit bc1512
  GeglVisitor *self = GEGL_VISITOR (gobject);
Packit bc1512
Packit bc1512
  switch (property_id)
Packit bc1512
    {
Packit bc1512
      case PROP_ID:
Packit bc1512
        g_value_set_pointer (value, self->context_id);
Packit bc1512
        break;
Packit bc1512
Packit bc1512
      default:
Packit bc1512
        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
Packit bc1512
        break;
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_visitor_init (GeglVisitor *self)
Packit bc1512
{
Packit bc1512
  self->visits_list = NULL;
Packit bc1512
  self->hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
Packit bc1512
                                      NULL,
Packit bc1512
                                      (GDestroyNotify) visit_info_destroy);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
finalize (GObject *gobject)
Packit bc1512
{
Packit bc1512
  GeglVisitor *self = GEGL_VISITOR (gobject);
Packit bc1512
Packit bc1512
  g_slist_free (self->visits_list);
Packit bc1512
  g_hash_table_destroy (self->hash);
Packit bc1512
Packit bc1512
  G_OBJECT_CLASS (gegl_visitor_parent_class)->finalize (gobject);
Packit bc1512
}
Packit bc1512
Packit bc1512
static GeglVisitInfo *
Packit bc1512
lookup (GeglVisitor   *self,
Packit bc1512
        GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  return g_hash_table_lookup (self->hash, visitable);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* resets the object's data (list of visits and visitable statuses) */
Packit bc1512
void gegl_visitor_reset (GeglVisitor   *self)
Packit bc1512
{
Packit bc1512
  if (self->visits_list)
Packit bc1512
    {
Packit bc1512
      g_slist_free (self->visits_list);
Packit bc1512
      self->visits_list = NULL;
Packit bc1512
    }
Packit bc1512
  g_hash_table_remove_all (self->hash);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Inserts the visitable into the object's hash table of visitables with the
Packit bc1512
 * object as a key and a new GeglVisitInfo (zero initialised) as object
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
insert (GeglVisitor   *self,
Packit bc1512
        GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  if (lookup (self, visitable))
Packit bc1512
    {
Packit bc1512
      g_warning ("visitable already in visitor's hash table");
Packit bc1512
    }
Packit bc1512
  else
Packit bc1512
    {
Packit bc1512
      g_hash_table_insert (self->hash, visitable, g_slice_new0 (GeglVisitInfo));
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Returns TRUE if a GeglVisitable has already been visited */
Packit bc1512
static gboolean
Packit bc1512
get_visited (GeglVisitor   *self,
Packit bc1512
             GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  GeglVisitInfo *visit_info = lookup (self, visitable);
Packit bc1512
Packit bc1512
  g_assert (visit_info);
Packit bc1512
Packit bc1512
  return visit_info->visited;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
set_visited (GeglVisitor   *self,
Packit bc1512
             GeglVisitable *visitable,
Packit bc1512
             gboolean       visited)
Packit bc1512
{
Packit bc1512
  GeglVisitInfo *visit_info = lookup (self, visitable);
Packit bc1512
Packit bc1512
  g_assert (visit_info);
Packit bc1512
Packit bc1512
  visit_info->visited = visited;
Packit bc1512
}
Packit bc1512
Packit bc1512
static gboolean
Packit bc1512
get_discovered (GeglVisitor   *self,
Packit bc1512
                GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  GeglVisitInfo *visit_info = lookup (self, visitable);
Packit bc1512
Packit bc1512
  g_assert (visit_info);
Packit bc1512
Packit bc1512
  return visit_info->discovered;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
set_discovered (GeglVisitor   *self,
Packit bc1512
                GeglVisitable *visitable,
Packit bc1512
                gboolean       discovered)
Packit bc1512
{
Packit bc1512
  GeglVisitInfo *visit_info = lookup (self, visitable);
Packit bc1512
Packit bc1512
  g_assert (visit_info);
Packit bc1512
Packit bc1512
  visit_info->discovered = discovered;
Packit bc1512
}
Packit bc1512
Packit bc1512
static gint
Packit bc1512
get_shared_count (GeglVisitor   *self,
Packit bc1512
                  GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  GeglVisitInfo *visit_info = lookup (self, visitable);
Packit bc1512
Packit bc1512
  g_assert (visit_info);
Packit bc1512
Packit bc1512
  return visit_info->shared_count;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
set_shared_count (GeglVisitor   *self,
Packit bc1512
                  GeglVisitable *visitable,
Packit bc1512
                  gint           shared_count)
Packit bc1512
{
Packit bc1512
  GeglVisitInfo *visit_info = lookup (self, visitable);
Packit bc1512
Packit bc1512
  g_assert (visit_info);
Packit bc1512
Packit bc1512
  visit_info->shared_count = shared_count;
Packit bc1512
}
Packit bc1512
Packit bc1512
/**
Packit bc1512
 * gegl_visitor_get_visits_list
Packit bc1512
 * @self: a #GeglVisitor.
Packit bc1512
 *
Packit bc1512
 * Gets a list of the visitables the visitor has visited so far.
Packit bc1512
 *
Packit bc1512
 * Returns: A list of the visitables visited by this visitor.
Packit bc1512
 **/
Packit bc1512
GSList *
Packit bc1512
gegl_visitor_get_visits_list (GeglVisitor *self)
Packit bc1512
{
Packit bc1512
  g_return_val_if_fail (GEGL_IS_VISITOR (self), NULL);
Packit bc1512
Packit bc1512
  return self->visits_list;
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
visit_info_destroy (GeglVisitInfo *visit_info)
Packit bc1512
{
Packit bc1512
  g_slice_free (GeglVisitInfo, visit_info);
Packit bc1512
}
Packit bc1512
Packit bc1512
/**
Packit bc1512
 * gegl_visitor_dfs_traverse:
Packit bc1512
 * @self: #GeglVisitor
Packit bc1512
 * @visitable: the start #GeglVisitable
Packit bc1512
 *
Packit bc1512
 * Traverse depth first starting at @visitable.
Packit bc1512
 **/
Packit bc1512
void
Packit bc1512
gegl_visitor_dfs_traverse (GeglVisitor   *self,
Packit bc1512
                           GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  /* sets up the structures that keeps track of the */
Packit bc1512
  init_dfs_traversal (self, visitable);
Packit bc1512
  dfs_traverse (self, visitable);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Recursively (depth first) sets up the structure (hash) that keeps track of
Packit bc1512
 * if a visitable's status
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
init_dfs_traversal (GeglVisitor   *self,
Packit bc1512
                    GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  GSList *depends_on_list;
Packit bc1512
  GSList *llink;
Packit bc1512
Packit bc1512
  /* add the visitable to the list */
Packit bc1512
  insert (self, visitable);
Packit bc1512
  depends_on_list = gegl_visitable_depends_on (visitable);
Packit bc1512
  llink           = depends_on_list;
Packit bc1512
Packit bc1512
  while (llink)
Packit bc1512
    {
Packit bc1512
      GeglVisitable *visitable  = llink->data;
Packit bc1512
      GeglVisitInfo *visit_info = lookup (self, visitable);
Packit bc1512
Packit bc1512
      /* if the visitable doesn't have a visit_info,
Packit bc1512
       * then it needs to be initialised
Packit bc1512
       */
Packit bc1512
      if (!visit_info)
Packit bc1512
        init_dfs_traversal (self, visitable);
Packit bc1512
Packit bc1512
      llink = g_slist_next (llink);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  g_slist_free (depends_on_list);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* Recursively (depth first) traverses the visitables and call's their
Packit bc1512
 * accept methods
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
dfs_traverse (GeglVisitor   *self,
Packit bc1512
              GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  GSList *depends_on_list;
Packit bc1512
  GSList *llink;
Packit bc1512
Packit bc1512
  depends_on_list = gegl_visitable_depends_on (visitable);
Packit bc1512
  llink           = depends_on_list;
Packit bc1512
Packit bc1512
  while (llink)
Packit bc1512
    {
Packit bc1512
      GeglVisitable *visitable = llink->data;
Packit bc1512
Packit bc1512
      /* if the visitable has not yet been visitied then visit it */
Packit bc1512
      if (!get_visited (self, visitable))
Packit bc1512
        dfs_traverse (self, visitable);
Packit bc1512
Packit bc1512
      llink = g_slist_next (llink);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  g_slist_free (depends_on_list);
Packit bc1512
Packit bc1512
  /* trigger the actual visit (call the visitable's accept method that will
Packit bc1512
   * call the visitable's visit method (c.f. the visitor pattern)
Packit bc1512
   */
Packit bc1512
  gegl_visitable_accept (visitable, self);
Packit bc1512
  /* mark the visitable as already visited*/
Packit bc1512
  set_visited (self, visitable, TRUE);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
init_bfs_traversal (GeglVisitor   *self,
Packit bc1512
                    GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  GSList *depends_on_list;
Packit bc1512
  GSList *llink;
Packit bc1512
Packit bc1512
  insert (self, visitable);
Packit bc1512
Packit bc1512
  depends_on_list = gegl_visitable_depends_on (visitable);
Packit bc1512
  llink           = depends_on_list;
Packit bc1512
Packit bc1512
  while (llink)
Packit bc1512
    {
Packit bc1512
      gint           shared_count;
Packit bc1512
      GeglVisitable *depends_on_visitable = llink->data;
Packit bc1512
      GeglVisitInfo *visit_info           = lookup (self, depends_on_visitable);
Packit bc1512
Packit bc1512
      if (!visit_info)
Packit bc1512
        init_bfs_traversal (self, depends_on_visitable);
Packit bc1512
Packit bc1512
      shared_count = get_shared_count (self, depends_on_visitable);
Packit bc1512
      shared_count++;
Packit bc1512
      set_shared_count (self, depends_on_visitable, shared_count);
Packit bc1512
      llink = g_slist_next (llink);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  g_slist_free (depends_on_list);
Packit bc1512
}
Packit bc1512
Packit bc1512
/**
Packit bc1512
 * gegl_visitor_bfs_traverse:
Packit bc1512
 * @self: a #GeglVisitor
Packit bc1512
 * @visitable: the root #GeglVisitable.
Packit bc1512
 *
Packit bc1512
 * Traverse breadth-first starting at @visitable.
Packit bc1512
 **/
Packit bc1512
void
Packit bc1512
gegl_visitor_bfs_traverse (GeglVisitor   *self,
Packit bc1512
                           GeglVisitable *visitable)
Packit bc1512
{
Packit bc1512
  GQueue  queue = G_QUEUE_INIT;
Packit bc1512
Packit bc1512
  /* Init all visitables */
Packit bc1512
  init_bfs_traversal (self, visitable);
Packit bc1512
Packit bc1512
  /* Initialize the queue with this visitable */
Packit bc1512
  g_queue_push_head (&queue, visitable);
Packit bc1512
Packit bc1512
  /* Mark visitable as "discovered" */
Packit bc1512
  set_discovered (self, visitable, TRUE);
Packit bc1512
Packit bc1512
  /* Pop the top of the queue*/
Packit bc1512
  while ((visitable = g_queue_pop_head (&queue)))
Packit bc1512
    {
Packit bc1512
      gint shared_count = get_shared_count (self, visitable);
Packit bc1512
Packit bc1512
      /* Put this one at the end of the queue if its active
Packit bc1512
       * immediate parents haven't all been visited yet.
Packit bc1512
       */
Packit bc1512
      if (shared_count > 0)
Packit bc1512
        {
Packit bc1512
          g_queue_push_tail (&queue, visitable);
Packit bc1512
          continue;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* Loop through visitable's sources and examine them */
Packit bc1512
      {
Packit bc1512
        GSList *depends_on_list = gegl_visitable_depends_on (visitable);
Packit bc1512
        GSList *llink;
Packit bc1512
Packit bc1512
        for (llink = depends_on_list; llink; llink = g_slist_next (llink))
Packit bc1512
          {
Packit bc1512
            GeglVisitable *depends_on_visitable = llink->data;
Packit bc1512
Packit bc1512
            shared_count = get_shared_count (self, depends_on_visitable);
Packit bc1512
            shared_count--;
Packit bc1512
            set_shared_count (self, depends_on_visitable, shared_count);
Packit bc1512
Packit bc1512
            /* Add any undiscovered visitable to the queue at end */
Packit bc1512
            if (!get_discovered (self, depends_on_visitable))
Packit bc1512
              {
Packit bc1512
                g_queue_push_tail (&queue, depends_on_visitable);
Packit bc1512
Packit bc1512
                /* Mark it as discovered */
Packit bc1512
                set_discovered (self, depends_on_visitable, TRUE);
Packit bc1512
              }
Packit bc1512
          }
Packit bc1512
Packit bc1512
        g_slist_free (depends_on_list);
Packit bc1512
      }
Packit bc1512
Packit bc1512
      /* Visit the visitable */
Packit bc1512
      gegl_visitable_accept (visitable, self);
Packit bc1512
      set_visited (self, visitable, TRUE);
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
/* should be called by extending classes when their visit_pad function
Packit bc1512
 * is called
Packit bc1512
 */
Packit bc1512
void
Packit bc1512
gegl_visitor_visit_pad (GeglVisitor *self,
Packit bc1512
                        GeglPad     *pad)
Packit bc1512
{
Packit bc1512
  GeglVisitorClass *klass;
Packit bc1512
Packit bc1512
  klass = GEGL_VISITOR_GET_CLASS (self);
Packit bc1512
Packit bc1512
  if (klass->visit_pad)
Packit bc1512
    klass->visit_pad (self, pad);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
visit_pad (GeglVisitor *self,
Packit bc1512
           GeglPad     *pad)
Packit bc1512
{
Packit bc1512
  self->visits_list = g_slist_prepend (self->visits_list, pad);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* should be called by extending classes when their visit_node function
Packit bc1512
 * is called
Packit bc1512
 */
Packit bc1512
void
Packit bc1512
gegl_visitor_visit_node (GeglVisitor *self,
Packit bc1512
                         GeglNode    *node)
Packit bc1512
{
Packit bc1512
  GeglVisitorClass *klass;
Packit bc1512
Packit bc1512
  klass = GEGL_VISITOR_GET_CLASS (self);
Packit bc1512
Packit bc1512
  if (klass->visit_node)
Packit bc1512
    klass->visit_node (self, node);
Packit bc1512
}
Packit bc1512
Packit bc1512
/* adds the visiting node to the list of visits */
Packit bc1512
static void
Packit bc1512
visit_node (GeglVisitor *self,
Packit bc1512
            GeglNode    *node)
Packit bc1512
{
Packit bc1512
#if 0
Packit bc1512
#if ENABLE_MT
Packit bc1512
  g_mutex_lock (node->mutex);
Packit bc1512
#endif
Packit bc1512
#endif
Packit bc1512
  self->visits_list = g_slist_prepend (self->visits_list, node);
Packit bc1512
#if 0
Packit bc1512
#if ENABLE_MT
Packit bc1512
  g_mutex_unlock (node->mutex);
Packit bc1512
#endif
Packit bc1512
#endif
Packit bc1512
}