1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * dependent.c: Manage calculation dependencies between objects
5 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
6 * Copyright (C) 2006-2013 Morten Welinder (terra@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gnumeric-config.h>
25 #include "dependent.h"
27 #include "workbook-priv.h"
32 #include "expr-impl.h"
33 #include "expr-name.h"
34 #include "application.h"
35 #include "workbook-view.h"
38 #include "sheet-view.h"
41 #include <goffice/goffice.h>
44 static gboolean
gnm_cell_eval_content (GnmCell
*cell
);
45 static void dependent_changed (GnmDependent
*dep
);
46 static void dependent_clear_dynamic_deps (GnmDependent
*dep
);
48 /* ------------------------------------------------------------------------- */
51 * Note: we unconditionally use pools for
54 * since we need the ability to free en masse.
62 static GOMemChunk
*micro_few_pool
;
63 static GOMemChunk
*cset_pool
;
64 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
65 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
66 #define MICRO_HASH_FEW 3 /* Odd and small. */
67 #define NEW_FEW CHUNK_ALLOC (gpointer, micro_few_pool)
68 #define FREE_FEW(p) CHUNK_FREE (micro_few_pool, p)
70 #define CHUNK_ALLOC(T,c) g_slice_new (T)
71 #define CHUNK_FREE(p,v) g_slice_free1 (sizeof(*v),(v))
72 #define MICRO_HASH_FEW 4 /* Even and small. */
73 #define NEW_FEW (gpointer *)g_slice_alloc (MICRO_HASH_FEW * sizeof (gpointer))
74 #define FREE_FEW(p) g_slice_free1 (MICRO_HASH_FEW * sizeof (gpointer), p)
77 /* ------------------------------------------------------------------------- */
78 /* Maps between row numbers and bucket numbers. */
80 #define BUCKET_SIZE 1024
81 #define BUCKET_OF_ROW(row) ((row) / BUCKET_SIZE)
82 #define BUCKET_START_ROW(b) ((b) * BUCKET_SIZE)
83 #define BUCKET_END_ROW(b) ((b) * BUCKET_SIZE + (BUCKET_SIZE - 1))
85 /* ------------------------------------------------------------------------- */
88 #define CSET_SEGMENT_SIZE 29
90 typedef struct _CSet CSet
;
94 gpointer data
[CSET_SEGMENT_SIZE
];
95 /* And one pointer for allocation overhead. */
100 cset_find (CSet
*list
, gpointer datum
)
103 guint i
= list
->count
;
105 if (list
->data
[i
] == datum
)
114 cset_free (CSet
*list
)
117 CSet
*next
= list
->next
;
118 CHUNK_FREE (cset_pool
, list
);
123 /* NOTE: takes reference. */
125 cset_insert (CSet
**list
, gpointer datum
)
128 if (cs
== NULL
|| cs
->count
== CSET_SEGMENT_SIZE
) {
129 CSet
*h
= *list
= CHUNK_ALLOC (CSet
, cset_pool
);
134 cs
->data
[cs
->count
++] = datum
;
137 /* NOTE: takes reference. Returns TRUE if datum was already present. */
139 cset_insert_checked (CSet
**list
, gpointer datum
)
142 CSet
*nonfull
= NULL
;
146 if (i
!= CSET_SEGMENT_SIZE
)
149 if (cs
->data
[i
] == datum
)
155 nonfull
->data
[nonfull
->count
++] = datum
;
157 cset_insert (list
, datum
);
162 /* NOTE: takes reference. Returns TRUE if removed. */
164 cset_remove (CSet
**list
, gpointer datum
)
166 CSet
*l
, *last
= NULL
;
168 for (l
= *list
; l
; l
= l
->next
) {
171 for (i
= l
->count
; i
-- > 0; )
172 if (l
->data
[i
] == datum
) {
176 last
->next
= l
->next
;
179 CHUNK_FREE (cset_pool
, l
);
181 l
->data
[i
] = l
->data
[l
->count
];
189 #define CSET_FOREACH(list,var,code) \
192 for (cs_ = (list); cs_; cs_ = cs_->next) { \
194 for (i_ = cs_->count; i_-- > 0; ) { \
195 var = cs_->data[i_]; \
202 /* ------------------------------------------------------------------------- */
205 gnm_dep_set_expr_undo_undo (GnmDependent
*dep
, GnmExprTop
const *texpr
)
207 dependent_set_expr (dep
, texpr
);
208 dependent_link (dep
);
209 dependent_changed (dep
);
213 gnm_dep_set_expr_undo_new (GnmDependent
*dep
)
215 gnm_expr_top_ref (dep
->texpr
);
216 return go_undo_binary_new (dep
, (gpointer
)dep
->texpr
,
217 (GOUndoBinaryFunc
)gnm_dep_set_expr_undo_undo
,
219 (GFreeFunc
)gnm_expr_top_unref
);
223 gnm_dep_unlink_undo_new (GSList
*deps
)
225 return go_undo_unary_new (deps
,
226 (GOUndoUnaryFunc
)dependents_link
,
227 (GFreeFunc
)g_slist_free
);
230 /* ------------------------------------------------------------------------- */
232 #undef DEBUG_EVALUATION
235 dummy_dep_eval (G_GNUC_UNUSED GnmDependent
*dep
)
239 static void cell_dep_eval (GnmDependent
*dep
);
240 static void cell_dep_set_expr (GnmDependent
*dep
, GnmExprTop
const *new_texpr
);
241 static GSList
*cell_dep_changed (GnmDependent
*dep
);
242 static GnmCellPos
*cell_dep_pos (GnmDependent
const *dep
);
243 static void cell_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
244 static const GnmDependentClass cell_dep_class
= {
252 static GSList
*dynamic_dep_changed (GnmDependent
*dep
);
253 static void dynamic_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
254 static const GnmDependentClass dynamic_dep_class
= {
259 dynamic_dep_debug_name
,
263 GnmDependent
*container
;
268 static void name_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
269 static const GnmDependentClass name_dep_class
= {
277 static void managed_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
278 static const GnmDependentClass managed_dep_class
= {
283 managed_dep_debug_name
,
286 static void style_dep_eval (GnmDependent
*dep
);
287 static GSList
*style_dep_changed (GnmDependent
*dep
);
288 static GnmCellPos
*style_dep_pos (GnmDependent
const *dep
);
289 static void style_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
290 static const GnmDependentClass style_dep_class
= {
295 style_dep_debug_name
,
303 static GPtrArray
*dep_classes
= NULL
;
306 dependent_types_init (void)
308 g_return_if_fail (dep_classes
== NULL
);
310 /* Init with a NULL class so we can access directly */
311 dep_classes
= g_ptr_array_new ();
312 g_ptr_array_add (dep_classes
, NULL
); /* bogus filler */
313 g_ptr_array_add (dep_classes
, (gpointer
)&cell_dep_class
);
314 g_ptr_array_add (dep_classes
, (gpointer
)&dynamic_dep_class
);
315 g_ptr_array_add (dep_classes
, (gpointer
)&name_dep_class
);
316 g_ptr_array_add (dep_classes
, (gpointer
)&managed_dep_class
);
317 g_ptr_array_add (dep_classes
, (gpointer
)&style_dep_class
);
321 go_mem_chunk_new ("micro few pool",
322 MICRO_HASH_FEW
* sizeof (gpointer
),
325 go_mem_chunk_new ("cset pool",
332 dependent_types_shutdown (void)
334 g_return_if_fail (dep_classes
!= NULL
);
335 g_ptr_array_free (dep_classes
, TRUE
);
339 go_mem_chunk_destroy (micro_few_pool
, FALSE
);
340 micro_few_pool
= NULL
;
341 go_mem_chunk_destroy (cset_pool
, FALSE
);
347 * dependent_register_type :
350 * Store the vtable and allocate an ID for a new class
354 dependent_type_register (GnmDependentClass
const *klass
)
358 g_return_val_if_fail (dep_classes
!= NULL
, 0);
360 g_ptr_array_add (dep_classes
, (gpointer
)klass
);
361 res
= dep_classes
->len
-1;
363 g_return_val_if_fail (res
<= DEPENDENT_TYPE_MASK
, res
);
369 * dependent_flag_recalc:
370 * @dep: the dependent that contains the expression needing recomputation.
372 * Marks @dep as needing recalculation
373 * NOTE : it does NOT recursively dirty dependencies.
375 #define dependent_flag_recalc(dep) \
376 do { (dep)->flags |= DEPENDENT_NEEDS_RECALC; } while (0)
380 * @cell: the dependent that changed
385 dependent_changed (GnmDependent
*dep
)
388 dep
->sheet
->workbook
->recursive_dirty_enabled
)
389 dependent_queue_recalc (dep
);
391 dependent_flag_recalc (dep
);
395 * dependent_set_expr:
396 * @dep: The dependent we are interested in.
397 * @new_texpr: new expression.
399 * When the expression associated with @dep needs to change this routine
400 * dispatches to the virtual handler, unlinking @dep if necessary beforehand.
401 * Adds a ref to @new_expr.
403 * NOTE : it does NOT relink dependents in case they are going to move later.
406 dependent_set_expr (GnmDependent
*dep
, GnmExprTop
const *new_texpr
)
408 int const t
= dependent_type (dep
);
409 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
411 if (dependent_is_linked (dep
))
412 dependent_unlink (dep
);
413 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
)
414 dependent_clear_dynamic_deps (dep
);
417 klass
->set_expr (dep
, new_texpr
);
420 gnm_expr_top_ref (new_texpr
);
422 gnm_expr_top_unref (dep
->texpr
);
423 dep
->texpr
= new_texpr
;
425 dependent_changed (dep
);
430 dependent_has_pos (GnmDependent
const *dep
)
432 int const t
= dependent_type (dep
);
433 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
434 return klass
->pos
!= NULL
;
438 dependent_pos (GnmDependent
const *dep
)
440 static GnmCellPos
const dummy
= { 0, 0 };
441 int const t
= dependent_type (dep
);
442 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
444 return klass
->pos
? klass
->pos (dep
) : &dummy
;
448 dependent_move (GnmDependent
*dep
, int dx
, int dy
)
450 int const t
= dependent_type (dep
);
451 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
454 g_return_if_fail (klass
->pos
!= NULL
);
455 pos
= klass
->pos (dep
);
456 /* Need a virtual for this? */
463 * dependent_set_sheet:
468 dependent_set_sheet (GnmDependent
*dep
, Sheet
*sheet
)
470 g_return_if_fail (dep
!= NULL
);
471 g_return_if_fail (dep
->sheet
== NULL
);
472 g_return_if_fail (!dependent_is_linked (dep
));
476 dependent_link (dep
);
477 dependent_changed (dep
);
482 cb_cell_list_deps (GnmDependent
*dep
, gpointer user
)
484 GSList
**list
= (GSList
**)user
;
485 *list
= g_slist_prepend (*list
, dep
);
489 cell_list_deps (GnmCell
const *cell
)
492 cell_foreach_dep (cell
, cb_cell_list_deps
, &deps
);
497 dependent_queue_recalc_main (GSList
*work
)
500 * Work is now a list of marked cells whose dependencies need
501 * to be marked. Marking early guarentees that we will not
502 * get duplicates. (And it thus limits the length of the list.)
503 * We treat work as a stack.
507 GnmDependent
*dep
= work
->data
;
508 int const t
= dependent_type (dep
);
509 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
511 /* Pop the top element. */
512 work
= g_slist_delete_link (work
, work
);
514 if (klass
->changed
) {
515 GSList
*extra
= klass
->changed (dep
);
517 g_slist_last (extra
)->next
= work
;
526 * dependent_queue_recalc_list :
529 * Queues any elements of @list for recalc that are not already queued,
530 * and marks all elements as needing a recalc.
533 dependent_queue_recalc_list (GSList
*list
)
537 for (; list
!= NULL
; list
= list
->next
) {
538 GnmDependent
*dep
= list
->data
;
539 if (!dependent_needs_recalc (dep
)) {
540 dependent_flag_recalc (dep
);
541 work
= g_slist_prepend (work
, dep
);
545 dependent_queue_recalc_main (work
);
549 dependent_queue_recalc (GnmDependent
*dep
)
551 g_return_if_fail (dep
!= NULL
);
553 #ifdef DEBUG_EVALUATION
554 g_printerr ("/* QUEUE (%s) */\n", cell_name (GNM_DEP_TO_CELL (dep
)));
556 if (!dependent_needs_recalc (dep
)) {
560 dependent_queue_recalc_list (&listrec
);
564 /**************************************************************************/
576 #define MICRO_HASH_MIN_SIZE 11
577 #define MICRO_HASH_MAX_SIZE 13845163
579 #define MICRO_HASH_hash(key) ((guint)GPOINTER_TO_UINT(key))
582 micro_hash_many_to_few (MicroHash
*hash_table
)
584 CSet
**buckets
= hash_table
->u
.many
;
585 int nbuckets
= hash_table
->num_buckets
;
588 hash_table
->u
.few
= NEW_FEW
;
590 while (nbuckets
-- > 0 ) {
593 CSET_FOREACH (buckets
[nbuckets
], datum
, {
594 hash_table
->u
.few
[i
++] = datum
;
596 cset_free (buckets
[nbuckets
]);
603 micro_hash_many_resize (MicroHash
*hash_table
, int new_nbuckets
)
605 CSet
**buckets
= hash_table
->u
.many
;
606 int nbuckets
= hash_table
->num_buckets
;
607 CSet
**new_buckets
= g_new0 (CSet
*, new_nbuckets
);
609 hash_table
->u
.many
= new_buckets
;
610 hash_table
->num_buckets
= new_nbuckets
;
612 while (nbuckets
-- > 0 ) {
615 CSET_FOREACH (buckets
[nbuckets
], datum
, {
616 guint bucket
= MICRO_HASH_hash (datum
) % new_nbuckets
;
617 cset_insert (&(new_buckets
[bucket
]), datum
);
619 cset_free (buckets
[nbuckets
]);
626 int capacity
= 0, totlen
= 0;
629 for (i
= 0; i
< new_nbuckets
; i
++) {
630 CSet
*cs
= new_buckets
[i
];
635 capacity
+= CSET_SEGMENT_SIZE
;
641 g_printerr ("resize %p: %d [%d %.1f %.0f%%]\n",
644 hash_table
->num_elements
,
645 (double)totlen
/ nonzero
,
646 100.0 * totlen
/ capacity
);
653 micro_hash_few_to_many (MicroHash
*hash_table
)
655 int nbuckets
= hash_table
->num_buckets
= MICRO_HASH_MIN_SIZE
;
656 CSet
**buckets
= g_new0 (CSet
*, nbuckets
);
659 for (i
= 0; i
< hash_table
->num_elements
; i
++) {
660 gpointer datum
= hash_table
->u
.few
[i
];
661 guint bucket
= MICRO_HASH_hash (datum
) % nbuckets
;
662 cset_insert (&(buckets
[bucket
]), datum
);
664 FREE_FEW (hash_table
->u
.few
);
665 hash_table
->u
.many
= buckets
;
671 micro_hash_insert (MicroHash
*hash_table
, gpointer key
)
673 int N
= hash_table
->num_elements
;
675 g_return_if_fail (key
!= NULL
);
678 hash_table
->u
.one
= key
;
680 gpointer key0
= hash_table
->u
.one
;
684 hash_table
->u
.few
= NEW_FEW
;
685 hash_table
->u
.few
[0] = key0
;
686 hash_table
->u
.few
[1] = key
;
687 memset (hash_table
->u
.few
+ 2, 0, (MICRO_HASH_FEW
- 2) * sizeof (gpointer
));
688 } else if (N
<= MICRO_HASH_FEW
) {
691 for (i
= 0; i
< N
; i
++)
692 if (hash_table
->u
.few
[i
] == key
)
695 if (N
== MICRO_HASH_FEW
) {
698 micro_hash_few_to_many (hash_table
);
699 bucket
= MICRO_HASH_hash (key
) % hash_table
->num_buckets
;
700 cset_insert (&(hash_table
->u
.many
[bucket
]), key
);
702 hash_table
->u
.few
[N
] = key
;
704 int nbuckets
= hash_table
->num_buckets
;
705 guint bucket
= MICRO_HASH_hash (key
) % nbuckets
;
706 CSet
**buckets
= hash_table
->u
.many
;
708 if (cset_insert_checked (&(buckets
[bucket
]), key
))
711 if (N
> CSET_SEGMENT_SIZE
* nbuckets
&&
712 nbuckets
< MICRO_HASH_MAX_SIZE
) {
713 int new_nbuckets
= g_spaced_primes_closest (N
/ (CSET_SEGMENT_SIZE
/ 2));
714 if (new_nbuckets
> MICRO_HASH_MAX_SIZE
)
715 new_nbuckets
= MICRO_HASH_MAX_SIZE
;
716 micro_hash_many_resize (hash_table
, new_nbuckets
);
720 hash_table
->num_elements
++;
724 micro_hash_remove (MicroHash
*hash_table
, gpointer key
)
726 int N
= hash_table
->num_elements
;
733 if (hash_table
->u
.one
!= key
)
735 hash_table
->u
.one
= NULL
;
736 hash_table
->num_elements
--;
740 if (N
<= MICRO_HASH_FEW
) {
743 for (i
= 0; i
< N
; i
++)
744 if (hash_table
->u
.few
[i
] == key
) {
745 hash_table
->u
.few
[i
] = hash_table
->u
.few
[N
- 1];
746 hash_table
->num_elements
--;
747 if (hash_table
->num_elements
> 1)
750 key
= hash_table
->u
.few
[0];
751 FREE_FEW (hash_table
->u
.few
);
752 hash_table
->u
.one
= key
;
758 bucket
= MICRO_HASH_hash (key
) % hash_table
->num_buckets
;
759 if (cset_remove (&(hash_table
->u
.many
[bucket
]), key
)) {
760 hash_table
->num_elements
--;
762 if (hash_table
->num_elements
<= MICRO_HASH_FEW
)
763 micro_hash_many_to_few (hash_table
);
773 micro_hash_release (MicroHash
*hash_table
)
775 int N
= hash_table
->num_elements
;
779 else if (N
<= MICRO_HASH_FEW
)
780 FREE_FEW (hash_table
->u
.few
);
782 guint i
= hash_table
->num_buckets
;
784 cset_free (hash_table
->u
.many
[i
]);
785 g_free (hash_table
->u
.many
);
787 hash_table
->num_elements
= 0;
788 hash_table
->num_buckets
= 1;
789 hash_table
->u
.one
= NULL
;
793 micro_hash_init (MicroHash
*hash_table
, gpointer key
)
795 hash_table
->num_elements
= 1;
796 hash_table
->u
.one
= key
;
799 static inline gboolean
800 micro_hash_is_empty (MicroHash
const *hash_table
)
802 return hash_table
->num_elements
== 0;
805 /*************************************************************************/
807 #define micro_hash_foreach_dep(dc, dep, code) do { \
808 guint i_ = dc.num_elements; \
809 if (i_ <= MICRO_HASH_FEW) { \
810 const gpointer *e_ = (i_ == 1) ? &dc.u.one : dc.u.few; \
812 GnmDependent *dep = e_[i_]; \
817 guint b_ = dc.num_buckets; \
819 CSET_FOREACH (dc.u.many[b_], dep, code); \
823 /**************************************************************************
824 * Data structures for managing dependencies between objects.
826 * The DependencyRange hash needs to be improved. It is a huge
827 * performance hit when there are large numbers of range depends.
831 * A DependencyRange defines a range of cells whose values
832 * are used by another objects in the spreadsheet.
834 * A change in those cells will trigger a recomputation on the
835 * cells listed in deps.
838 MicroHash deps
; /* Must be first */
843 * A DependencySingle stores a list of dependents that rely
844 * on the cell at @pos.
846 * A change in this cell will trigger a recomputation on the
847 * cells listed in deps.
850 MicroHash deps
; /* Must be first */
856 MicroHash deps
; /* Must be first */
860 deprange_hash (DependencyRange
const *r
)
862 guint a
= r
->range
.start
.row
;
863 guint b
= r
->range
.end
.row
;
864 guint c
= r
->range
.start
.col
;
865 guint d
= r
->range
.end
.col
;
867 return (((((a
<< 8) + b
) << 8) + c
) << 8) + d
;
871 deprange_equal (DependencyRange
const *r1
, DependencyRange
const *r2
)
873 return range_equal (&(r1
->range
), &(r2
->range
));
877 depsingle_hash (DependencySingle
const *depsingle
)
879 return (depsingle
->pos
.row
<< 8) ^ depsingle
->pos
.col
;
883 depsingle_equal (DependencySingle
const *a
, DependencySingle
const *b
)
885 return (a
->pos
.row
== b
->pos
.row
&& a
->pos
.col
== b
->pos
.col
);
888 static GnmDependentFlags
889 link_single_dep (GnmDependent
*dep
, GnmCellPos
const *pos
, GnmCellRef
const *ref
)
891 DependencySingle lookup
;
892 DependencySingle
*single
;
893 GnmDependentFlags flag
= DEPENDENT_NO_FLAG
;
894 Sheet
const *sheet
= eval_sheet (ref
->sheet
, dep
->sheet
);
895 GnmDepContainer
*deps
= sheet
->deps
;
897 if (sheet
!= dep
->sheet
)
898 flag
= (sheet
->workbook
!= dep
->sheet
->workbook
)
899 ? DEPENDENT_GOES_INTERBOOK
900 : DEPENDENT_GOES_INTERSHEET
;
902 /* Inserts if it is not already there */
903 gnm_cellpos_init_cellref (&lookup
.pos
, ref
, pos
, sheet
);
904 single
= g_hash_table_lookup (deps
->single_hash
, &lookup
);
905 if (single
== NULL
) {
906 single
= go_mem_chunk_alloc (deps
->single_pool
);
908 micro_hash_init (&single
->deps
, dep
);
909 g_hash_table_insert (deps
->single_hash
, single
, single
);
911 micro_hash_insert (&single
->deps
, dep
);
916 static GnmDependentFlags
917 unlink_single_dep (GnmDependent
*dep
, GnmCellPos
const *pos
, GnmCellRef
const *a
)
919 DependencySingle lookup
;
920 DependencySingle
*single
;
921 GnmDependentFlags flag
= DEPENDENT_NO_FLAG
;
922 Sheet
const *sheet
= eval_sheet (a
->sheet
, dep
->sheet
);
923 GnmDepContainer
*deps
= sheet
->deps
;
925 if (sheet
!= dep
->sheet
)
926 flag
= (sheet
->workbook
!= dep
->sheet
->workbook
)
927 ? DEPENDENT_GOES_INTERBOOK
928 : DEPENDENT_GOES_INTERSHEET
;
932 gnm_cellpos_init_cellref (&lookup
.pos
, a
, pos
, sheet
);
933 single
= g_hash_table_lookup (deps
->single_hash
, &lookup
);
934 if (single
!= NULL
) {
935 micro_hash_remove (&single
->deps
, dep
);
936 if (micro_hash_is_empty (&single
->deps
)) {
937 g_hash_table_remove (deps
->single_hash
, single
);
938 micro_hash_release (&single
->deps
);
939 go_mem_chunk_free (deps
->single_pool
, single
);
946 static inline GnmDependentFlags
947 link_unlink_single_dep (GnmDependent
*dep
, GnmCellPos
const *pos
,
948 GnmCellRef
const *a
, gboolean qlink
)
951 ? link_single_dep (dep
, pos
, a
)
952 : unlink_single_dep (dep
, pos
, a
);
957 link_range_dep (GnmDepContainer
*deps
, GnmDependent
*dep
,
958 DependencyRange
const *r
)
960 int i
= BUCKET_OF_ROW (r
->range
.start
.row
);
961 int end
= BUCKET_OF_ROW (r
->range
.end
.row
);
962 DependencyRange r2
= *r
;
965 * It is possible to see ranges bigger than the sheet when
966 * operating with 3D ranges. See bug #704109.
968 end
= MIN (end
, deps
->buckets
- 1);
970 for ( ; i
<= end
; i
++) {
971 DependencyRange
*result
;
973 /* Restrict range to bucket. */
974 r2
.range
.start
.row
= MAX (r
->range
.start
.row
, BUCKET_START_ROW (i
));
975 r2
.range
.end
.row
= MIN (r
->range
.end
.row
, BUCKET_END_ROW (i
));
977 if (deps
->range_hash
[i
] == NULL
)
978 deps
->range_hash
[i
] = g_hash_table_new (
979 (GHashFunc
) deprange_hash
,
980 (GEqualFunc
) deprange_equal
);
982 result
= g_hash_table_lookup (deps
->range_hash
[i
], &r2
);
984 /* Inserts if it is not already there */
985 micro_hash_insert (&result
->deps
, dep
);
990 /* Create a new DependencyRange structure */
991 result
= go_mem_chunk_alloc (deps
->range_pool
);
993 micro_hash_init (&result
->deps
, dep
);
994 g_hash_table_insert (deps
->range_hash
[i
], result
, result
);
999 unlink_range_dep (GnmDepContainer
*deps
, GnmDependent
*dep
,
1000 DependencyRange
const *r
)
1002 int i
= BUCKET_OF_ROW (r
->range
.start
.row
);
1003 int end
= BUCKET_OF_ROW (r
->range
.end
.row
);
1004 DependencyRange r2
= *r
;
1009 end
= MIN (end
, deps
->buckets
- 1);
1011 for ( ; i
<= end
; i
++) {
1012 DependencyRange
*result
;
1014 /* Restrict range to bucket. */
1015 r2
.range
.start
.row
= MAX (r
->range
.start
.row
, BUCKET_START_ROW (i
));
1016 r2
.range
.end
.row
= MIN (r
->range
.end
.row
, BUCKET_END_ROW (i
));
1018 result
= g_hash_table_lookup (deps
->range_hash
[i
], &r2
);
1020 micro_hash_remove (&result
->deps
, dep
);
1021 if (micro_hash_is_empty (&result
->deps
)) {
1022 g_hash_table_remove (deps
->range_hash
[i
], result
);
1023 micro_hash_release (&result
->deps
);
1024 go_mem_chunk_free (deps
->range_pool
, result
);
1031 link_unlink_range_dep (GnmDepContainer
*deps
, GnmDependent
*dep
,
1032 DependencyRange
const *r
, gboolean qlink
)
1035 link_range_dep (deps
, dep
, r
);
1037 unlink_range_dep (deps
, dep
, r
);
1040 static GnmDependentFlags
1041 link_unlink_cellrange_dep (GnmDependent
*dep
, GnmCellPos
const *pos
,
1042 GnmCellRef
const *a
, GnmCellRef
const *b
,
1045 DependencyRange range
;
1046 GnmDependentFlags flag
= DEPENDENT_NO_FLAG
;
1048 gnm_cellpos_init_cellref (&range
.range
.start
, a
, pos
, dep
->sheet
);
1049 gnm_cellpos_init_cellref (&range
.range
.end
, b
, pos
, dep
->sheet
);
1050 range_normalize (&range
.range
);
1052 if (a
->sheet
!= NULL
) {
1053 if (a
->sheet
!= dep
->sheet
)
1054 flag
= (a
->sheet
->workbook
!= dep
->sheet
->workbook
)
1055 ? DEPENDENT_GOES_INTERBOOK
: DEPENDENT_GOES_INTERSHEET
;
1057 if (b
->sheet
!= NULL
&& a
->sheet
!= b
->sheet
) {
1058 Workbook
const *wb
= a
->sheet
->workbook
;
1059 int i
= a
->sheet
->index_in_wb
;
1060 int stop
= b
->sheet
->index_in_wb
;
1061 if (i
> stop
) { int tmp
= i
; i
= stop
; stop
= tmp
; }
1063 g_return_val_if_fail (b
->sheet
->workbook
== wb
, flag
);
1066 Sheet
*sheet
= g_ptr_array_index (wb
->sheets
, i
);
1068 link_unlink_range_dep (sheet
->deps
, dep
, &range
,
1071 flag
|= DEPENDENT_HAS_3D
;
1073 link_unlink_range_dep (a
->sheet
->deps
, dep
, &range
,
1076 link_unlink_range_dep (dep
->sheet
->deps
, dep
, &range
, qlink
);
1081 static GnmDependentFlags
1082 link_unlink_expr_dep (GnmEvalPos
*ep
, GnmExpr
const *tree
, gboolean qlink
)
1084 g_return_val_if_fail (tree
!= NULL
, DEPENDENT_NO_FLAG
);
1086 switch (GNM_EXPR_GET_OPER (tree
)) {
1087 case GNM_EXPR_OP_RANGE_CTOR
: /* See #562363 */
1088 case GNM_EXPR_OP_INTERSECT
:
1089 case GNM_EXPR_OP_ANY_BINARY
:
1090 return link_unlink_expr_dep (ep
, tree
->binary
.value_a
, qlink
) |
1091 link_unlink_expr_dep (ep
, tree
->binary
.value_b
, qlink
);
1092 case GNM_EXPR_OP_ANY_UNARY
:
1093 return link_unlink_expr_dep (ep
, tree
->unary
.value
, qlink
);
1094 case GNM_EXPR_OP_CELLREF
:
1095 return link_unlink_single_dep (ep
->dep
, dependent_pos (ep
->dep
), &tree
->cellref
.ref
, qlink
);
1097 case GNM_EXPR_OP_CONSTANT
:
1098 /* TODO: pass in eval flags so that we can use implicit
1101 if (VALUE_IS_CELLRANGE (tree
->constant
.value
))
1102 return link_unlink_cellrange_dep
1103 (ep
->dep
, dependent_pos (ep
->dep
),
1104 &tree
->constant
.value
->v_range
.cell
.a
,
1105 &tree
->constant
.value
->v_range
.cell
.b
,
1107 return DEPENDENT_NO_FLAG
;
1110 * FIXME: needs to be taught implicit intersection +
1111 * more cunning handling of argument type matching.
1113 case GNM_EXPR_OP_FUNCALL
: {
1115 GnmDependentFlags flag
= DEPENDENT_NO_FLAG
;
1116 if (tree
->func
.func
->fn_type
== GNM_FUNC_TYPE_STUB
)
1117 gnm_func_load_stub (tree
->func
.func
);
1118 if (tree
->func
.func
->linker
) {
1119 GnmFuncEvalInfo fei
;
1121 fei
.func_call
= &tree
->func
;
1122 flag
= tree
->func
.func
->linker (&fei
, qlink
);
1124 if (!(flag
& DEPENDENT_IGNORE_ARGS
))
1125 for (i
= 0; i
< tree
->func
.argc
; i
++)
1126 flag
|= link_unlink_expr_dep (ep
, tree
->func
.argv
[i
], qlink
);
1130 case GNM_EXPR_OP_NAME
:
1132 expr_name_add_dep (tree
->name
.name
, ep
->dep
);
1134 expr_name_remove_dep (tree
->name
.name
, ep
->dep
);
1135 if (expr_name_is_active (tree
->name
.name
))
1136 return link_unlink_expr_dep (ep
, tree
->name
.name
->texpr
->expr
, qlink
) | DEPENDENT_USES_NAME
;
1137 return DEPENDENT_USES_NAME
;
1139 case GNM_EXPR_OP_ARRAY_ELEM
: {
1140 /* Non-corner cells depend on the corner */
1142 GnmCellPos
const *pos
= dependent_pos (ep
->dep
);
1143 // We cannot support array expressions unless we have
1145 g_return_val_if_fail (pos
!= NULL
, DEPENDENT_NO_FLAG
);
1147 a
.col_relative
= a
.row_relative
= FALSE
;
1148 a
.sheet
= ep
->dep
->sheet
;
1149 a
.col
= pos
->col
- tree
->array_elem
.x
;
1150 a
.row
= pos
->row
- tree
->array_elem
.y
;
1152 return link_unlink_single_dep (ep
->dep
, pos
, &a
, qlink
);
1155 case GNM_EXPR_OP_ARRAY_CORNER
: {
1156 // It's mildly unclean to do this here. We need the texpr, so get the cell.
1157 GnmCellPos
const *cpos
= dependent_pos (ep
->dep
);
1158 GnmCell
const *cell
= sheet_cell_get (ep
->dep
->sheet
, cpos
->col
, cpos
->row
);
1159 GnmEvalPos pos
= *ep
;
1160 pos
.array_texpr
= cell
->base
.texpr
;
1161 /* Corner cell depends on the contents of the expr */
1162 return link_unlink_expr_dep (&pos
, tree
->array_corner
.expr
, qlink
);
1165 case GNM_EXPR_OP_SET
: {
1167 GnmDependentFlags res
= DEPENDENT_NO_FLAG
;
1169 for (i
= 0; i
< tree
->set
.argc
; i
++)
1170 res
|= link_unlink_expr_dep (ep
, tree
->set
.argv
[i
], qlink
);
1173 #ifndef DEBUG_SWITCH_ENUM
1175 g_assert_not_reached ();
1178 return DEPENDENT_NO_FLAG
;
1182 workbook_link_3d_dep (GnmDependent
*dep
)
1184 Workbook
*wb
= dep
->sheet
->workbook
;
1186 if (wb
->being_reordered
)
1188 if (wb
->sheet_order_dependents
== NULL
)
1189 wb
->sheet_order_dependents
=
1190 g_hash_table_new (g_direct_hash
, g_direct_equal
);
1191 g_hash_table_insert (wb
->sheet_order_dependents
, dep
, dep
);
1195 workbook_unlink_3d_dep (GnmDependent
*dep
)
1197 Workbook
*wb
= dep
->sheet
->workbook
;
1199 /* during destruction */
1200 if (wb
->sheet_order_dependents
== NULL
)
1203 if (wb
->being_reordered
)
1206 g_hash_table_remove (wb
->sheet_order_dependents
, dep
);
1210 * gnm_dep_style_dependency:
1214 * @accum: (inout) (transfer full) (element-type GnmDependent):
1217 gnm_dep_style_dependency (Sheet
*sheet
,
1218 GnmExprTop
const *texpr
,
1225 * FIXME: Maybe do better for an expression that is just an
1229 for (row
= r
->start
.row
; row
<= r
->end
.row
; row
++) {
1230 for (col
= r
->start
.col
; col
<= r
->end
.col
; col
++) {
1231 GnmStyleDependent
*sd
= g_new0 (GnmStyleDependent
, 1);
1232 GnmDependent
*dep
= &sd
->base
;
1235 dep
->flags
= DEPENDENT_STYLE
;
1240 dependent_set_expr (dep
, texpr
);
1241 dependent_link (dep
);
1242 g_ptr_array_add (accum
, dep
);
1247 /*****************************************************************************/
1250 cell_dep_eval (GnmDependent
*dep
)
1252 gboolean finished
= gnm_cell_eval_content (GNM_DEP_TO_CELL (dep
));
1253 (void)finished
; /* We don't currently care */
1254 dep
->flags
&= ~GNM_CELL_HAS_NEW_EXPR
;
1258 cell_dep_set_expr (GnmDependent
*dep
, GnmExprTop
const *new_texpr
)
1261 * Explicitly do not check for array subdivision, we may be
1262 * replacing the corner of an array.
1264 gnm_cell_set_expr_unsafe (GNM_DEP_TO_CELL (dep
), new_texpr
);
1268 cell_dep_changed (GnmDependent
*dep
)
1270 /* When a cell changes, so do its dependents. */
1272 GSList
*deps
= cell_list_deps (GNM_DEP_TO_CELL (dep
));
1273 GSList
*waste
= NULL
, *work
= NULL
;
1274 GSList
*next
, *list
;
1275 for (list
= deps
; list
!= NULL
; list
= next
) {
1276 GnmDependent
*dep
= list
->data
;
1278 if (dependent_needs_recalc (dep
)) {
1282 dependent_flag_recalc (dep
);
1287 g_slist_free (waste
);
1292 cell_dep_pos (GnmDependent
const *dep
)
1294 return &GNM_DEP_TO_CELL (dep
)->pos
;
1298 cell_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1300 g_string_append (target
, cell_name (GNM_DEP_TO_CELL (dep
)));
1303 /*****************************************************************************/
1305 * "Managed" dependent handling.
1307 * A managed dependent is simply an expression set up to be maintained by
1308 * the dependency system so it follows row/column insert/delete nicely.
1310 * The expression being managed is typically a cell range, but can be any
1311 * expression. Everything is interpreted relative to A1 in the sheet.
1315 dependent_managed_init (GnmDependent
*dep
, Sheet
*sheet
)
1317 memset (dep
, 0, sizeof (*dep
));
1318 dep
->flags
= DEPENDENT_MANAGED
;
1323 dependent_managed_set_expr (GnmDependent
*dep
, GnmExprTop
const *texpr
)
1325 g_return_if_fail (dep
!= NULL
);
1327 /* We need some kind of IS_A */
1328 g_return_if_fail (dependent_type (dep
) == DEPENDENT_MANAGED
);
1331 dependent_set_expr (dep
, texpr
);
1332 if (texpr
&& dep
->sheet
)
1333 dependent_link (dep
);
1337 dependent_managed_set_sheet (GnmDependent
*dep
, Sheet
*sheet
)
1339 GnmExprTop
const *texpr
;
1341 g_return_if_fail (dep
!= NULL
);
1343 /* We need some kind of IS_A */
1344 g_return_if_fail (dependent_type (dep
) == DEPENDENT_MANAGED
);
1347 if (dep
->sheet
== sheet
)
1351 if (texpr
) gnm_expr_top_ref (texpr
);
1352 dependent_set_expr (dep
, NULL
);
1353 /* We're now unlinked from everything. */
1355 dependent_managed_set_expr (dep
, texpr
);
1356 if (texpr
) gnm_expr_top_unref (texpr
);
1360 managed_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1362 g_string_append_printf (target
, "Managed%p", (void *)dep
);
1365 /*****************************************************************************/
1368 debug_style_deps (void)
1370 static int debug
= -1;
1372 debug
= gnm_debug_flag ("style-deps");
1377 style_dep_unrender (GnmDependent
*dep
, const char *what
)
1379 GnmCellPos
const *pos
= dependent_pos (dep
);
1381 Sheet
*sheet
= dep
->sheet
;
1383 if (debug_style_deps ())
1384 g_printerr ("StyleDep %p at %s %s\n",
1385 dep
, cellpos_as_string (pos
), what
);
1388 * If the cell exists, unrender it so format changes can take
1391 cell
= sheet_cell_get (sheet
, pos
->col
, pos
->row
);
1393 gnm_cell_unrender (cell
);
1395 sheet_redraw_region (sheet
,
1397 pos
->col
, pos
->row
);
1401 style_dep_eval (GnmDependent
*dep
)
1404 * It is possible that the cell has been rendered between ::changed
1405 * was called and now.
1407 style_dep_unrender (dep
, "being evaluated");
1411 style_dep_changed (GnmDependent
*dep
)
1413 style_dep_unrender (dep
, "changed");
1418 style_dep_pos (GnmDependent
const *dep
)
1420 return &((GnmStyleDependent
*)dep
)->pos
;
1424 style_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1426 g_string_append_printf (target
, "StyleDep@%s[%p]",
1427 cellpos_as_string (style_dep_pos (dep
)),
1431 /*****************************************************************************/
1434 name_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1436 g_string_append_printf (target
, "Name%p", (void *)dep
);
1439 /*****************************************************************************/
1442 dynamic_dep_changed (GnmDependent
*dep
)
1444 DynamicDep
const *dyn
= (DynamicDep
*)dep
;
1446 /* When a dynamic dependent changes, we mark its container. */
1448 if (dependent_needs_recalc (dyn
->container
))
1451 dependent_flag_recalc (dyn
->container
);
1452 return g_slist_prepend (NULL
, dyn
->container
);
1456 dynamic_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1458 g_string_append_printf (target
, "DynamicDep%p", (void *)dep
);
1462 dependent_add_dynamic_dep (GnmDependent
*dep
, GnmRangeRef
const *rr
)
1464 GnmDependentFlags flags
;
1466 GnmCellPos
const *pos
;
1467 DependencyRange range
;
1469 g_return_if_fail (dep
!= NULL
);
1471 pos
= dependent_pos (dep
);
1473 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
)
1474 dyn
= g_hash_table_lookup (dep
->sheet
->deps
->dynamic_deps
, dep
);
1476 dep
->flags
|= DEPENDENT_HAS_DYNAMIC_DEPS
;
1477 dyn
= g_new (DynamicDep
, 1);
1478 dyn
->base
.flags
= DEPENDENT_DYNAMIC_DEP
;
1479 dyn
->base
.sheet
= dep
->sheet
;
1480 dyn
->base
.texpr
= NULL
;
1481 dyn
->container
= dep
;
1483 dyn
->singles
= NULL
;
1484 g_hash_table_insert (dep
->sheet
->deps
->dynamic_deps
, dep
, dyn
);
1487 gnm_cellpos_init_cellref (&range
.range
.start
, &rr
->a
, pos
, dep
->sheet
);
1488 gnm_cellpos_init_cellref (&range
.range
.end
, &rr
->b
, pos
, dep
->sheet
);
1489 if (range_is_singleton (&range
.range
)) {
1490 flags
= link_single_dep (&dyn
->base
, pos
, &rr
->a
);
1491 dyn
->singles
= g_slist_prepend (dyn
->singles
, gnm_rangeref_dup (rr
));
1493 flags
= link_unlink_cellrange_dep (&dyn
->base
, pos
, &rr
->a
, &rr
->b
, TRUE
);
1494 dyn
->ranges
= g_slist_prepend (dyn
->ranges
, gnm_rangeref_dup (rr
));
1496 if (flags
& DEPENDENT_HAS_3D
)
1497 workbook_link_3d_dep (dep
);
1501 dependent_clear_dynamic_deps (GnmDependent
*dep
)
1503 g_hash_table_remove (dep
->sheet
->deps
->dynamic_deps
, dep
);
1506 /*****************************************************************************/
1510 * @dep: the dependent that changed
1512 * Adds the dependent to the workbook wide list of dependents.
1515 dependent_link (GnmDependent
*dep
)
1520 g_return_if_fail (dep
!= NULL
);
1521 g_return_if_fail (dep
->texpr
!= NULL
);
1522 g_return_if_fail (!(dep
->flags
& DEPENDENT_IS_LINKED
));
1523 g_return_if_fail (IS_SHEET (dep
->sheet
));
1524 g_return_if_fail (dep
->sheet
->deps
!= NULL
);
1528 /* Make this the new tail of the dependent list. */
1529 dep
->prev_dep
= sheet
->deps
->tail
;
1530 dep
->next_dep
= NULL
;
1532 dep
->prev_dep
->next_dep
= dep
;
1534 sheet
->deps
->head
= dep
; /* first element */
1535 sheet
->deps
->tail
= dep
;
1536 dep
->flags
|= DEPENDENT_IS_LINKED
|
1537 link_unlink_expr_dep (eval_pos_init_dep (&ep
, dep
),
1538 dep
->texpr
->expr
, TRUE
);
1540 if (dep
->flags
& DEPENDENT_HAS_3D
)
1541 workbook_link_3d_dep (dep
);
1546 * @dep: the dependent that changed
1548 * Removes the dependent from its container's set of dependents and always
1549 * removes the linkages to what it depends on.
1552 dependent_unlink (GnmDependent
*dep
)
1554 GnmDepContainer
*contain
;
1557 g_return_if_fail (dep
!= NULL
);
1558 g_return_if_fail (dependent_is_linked (dep
));
1559 g_return_if_fail (dep
->texpr
!= NULL
);
1560 g_return_if_fail (IS_SHEET (dep
->sheet
));
1562 link_unlink_expr_dep (eval_pos_init_dep (&ep
, dep
),
1563 dep
->texpr
->expr
, FALSE
);
1564 contain
= dep
->sheet
->deps
;
1565 if (contain
!= NULL
) {
1566 if (contain
->head
== dep
)
1567 contain
->head
= dep
->next_dep
;
1568 if (contain
->tail
== dep
)
1569 contain
->tail
= dep
->prev_dep
;
1571 dep
->next_dep
->prev_dep
= dep
->prev_dep
;
1573 dep
->prev_dep
->next_dep
= dep
->next_dep
;
1575 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
)
1576 dependent_clear_dynamic_deps (dep
);
1579 if (dep
->flags
& DEPENDENT_HAS_3D
)
1580 workbook_unlink_3d_dep (dep
);
1581 dep
->flags
&= ~DEPENDENT_LINK_FLAGS
;
1585 * gnm_cell_eval_content:
1586 * @cell: the cell to evaluate.
1588 * This function evaluates the contents of the cell,
1589 * it should not be used by anyone. It is an internal
1593 gnm_cell_eval_content (GnmCell
*cell
)
1595 static GnmCell
*iterating
= NULL
;
1600 if (!gnm_cell_has_expr (cell
) || /* plain cells without expr */
1601 !dependent_is_linked (&cell
->base
)) /* special case within TABLE */
1604 #ifdef DEBUG_EVALUATION
1607 char *str
= gnm_expr_top_as_string
1609 parse_pos_init_cell (&pp
, cell
),
1611 g_printerr ("{\nEvaluating %s!%s: %s;\n",
1612 cell
->base
.sheet
->name_quoted
, cell_name (cell
),
1618 /* This is the bottom of a cycle */
1619 if (cell
->base
.flags
& DEPENDENT_BEING_CALCULATED
) {
1620 if (!cell
->base
.sheet
->workbook
->iteration
.enabled
)
1623 /* but not the first bottom */
1624 if (cell
->base
.flags
& DEPENDENT_BEING_ITERATED
) {
1625 #ifdef DEBUG_EVALUATION
1626 g_printerr ("}; /* already-iterate (%d) */\n", iterating
== NULL
);
1628 return iterating
== NULL
;
1631 /* if we are still marked as iterating then make this the last
1634 if (iterating
== cell
) {
1635 #ifdef DEBUG_EVALUATION
1636 puts ("}; /* NO-iterate (1) */");
1639 } else if (iterating
== NULL
) {
1640 cell
->base
.flags
|= DEPENDENT_BEING_ITERATED
;
1642 #ifdef DEBUG_EVALUATION
1643 puts ("}; /* START iterate = TRUE (0) */");
1647 #ifdef DEBUG_EVALUATION
1648 puts ("}; /* other-iterate (0) */");
1654 /* Prepare to calculate */
1655 eval_pos_init_cell (&pos
, cell
);
1656 cell
->base
.flags
|= DEPENDENT_BEING_CALCULATED
;
1659 * I'm somewhat afraid of thinking about the semantics of iteration
1660 * if different workbooks have different settings.
1662 max_iteration
= cell
->base
.sheet
->workbook
->iteration
.max_number
;
1665 v
= gnm_expr_top_eval (cell
->base
.texpr
, &pos
,
1666 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
1668 v
= value_new_error (&pos
, "Internal error");
1670 #ifdef DEBUG_EVALUATION
1673 ? value_get_as_string (v
)
1674 : g_strdup ("NULL");
1675 g_printerr ("Evaluation(%d) %s := %s\n",
1676 max_iteration
, cell_name (cell
), valtxt
);
1681 /* The top of a cycle */
1682 if (cell
->base
.flags
& DEPENDENT_BEING_ITERATED
) {
1683 cell
->base
.flags
&= ~DEPENDENT_BEING_ITERATED
;
1685 /* We just completed the last iteration, don't change things */
1686 if (iterating
&& max_iteration
-- > 0) {
1687 /* If we are within bounds make this the last round */
1688 if (value_diff (cell
->value
, v
) < cell
->base
.sheet
->workbook
->iteration
.tolerance
)
1691 #ifdef DEBUG_EVALUATION
1692 puts ("/* iterate == NULL */");
1696 value_release (cell
->value
);
1699 gnm_cell_unrender (cell
);
1700 #ifdef DEBUG_EVALUATION
1701 puts ("/* LOOP */");
1705 g_return_val_if_fail (iterating
, TRUE
);
1708 gboolean had_value
= (cell
->value
!= NULL
);
1709 if (had_value
&& value_equal (v
, cell
->value
)) {
1710 /* Value didn't change. */
1713 gboolean was_string
= had_value
&& (VALUE_IS_STRING (cell
->value
) || VALUE_IS_ERROR (cell
->value
));
1714 gboolean is_string
= VALUE_IS_STRING (v
) || VALUE_IS_ERROR (v
);
1716 if ((was_string
|| is_string
))
1717 sheet_cell_queue_respan (cell
);
1720 value_release (cell
->value
);
1723 gnm_cell_unrender (cell
);
1727 if (iterating
== cell
)
1730 #ifdef DEBUG_EVALUATION
1731 g_printerr ("} (%d)\n", iterating
== NULL
);
1733 cell
->base
.flags
&= ~DEPENDENT_BEING_CALCULATED
;
1734 return iterating
== NULL
;
1742 dependent_eval (GnmDependent
*dep
)
1744 int const t
= dependent_type (dep
);
1745 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
1747 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
) {
1748 dependent_clear_dynamic_deps (dep
);
1749 dep
->flags
&= ~DEPENDENT_HAS_DYNAMIC_DEPS
;
1753 * Problem: this really should be a tail call.
1757 /* Don't clear flag until after in case we iterate */
1758 dep
->flags
&= ~DEPENDENT_NEEDS_RECALC
;
1762 gnm_cell_eval (GnmCell
*cell
)
1764 if (gnm_cell_needs_recalc (cell
)) {
1766 * This should be a tail call so the stack frame can be
1767 * eliminated before the call.
1769 dependent_eval (GNM_CELL_TO_DEP (cell
));
1775 * cell_queue_recalc :
1778 * Queue the cell and everything that depends on it for recalculation.
1779 * If a dependency is already queued ignore it.
1782 cell_queue_recalc (GnmCell
*cell
)
1784 g_return_if_fail (cell
!= NULL
);
1786 if (!gnm_cell_needs_recalc (cell
)) {
1789 if (gnm_cell_has_expr (cell
))
1790 dependent_flag_recalc (GNM_CELL_TO_DEP (cell
));
1792 deps
= cell_list_deps (cell
);
1793 dependent_queue_recalc_list (deps
);
1794 g_slist_free (deps
);
1802 } search_rangedeps_closure_t
;
1805 cb_search_rangedeps (gpointer key
, G_GNUC_UNUSED gpointer value
,
1808 search_rangedeps_closure_t
const *c
= closure
;
1809 DependencyRange
const *deprange
= key
;
1810 GnmRange
const *range
= &(deprange
->range
);
1813 /* When things get slow this is a good test to enable */
1814 static int counter
= 0;
1815 if ((++counter
% 100000) == 0)
1816 g_printerr ("%d\n", counter
/ 100000);
1819 if (range_contains (range
, c
->col
, c
->row
)) {
1820 GnmDepFunc func
= c
->func
;
1821 micro_hash_foreach_dep (deprange
->deps
, dep
,
1822 (func
) (dep
, c
->user
););
1827 cell_foreach_range_dep (GnmCell
const *cell
, GnmDepFunc func
, gpointer user
)
1829 search_rangedeps_closure_t closure
;
1830 GHashTable
*bucket
=
1831 cell
->base
.sheet
->deps
->range_hash
[BUCKET_OF_ROW (cell
->pos
.row
)];
1833 if (bucket
!= NULL
) {
1834 closure
.col
= cell
->pos
.col
;
1835 closure
.row
= cell
->pos
.row
;
1836 closure
.func
= func
;
1837 closure
.user
= user
;
1838 g_hash_table_foreach (bucket
, &cb_search_rangedeps
, &closure
);
1843 cell_foreach_single_dep (Sheet
const *sheet
, int col
, int row
,
1844 GnmDepFunc func
, gpointer user
)
1846 DependencySingle lookup
, *single
;
1847 GnmDepContainer
*deps
= sheet
->deps
;
1849 lookup
.pos
.col
= col
;
1850 lookup
.pos
.row
= row
;
1852 single
= g_hash_table_lookup (deps
->single_hash
, &lookup
);
1854 micro_hash_foreach_dep (single
->deps
, dep
,
1855 (*func
) (dep
, user
););
1861 * @func: (scope call):
1866 cell_foreach_dep (GnmCell
const *cell
, GnmDepFunc func
, gpointer user
)
1868 g_return_if_fail (cell
!= NULL
);
1870 /* accelerate exit */
1871 if (!cell
->base
.sheet
->deps
)
1874 cell_foreach_range_dep (cell
, func
, user
);
1875 cell_foreach_single_dep (cell
->base
.sheet
, cell
->pos
.col
, cell
->pos
.row
,
1880 cb_recalc_all_depends (gpointer key
, G_GNUC_UNUSED gpointer value
,
1881 G_GNUC_UNUSED gpointer ignore
)
1883 DependencyAny
const *depany
= key
;
1884 GSList
*work
= NULL
;
1885 micro_hash_foreach_dep (depany
->deps
, dep
, {
1886 if (!dependent_needs_recalc (dep
)) {
1887 dependent_flag_recalc (dep
);
1888 work
= g_slist_prepend (work
, dep
);
1891 dependent_queue_recalc_main (work
);
1897 cb_range_contained_depend (gpointer key
, G_GNUC_UNUSED gpointer value
,
1900 DependencyRange
const *deprange
= key
;
1901 GnmRange
const *range
= &deprange
->range
;
1902 GnmRange
const *target
= user
;
1904 if (range_overlap (target
, range
)) {
1905 GSList
*work
= NULL
;
1906 micro_hash_foreach_dep (deprange
->deps
, dep
, {
1907 if (!dependent_needs_recalc (dep
)) {
1908 dependent_flag_recalc (dep
);
1909 work
= g_slist_prepend (work
, dep
);
1912 dependent_queue_recalc_main (work
);
1917 cb_single_contained_depend (gpointer key
,
1918 G_GNUC_UNUSED gpointer value
,
1921 DependencySingle
const *depsingle
= key
;
1922 GnmRange
const *target
= user
;
1924 if (range_contains (target
, depsingle
->pos
.col
, depsingle
->pos
.row
)) {
1925 GSList
*work
= NULL
;
1926 micro_hash_foreach_dep (depsingle
->deps
, dep
, {
1927 if (!dependent_needs_recalc (dep
)) {
1928 dependent_flag_recalc (dep
);
1929 work
= g_slist_prepend (work
, dep
);
1932 dependent_queue_recalc_main (work
);
1937 * sheet_region_queue_recalc:
1938 * @sheet: The sheet.
1939 * @range: Optionally NULL range.
1941 * Queues things that depend on @sheet!@range for recalc.
1943 * If @range is NULL the entire sheet is used.
1946 sheet_region_queue_recalc (Sheet
const *sheet
, GnmRange
const *r
)
1950 g_return_if_fail (IS_SHEET (sheet
));
1951 g_return_if_fail (sheet
->deps
!= NULL
);
1954 /* mark the contained depends dirty non recursively */
1955 SHEET_FOREACH_DEPENDENT (sheet
, dep
,
1956 dependent_flag_recalc (dep
););
1958 /* look for things that depend on the sheet */
1959 for (i
= sheet
->deps
->buckets
- 1; i
>= 0 ; i
--) {
1960 GHashTable
*hash
= sheet
->deps
->range_hash
[i
];
1962 g_hash_table_foreach (hash
,
1963 &cb_recalc_all_depends
, NULL
);
1965 g_hash_table_foreach (sheet
->deps
->single_hash
,
1966 &cb_recalc_all_depends
, NULL
);
1968 int const first
= BUCKET_OF_ROW (r
->start
.row
);
1970 /* mark the contained depends dirty non recursively */
1971 SHEET_FOREACH_DEPENDENT (sheet
, dep
, {
1972 GnmCell
*cell
= GNM_DEP_TO_CELL (dep
);
1973 if (dependent_is_cell (dep
) &&
1974 range_contains (r
, cell
->pos
.col
, cell
->pos
.row
))
1975 dependent_flag_recalc (dep
);
1978 /* look for things that depend on target region */
1979 for (i
= BUCKET_OF_ROW (r
->end
.row
); i
>= first
; i
--) {
1980 GHashTable
*hash
= sheet
->deps
->range_hash
[i
];
1982 g_hash_table_foreach (hash
,
1983 &cb_range_contained_depend
, (gpointer
)r
);
1985 g_hash_table_foreach (sheet
->deps
->single_hash
,
1986 &cb_single_contained_depend
, (gpointer
)r
);
1997 GnmExprTop
const *oldtree
;
1998 } ExprRelocateStorage
;
2001 * dependents_unrelocate_free :
2004 * Free the undo info associated with a dependent relocation.
2007 dependents_unrelocate_free (GSList
*info
)
2010 for (; ptr
!= NULL
; ptr
= ptr
->next
) {
2011 ExprRelocateStorage
*tmp
= ptr
->data
;
2012 gnm_expr_top_unref (tmp
->oldtree
);
2015 g_slist_free (info
);
2019 * dependents_unrelocate :
2022 * Apply the undo info associated with a dependent relocation.
2025 dependents_unrelocate (GSList
*info
)
2028 for (; ptr
!= NULL
; ptr
= ptr
->next
) {
2029 ExprRelocateStorage
*tmp
= ptr
->data
;
2031 if (tmp
->dep_type
== DEPENDENT_CELL
) {
2032 if (!IS_SHEET (tmp
->u
.pos
.sheet
)) {
2033 /* FIXME : happens when undoing a move from a deleted
2034 * sheet. Do we want to do something here */
2036 GnmCell
*cell
= sheet_cell_get
2038 tmp
->u
.pos
.eval
.col
, tmp
->u
.pos
.eval
.row
);
2040 /* It is possible to have a NULL if the relocation info
2041 * is not really relevant. eg when undoing a pasted
2042 * cut that was relocated but also saved to a buffer */
2044 if (gnm_expr_top_is_array_corner (tmp
->oldtree
)) {
2047 gnm_expr_top_get_array_size (tmp
->oldtree
, &cols
, &rows
);
2048 gnm_cell_set_array_formula (tmp
->u
.pos
.sheet
,
2049 tmp
->u
.pos
.eval
.col
,
2050 tmp
->u
.pos
.eval
.row
,
2051 tmp
->u
.pos
.eval
.col
+ cols
- 1,
2052 tmp
->u
.pos
.eval
.row
+ rows
- 1,
2053 gnm_expr_top_new (gnm_expr_copy (gnm_expr_top_get_array_expr (tmp
->oldtree
))));
2054 cell_queue_recalc (cell
);
2055 sheet_flag_status_update_cell (cell
);
2057 sheet_cell_set_expr (cell
, tmp
->oldtree
);
2060 } else if (tmp
->dep_type
== DEPENDENT_NAME
) {
2062 dependent_set_expr (tmp
->u
.dep
, tmp
->oldtree
);
2063 dependent_flag_recalc (tmp
->u
.dep
);
2064 dependent_link (tmp
->u
.dep
);
2071 * @deps: (element-type GnmDependent): An slist of dependents.
2073 * link a list of dependents. (The list used to get freed, but is not
2077 dependents_link (GSList
*deps
)
2082 for (; ptr
!= NULL
; ptr
= ptr
->next
) {
2083 GnmDependent
*dep
= ptr
->data
;
2084 if (dep
->sheet
->being_invalidated
)
2086 if (dep
->sheet
->deps
!= NULL
&& !dependent_is_linked (dep
)) {
2087 dependent_link (dep
);
2088 dependent_queue_recalc (dep
);
2094 GnmRange
const *target
;
2099 cb_range_contained_collect (DependencyRange
const *deprange
,
2100 G_GNUC_UNUSED gpointer ignored
,
2101 CollectClosure
*user
)
2103 GnmRange
const *range
= &deprange
->range
;
2105 if (range_overlap (user
->target
, range
))
2106 micro_hash_foreach_dep (deprange
->deps
, dep
, {
2107 if (!(dep
->flags
& (DEPENDENT_FLAGGED
| DEPENDENT_CAN_RELOCATE
)) &&
2108 dependent_type (dep
) != DEPENDENT_DYNAMIC_DEP
) {
2109 dep
->flags
|= DEPENDENT_FLAGGED
;
2110 user
->list
= g_slist_prepend (user
->list
, dep
);
2115 cb_single_contained_collect (DependencySingle
const *depsingle
,
2116 G_GNUC_UNUSED gpointer ignored
,
2117 CollectClosure
*user
)
2119 if (range_contains (user
->target
, depsingle
->pos
.col
, depsingle
->pos
.row
))
2120 micro_hash_foreach_dep (depsingle
->deps
, dep
, {
2121 if (!(dep
->flags
& (DEPENDENT_FLAGGED
| DEPENDENT_CAN_RELOCATE
)) &&
2122 dependent_type (dep
) != DEPENDENT_DYNAMIC_DEP
) {
2123 dep
->flags
|= DEPENDENT_FLAGGED
;
2124 user
->list
= g_slist_prepend (user
->list
, dep
);
2129 cb_collect_names (GnmNamedExpr
*nexpr
,
2130 G_GNUC_UNUSED gpointer value
,
2133 *l
= g_slist_prepend (*l
, nexpr
);
2136 struct cb_remote_names
{
2142 cb_remote_names1 (G_GNUC_UNUSED gconstpointer key
,
2143 GnmNamedExpr
*nexpr
,
2144 struct cb_remote_names
*data
)
2146 data
->names
= g_slist_prepend (data
->names
, nexpr
);
2150 cb_remote_names2 (GnmNamedExpr
*nexpr
,
2151 G_GNUC_UNUSED gpointer value
,
2152 struct cb_remote_names
*data
)
2155 nexpr
->pos
.sheet
? nexpr
->pos
.sheet
->workbook
: nexpr
->pos
.wb
;
2157 data
->names
= g_slist_prepend (data
->names
, nexpr
);
2161 * Get a list of all names that (may) reference data in a given sheet.
2162 * This is approximated as all names in the sheet, all global names in
2163 * its workbook, and all external references that actually mention the
2167 names_referencing_sheet (Sheet
*sheet
)
2169 struct cb_remote_names data
;
2172 data
.wb
= sheet
->workbook
;
2174 workbook_foreach_name (sheet
->workbook
, TRUE
,
2175 (GHFunc
)cb_remote_names1
,
2177 gnm_sheet_foreach_name (sheet
, (GHFunc
)cb_remote_names1
, &data
);
2179 if (sheet
->deps
->referencing_names
)
2180 g_hash_table_foreach (sheet
->deps
->referencing_names
,
2181 (GHFunc
)cb_remote_names2
,
2189 * dependents_relocate:
2190 * @info: the descriptor record for what is being moved where.
2192 * Fixes references to or from a region that is going to be moved.
2193 * Returns: (transfer full): a list of the locations and expressions that were changed outside of
2197 dependents_relocate (GnmExprRelocateInfo
const *rinfo
)
2199 GnmExprRelocateInfo local_rinfo
;
2200 GSList
*l
, *dependents
= NULL
, *undo_info
= NULL
;
2204 CollectClosure collect
;
2205 GOUndo
*u_exprs
, *u_names
;
2207 g_return_val_if_fail (rinfo
!= NULL
, NULL
);
2209 /* short circuit if nothing would move */
2210 if (rinfo
->col_offset
== 0 && rinfo
->row_offset
== 0 &&
2211 rinfo
->origin_sheet
== rinfo
->target_sheet
)
2214 sheet
= rinfo
->origin_sheet
;
2217 /* collect contained cells with expressions */
2218 SHEET_FOREACH_DEPENDENT (rinfo
->origin_sheet
, dep
, {
2219 GnmCell
*cell
= GNM_DEP_TO_CELL (dep
);
2220 if (dependent_is_cell (dep
) &&
2221 range_contains (r
, cell
->pos
.col
, cell
->pos
.row
)) {
2222 dependents
= g_slist_prepend (dependents
, dep
);
2223 dep
->flags
|= DEPENDENT_FLAGGED
;
2227 /* collect the things that depend on source region */
2229 collect
.list
= dependents
;
2230 g_hash_table_foreach (sheet
->deps
->single_hash
,
2231 (GHFunc
) &cb_single_contained_collect
,
2232 (gpointer
)&collect
);
2234 int const first
= BUCKET_OF_ROW (r
->start
.row
);
2236 for (i
= BUCKET_OF_ROW (r
->end
.row
); i
>= first
; i
--) {
2237 hash
= sheet
->deps
->range_hash
[i
];
2239 g_hash_table_foreach (hash
,
2240 (GHFunc
) &cb_range_contained_collect
,
2241 (gpointer
)&collect
);
2244 dependents
= collect
.list
;
2245 local_rinfo
= *rinfo
;
2246 for (l
= dependents
; l
; l
= l
->next
) {
2247 GnmExprTop
const *newtree
;
2248 GnmDependent
*dep
= l
->data
;
2250 dep
->flags
&= ~DEPENDENT_FLAGGED
;
2251 sheet_flag_status_update_range (dep
->sheet
, NULL
);
2253 parse_pos_init_dep (&local_rinfo
.pos
, dep
);
2255 /* it is possible nothing changed for contained deps
2256 * using absolute references */
2257 newtree
= gnm_expr_top_relocate (dep
->texpr
, &local_rinfo
, FALSE
);
2258 if (newtree
!= NULL
) {
2259 int const t
= dependent_type (dep
);
2260 ExprRelocateStorage
*tmp
=
2261 g_new (ExprRelocateStorage
, 1);
2264 if (t
== DEPENDENT_NAME
) {
2265 #warning "What should we do here and why do we leak tmp?"
2267 if (t
== DEPENDENT_CELL
)
2268 tmp
->u
.pos
= local_rinfo
.pos
;
2271 tmp
->oldtree
= dep
->texpr
;
2272 gnm_expr_top_ref (tmp
->oldtree
);
2273 undo_info
= g_slist_prepend (undo_info
, tmp
);
2275 dependent_set_expr (dep
, newtree
); /* unlinks */
2276 gnm_expr_top_unref (newtree
);
2278 /* queue the things that depend on the changed dep
2279 * even if it is going to move.
2281 dependent_queue_recalc (dep
);
2283 /* relink if it is not going to move, if it is moving
2284 * then the caller is responsible for relinking.
2285 * This avoids a link/unlink/link tuple
2287 if (t
== DEPENDENT_CELL
) {
2288 GnmCellPos
const *pos
= &GNM_DEP_TO_CELL (dep
)->pos
;
2289 if (dep
->sheet
!= sheet
||
2290 !range_contains (r
, pos
->col
, pos
->row
))
2291 dependent_link (dep
);
2293 dependent_link (dep
);
2297 * The expression may not be changing, but it depends
2298 * on something that is. Not-corner array cells go here
2301 dependent_queue_recalc (dep
);
2304 /* Not the most efficient, but probably not too bad. It is
2305 * definitely cheaper than finding the set of effected sheets. */
2306 sheet_flag_status_update_range (dep
->sheet
, NULL
);
2308 g_slist_free (dependents
);
2310 u_exprs
= go_undo_unary_new (undo_info
,
2311 (GOUndoUnaryFunc
)dependents_unrelocate
,
2312 (GFreeFunc
)dependents_unrelocate_free
);
2316 switch (rinfo
->reloc_type
) {
2317 case GNM_EXPR_RELOCATE_INVALIDATE_SHEET
:
2318 case GNM_EXPR_RELOCATE_MOVE_RANGE
:
2321 case GNM_EXPR_RELOCATE_COLS
:
2322 case GNM_EXPR_RELOCATE_ROWS
: {
2323 GSList
*l
, *names
= names_referencing_sheet (sheet
);
2324 GnmExprRelocateInfo rinfo2
= *rinfo
;
2326 for (l
= names
; l
; l
= l
->next
) {
2327 GnmNamedExpr
*nexpr
= l
->data
;
2328 GnmExprTop
const *newtree
;
2329 rinfo2
.pos
= nexpr
->pos
;
2330 newtree
= gnm_expr_top_relocate (nexpr
->texpr
,
2333 GOUndo
*u
= expr_name_set_expr_undo_new (nexpr
);
2334 u_names
= go_undo_combine (u_names
, u
);
2335 expr_name_set_expr (nexpr
, newtree
);
2338 g_slist_free (names
);
2342 g_assert_not_reached ();
2346 return go_undo_combine (u_exprs
, u_names
);
2349 /*******************************************************************/
2352 cb_collect_range (G_GNUC_UNUSED gpointer key
,
2353 DependencyAny
*depany
,
2356 *collector
= g_slist_prepend (*collector
, depany
);
2361 dep_hash_destroy (GHashTable
*hash
, GSList
**dyn_deps
, Sheet
*sheet
)
2363 GSList
*deps
= NULL
, *l
;
2364 GnmExprRelocateInfo rinfo
;
2365 GSList
*deplist
= NULL
;
2366 gboolean destroy
= (sheet
->revive
== NULL
);
2368 /* We collect first because we will be changing the hash. */
2370 g_hash_table_foreach_remove (hash
,
2371 (GHRFunc
)cb_collect_range
,
2373 g_hash_table_destroy (hash
);
2375 g_hash_table_foreach (hash
, (GHFunc
)cb_collect_range
, &deps
);
2378 for (l
= deps
; l
; l
= l
->next
) {
2379 DependencyAny
*depany
= l
->data
;
2381 micro_hash_foreach_dep (depany
->deps
, dep
, {
2382 if (dependent_type (dep
) == DEPENDENT_DYNAMIC_DEP
) {
2383 GnmDependent
*c
= ((DynamicDep
*)dep
)->container
;
2384 if (!c
->sheet
->being_invalidated
)
2386 g_slist_prepend (*dyn_deps
, c
);
2387 } else if (!dep
->sheet
->being_invalidated
) {
2389 * We collect here instead of doing right away as
2390 * the dependent_set_expr below can change the
2391 * container we are looping over.
2393 deplist
= g_slist_prepend (deplist
, dep
);
2398 micro_hash_release (&depany
->deps
);
2400 g_slist_free (deps
);
2403 * We do this after the above loop as this loop will
2404 * invalidate some of the DependencyAny pointers from
2405 * above. The testcase for that is 314207, deleting
2406 * all but the first sheet in one go.
2408 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
2409 for (l
= deplist
; l
; l
= l
->next
) {
2410 GnmDependent
*dep
= l
->data
;
2411 GnmExprTop
const *te
= dep
->texpr
;
2412 /* We are told this dependent depends on this region, hence if
2413 * newtree is null then either
2414 * 1) we did not depend on it (ie., serious breakage )
2415 * 2) we had a duplicate reference and we have already removed it.
2416 * 3) We depended on things via a name which will be
2417 * invalidated elsewhere */
2418 GnmExprTop
const *newtree
= gnm_expr_top_relocate (te
, &rinfo
, FALSE
);
2419 if (newtree
!= NULL
) {
2423 gnm_dep_set_expr_undo_new (dep
));
2424 dependent_set_expr (dep
, newtree
);
2425 gnm_expr_top_unref (newtree
);
2426 dependent_link (dep
);
2429 g_slist_free (deplist
);
2433 cb_collect_deps_of_name (GnmDependent
*dep
, G_GNUC_UNUSED gpointer value
,
2436 /* grab unflagged linked depends */
2437 if ((dep
->flags
& (DEPENDENT_FLAGGED
|DEPENDENT_IS_LINKED
)) == DEPENDENT_IS_LINKED
) {
2438 dep
->flags
|= DEPENDENT_FLAGGED
;
2439 *accum
= g_slist_prepend (*accum
, dep
);
2443 struct cb_collect_deps_of_names
{
2449 cb_collect_deps_of_names (GnmNamedExpr
*nexpr
,
2450 G_GNUC_UNUSED gpointer value
,
2451 struct cb_collect_deps_of_names
*accum
)
2453 accum
->names
= g_slist_prepend (accum
->names
, nexpr
);
2454 if (nexpr
->dependents
)
2455 g_hash_table_foreach (nexpr
->dependents
,
2456 (GHFunc
)cb_collect_deps_of_name
,
2461 invalidate_name (GnmNamedExpr
*nexpr
, Sheet
*sheet
)
2463 GnmExprTop
const *old_expr
= nexpr
->texpr
;
2464 GnmExprTop
const *new_expr
= NULL
;
2465 gboolean scope_being_killed
=
2467 ? nexpr
->pos
.sheet
->being_invalidated
2468 : nexpr
->pos
.wb
->during_destruction
;
2470 if (!scope_being_killed
) {
2471 GnmExprRelocateInfo rinfo
;
2472 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
2473 new_expr
= gnm_expr_top_relocate (old_expr
, &rinfo
, FALSE
);
2474 g_return_if_fail (new_expr
!= NULL
);
2477 if (nexpr
->dependents
&& g_hash_table_size (nexpr
->dependents
))
2478 g_warning ("Left-over name dependencies\n");
2481 go_undo_group_add (sheet
->revive
,
2482 expr_name_set_expr_undo_new (nexpr
));
2484 expr_name_set_expr (nexpr
, new_expr
);
2488 handle_dynamic_deps (GSList
*dyn_deps
)
2492 for (ptr
= dyn_deps
; ptr
!= NULL
; ptr
= ptr
->next
) {
2493 GnmDependent
*dep
= ptr
->data
;
2494 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
) {
2495 dependent_clear_dynamic_deps (dep
);
2496 dep
->flags
&= ~DEPENDENT_HAS_DYNAMIC_DEPS
;
2499 dependent_queue_recalc_list (dyn_deps
);
2500 g_slist_free (dyn_deps
);
2504 handle_referencing_names (GnmDepContainer
*deps
, Sheet
*sheet
)
2507 GHashTable
*names
= deps
->referencing_names
;
2508 struct cb_collect_deps_of_names accum
;
2509 gboolean destroy
= (sheet
->revive
== NULL
);
2515 deps
->referencing_names
= NULL
;
2519 g_hash_table_foreach (names
,
2520 (GHFunc
)cb_collect_deps_of_names
,
2523 for (ptr
= accum
.deps
; ptr
; ptr
= ptr
->next
) {
2524 GnmDependent
*dep
= ptr
->data
;
2525 dep
->flags
&= ~DEPENDENT_FLAGGED
;
2526 dependent_unlink (dep
);
2529 /* now that all of the dependents of these names are unlinked.
2530 * change the references in the names to avoid this sheet */
2531 for (ptr
= accum
.names
; ptr
; ptr
= ptr
->next
) {
2532 GnmNamedExpr
*nexpr
= ptr
->data
;
2533 invalidate_name (nexpr
, sheet
);
2535 g_slist_free (accum
.names
);
2537 /* then relink things en-mass in case one of the deps outside
2538 * this sheet used multiple names that referenced us */
2539 dependents_link (accum
.deps
);
2542 g_slist_free (accum
.deps
);
2543 g_hash_table_destroy (names
);
2545 go_undo_group_add (sheet
->revive
,
2546 gnm_dep_unlink_undo_new (accum
.deps
));
2551 handle_outgoing_references (GnmDepContainer
*deps
, Sheet
*sheet
)
2553 GnmDependentFlags what
= DEPENDENT_USES_NAME
;
2554 GSList
*accum
= NULL
;
2556 what
|= (sheet
->workbook
&& sheet
->workbook
->during_destruction
)
2557 ? DEPENDENT_GOES_INTERBOOK
2558 : DEPENDENT_GOES_INTERSHEET
;
2559 DEPENDENT_CONTAINER_FOREACH_DEPENDENT (deps
, dep
, {
2560 if (dependent_is_linked (dep
) && (dep
->flags
& what
)) {
2561 dependent_unlink (dep
);
2563 accum
= g_slist_prepend (accum
, dep
);
2568 go_undo_group_add (sheet
->revive
,
2569 gnm_dep_unlink_undo_new (accum
));
2574 * Invalidate references of all kinds to the sheet.
2576 * Also destroy the dependency container.
2579 do_deps_destroy (Sheet
*sheet
)
2581 GnmDepContainer
*deps
;
2582 GSList
*dyn_deps
= NULL
;
2585 g_return_if_fail (IS_SHEET (sheet
));
2586 g_return_if_fail (sheet
->being_invalidated
);
2588 /* The GnmDepContainer (i.e., sheet->deps) contains the names that
2589 * reference this, not the names it contains. Remove the locally
2590 * defined names here.
2592 * NOTE : they may continue to exist inactively for a bit. Be
2593 * careful to remove them _before_ destroying the deps. This is
2594 * a bit wasteful in that we unlink and relink a few things that
2595 * are going to be deleted. However, it is necessary to catch
2596 * all the different life cycles.
2598 gnm_named_expr_collection_free (sheet
->names
);
2599 sheet
->names
= NULL
;
2605 /* Destroy the records of what depends on this sheet. There is no need
2606 * to delicately remove individual items from the lists. The only
2607 * purpose that serves is to validate the state of our data structures.
2608 * If required this optimization can be disabled for debugging.
2611 if (sheet
->revive
) {
2612 g_object_unref (sheet
->revive
);
2613 sheet
->revive
= NULL
;
2616 for (i
= deps
->buckets
- 1; i
>= 0 ; i
--) {
2617 GHashTable
*hash
= deps
->range_hash
[i
];
2619 dep_hash_destroy (hash
, &dyn_deps
, sheet
);
2621 dep_hash_destroy (deps
->single_hash
, &dyn_deps
, sheet
);
2623 g_free (deps
->range_hash
);
2624 deps
->range_hash
= NULL
;
2626 * Note: we have not freed the elements in the pool. This call
2627 * frees everything in one go.
2629 go_mem_chunk_destroy (deps
->range_pool
, TRUE
);
2630 deps
->range_pool
= NULL
;
2632 deps
->single_hash
= NULL
;
2634 * Note: we have not freed the elements in the pool. This call
2635 * frees everything in one go.
2637 go_mem_chunk_destroy (deps
->single_pool
, TRUE
);
2638 deps
->single_pool
= NULL
;
2640 /* Now that we have tossed all deps to this sheet we can queue the
2641 * external dyn deps for recalc and free them */
2642 handle_dynamic_deps (dyn_deps
);
2644 g_hash_table_destroy (deps
->dynamic_deps
);
2645 deps
->dynamic_deps
= NULL
;
2647 handle_referencing_names (deps
, sheet
);
2649 /* Now we remove any links from dependents in this sheet to
2650 * to other containers. If the entire workbook is going away
2651 * just look for inter-book links.
2653 handle_outgoing_references (deps
, sheet
);
2659 * do_deps_invalidate:
2660 * Invalidate references of all kinds to the sheet.
2663 do_deps_invalidate (Sheet
*sheet
)
2665 GnmDepContainer
*deps
;
2666 GSList
*dyn_deps
= NULL
;
2669 g_return_if_fail (IS_SHEET (sheet
));
2670 g_return_if_fail (sheet
->being_invalidated
);
2671 g_return_if_fail (sheet
->revive
== NULL
);
2673 sheet
->revive
= go_undo_group_new ();
2675 gnm_named_expr_collection_unlink (sheet
->names
);
2679 for (i
= deps
->buckets
- 1; i
>= 0 ; i
--) {
2680 GHashTable
*hash
= deps
->range_hash
[i
];
2682 dep_hash_destroy (hash
, &dyn_deps
, sheet
);
2684 dep_hash_destroy (deps
->single_hash
, &dyn_deps
, sheet
);
2686 /* Now that we have tossed all deps to this sheet we can queue the
2687 * external dyn deps for recalc and free them */
2688 handle_dynamic_deps (dyn_deps
);
2690 handle_referencing_names (deps
, sheet
);
2692 /* Now we remove any links from dependents in this sheet to
2693 * to other containers. If the entire workbook is going away
2694 * just look for inter-book links.
2696 handle_outgoing_references (deps
, sheet
);
2700 cb_tweak_3d (GnmDependent
*dep
, G_GNUC_UNUSED gpointer value
, GSList
**deps
)
2702 *deps
= g_slist_prepend (*deps
, dep
);
2706 tweak_3d (Sheet
*sheet
)
2708 Workbook
*wb
= sheet
->workbook
;
2709 GSList
*deps
= NULL
, *l
;
2710 GnmExprRelocateInfo rinfo
;
2712 if (!wb
->sheet_order_dependents
)
2715 g_hash_table_foreach (wb
->sheet_order_dependents
,
2716 (GHFunc
)cb_tweak_3d
,
2719 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
2720 for (l
= deps
; l
; l
= l
->next
) {
2721 GnmDependent
*dep
= l
->data
;
2722 GnmExprTop
const *te
= dep
->texpr
;
2723 GnmExprTop
const *newtree
= gnm_expr_top_relocate (te
, &rinfo
, FALSE
);
2725 if (newtree
!= NULL
) {
2729 gnm_dep_set_expr_undo_new (dep
));
2730 dependent_set_expr (dep
, newtree
);
2731 gnm_expr_top_unref (newtree
);
2732 dependent_link (dep
);
2733 dependent_changed (dep
);
2736 g_slist_free (deps
);
2740 dependents_invalidate_sheets (GSList
*sheets
, gboolean destroy
)
2745 /* Mark all first. */
2746 for (tmp
= sheets
; tmp
; tmp
= tmp
->next
) {
2747 Sheet
*sheet
= tmp
->data
;
2748 sheet
->being_invalidated
= TRUE
;
2752 * Fixup 3d refs that start or end on one of these sheets.
2753 * Ideally we do this one per workbook, but that is not critical
2754 * so we are not going to outright sort the sheet list.
2757 for (tmp
= sheets
; tmp
; tmp
= tmp
->next
) {
2758 Sheet
*sheet
= tmp
->data
;
2759 Workbook
*wb
= sheet
->workbook
;
2766 /* Now invalidate. */
2767 for (tmp
= sheets
; tmp
; tmp
= tmp
->next
) {
2768 Sheet
*sheet
= tmp
->data
;
2770 do_deps_destroy (sheet
);
2772 do_deps_invalidate (sheet
);
2776 for (tmp
= sheets
; tmp
; tmp
= tmp
->next
) {
2777 Sheet
*sheet
= tmp
->data
;
2778 sheet
->being_invalidated
= FALSE
;
2783 dependents_invalidate_sheet (Sheet
*sheet
, gboolean destroy
)
2787 g_return_if_fail (IS_SHEET (sheet
));
2791 dependents_invalidate_sheets (&l
, destroy
);
2795 dependents_workbook_destroy (Workbook
*wb
)
2797 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
2798 g_return_if_fail (wb
->during_destruction
);
2799 g_return_if_fail (wb
->sheets
!= NULL
);
2801 /* Mark all first. */
2802 WORKBOOK_FOREACH_SHEET (wb
, sheet
, sheet
->being_invalidated
= TRUE
;);
2804 /* Kill 3d deps and workbook-level names, if needed. */
2805 if (wb
->sheet_order_dependents
) {
2806 g_hash_table_destroy (wb
->sheet_order_dependents
);
2807 wb
->sheet_order_dependents
= NULL
;
2809 gnm_named_expr_collection_free (wb
->names
);
2812 WORKBOOK_FOREACH_SHEET (wb
, sheet
, do_deps_destroy (sheet
););
2815 WORKBOOK_FOREACH_SHEET (wb
, sheet
, sheet
->being_invalidated
= FALSE
;);
2819 * dependents_revive_sheet:
2820 * Undo the effects of dependents_invalidate_sheet (sheet, FALSE).
2823 dependents_revive_sheet (Sheet
*sheet
)
2825 go_undo_undo (GO_UNDO (sheet
->revive
));
2826 g_object_unref (sheet
->revive
);
2827 sheet
->revive
= NULL
;
2829 /* Re-link local names. */
2830 gnm_named_expr_collection_relink (sheet
->names
);
2832 gnm_dep_container_sanity_check (sheet
->deps
);
2836 workbook_queue_all_recalc (Workbook
*wb
)
2838 /* FIXME: what about dependents in other workbooks? */
2839 WORKBOOK_FOREACH_DEPENDENT (wb
, dep
, dependent_flag_recalc (dep
););
2843 workbook_queue_volatile_recalc (Workbook
*wb
)
2845 WORKBOOK_FOREACH_DEPENDENT (wb
, dep
, {
2846 if (dependent_is_volatile (dep
))
2847 dependent_flag_recalc (dep
);
2856 * Computes all dependents in @wb that have been flaged as requiring
2859 * NOTE! This does not recalc dependents in other workbooks.
2862 workbook_recalc (Workbook
*wb
)
2864 gboolean redraw
= FALSE
;
2866 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
2868 gnm_app_recalc_start ();
2870 WORKBOOK_FOREACH_DEPENDENT (wb
, dep
, {
2871 if (dependent_needs_recalc (dep
)) {
2873 dependent_eval (dep
);
2877 gnm_app_recalc_finish ();
2880 * This is a bit of a band-aid. If anything is recalculated, we
2881 * force a full redraw. The alternative is to ask for updates
2882 * of every cell that is changed and that is probably more
2886 WORKBOOK_FOREACH_SHEET (wb
, sheet
, {
2887 SHEET_FOREACH_VIEW (sheet
, sv
, sv_flag_selection_change (sv
););
2888 sheet_redraw_all (sheet
, FALSE
);});
2893 * workbook_recalc_all :
2896 * Queues all dependents for recalc and recalculates.
2899 workbook_recalc_all (Workbook
*wb
)
2901 workbook_queue_all_recalc (wb
);
2903 /* Recalculate this workbook unconditionally. */
2904 workbook_recalc (wb
);
2906 /* Recalculate other workbooks when needed. */
2909 WORKBOOK_FOREACH_VIEW (wb
, view
,
2910 sheet_update (wb_view_cur_sheet (view
)););
2914 dynamic_dep_free (DynamicDep
*dyn
)
2916 GnmDependent
*dep
= dyn
->container
;
2917 GnmCellPos
const *pos
= dependent_pos (dep
);
2921 for (ptr
= dyn
->singles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2923 unlink_single_dep (&dyn
->base
, pos
, &rr
->a
);
2926 g_slist_free (dyn
->singles
);
2927 dyn
->singles
= NULL
;
2929 for (ptr
= dyn
->ranges
; ptr
!= NULL
; ptr
= ptr
->next
) {
2931 link_unlink_cellrange_dep (&dyn
->base
, pos
,
2932 &rr
->a
, &rr
->b
, FALSE
);
2935 g_slist_free (dyn
->ranges
);
2938 if (dyn
->base
.flags
& DEPENDENT_HAS_3D
)
2939 workbook_unlink_3d_dep (&dyn
->base
);
2945 dependent_is_volatile (GnmDependent
*dep
)
2947 /* This might need to be a virtual call. */
2948 return dep
->texpr
&& gnm_expr_top_is_volatile (dep
->texpr
);
2952 * gnm_dep_container_new: (skip)
2955 * Returns: (transfer full):
2958 gnm_dep_container_new (Sheet
*sheet
)
2960 GnmDepContainer
*deps
= g_new (GnmDepContainer
, 1);
2962 deps
->head
= deps
->tail
= NULL
;
2964 deps
->buckets
= 1 + BUCKET_OF_ROW (gnm_sheet_get_last_row (sheet
));
2965 deps
->range_hash
= g_new0 (GHashTable
*, deps
->buckets
);
2966 deps
->range_pool
= go_mem_chunk_new ("range pool",
2967 sizeof (DependencyRange
),
2969 deps
->single_hash
= g_hash_table_new ((GHashFunc
) depsingle_hash
,
2970 (GEqualFunc
) depsingle_equal
);
2971 deps
->single_pool
= go_mem_chunk_new ("single pool",
2972 sizeof (DependencySingle
),
2974 deps
->referencing_names
= g_hash_table_new (g_direct_hash
,
2977 deps
->dynamic_deps
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
2978 NULL
, (GDestroyNotify
) dynamic_dep_free
);
2984 gnm_dep_container_resize (GnmDepContainer
*deps
, int rows
)
2986 int i
, buckets
= 1 + BUCKET_OF_ROW (rows
- 1);
2988 for (i
= buckets
; i
< deps
->buckets
; i
++) {
2989 GHashTable
*hash
= deps
->range_hash
[i
];
2991 int s
= g_hash_table_size (hash
);
2993 g_printerr ("Hash table size: %d\n", s
);
2994 g_hash_table_destroy (hash
);
2995 deps
->range_hash
[i
] = NULL
;
2999 deps
->range_hash
= g_renew (GHashTable
*, deps
->range_hash
, buckets
);
3001 for (i
= deps
->buckets
; i
< buckets
; i
++)
3002 deps
->range_hash
[i
] = NULL
;
3004 deps
->buckets
= buckets
;
3007 /****************************************************************************
3012 dependent_debug_name_for_sheet (GnmDependent
const *dep
, Sheet
*sheet
,
3016 GnmDependentClass
*klass
;
3018 g_return_if_fail (dep
!= NULL
);
3019 g_return_if_fail (dep_classes
);
3022 g_warning ("Invalid dep, missing sheet");
3024 if (sheet
== dep
->sheet
) {
3027 g_string_append (target
,
3028 dep
->sheet
? dep
->sheet
->name_quoted
: "?");
3029 g_string_append_c (target
, '!');
3032 t
= dependent_type (dep
);
3033 klass
= g_ptr_array_index (dep_classes
, t
);
3034 klass
->debug_name (dep
, target
);
3039 * dependent_debug_name :
3040 * @dep: The dependent we are interested in.
3042 * A useful little debugging utility.
3045 dependent_debug_name (GnmDependent
const *dep
, GString
*target
)
3047 dependent_debug_name_for_sheet (dep
, NULL
, target
);
3052 dump_range_dep (gpointer key
, G_GNUC_UNUSED gpointer value
, gpointer sheet_
)
3054 DependencyRange
const *deprange
= key
;
3055 GnmRange
const *range
= &(deprange
->range
);
3056 Sheet
*sheet
= sheet_
;
3057 GString
*target
= g_string_sized_new (10000);
3058 gboolean first
= TRUE
;
3060 g_string_append (target
, " ");
3061 g_string_append (target
, range_as_string (range
));
3062 g_string_append (target
, " -> (");
3064 micro_hash_foreach_dep (deprange
->deps
, dep
, {
3068 g_string_append (target
, ", ");
3069 dependent_debug_name_for_sheet (dep
, sheet
, target
);
3071 g_string_append_c (target
, ')');
3073 g_printerr ("%s\n", target
->str
);
3074 g_string_free (target
, TRUE
);
3078 dump_single_dep (gpointer key
, G_GNUC_UNUSED gpointer value
, gpointer sheet_
)
3080 DependencySingle
*depsingle
= key
;
3081 Sheet
*sheet
= sheet_
;
3082 GString
*target
= g_string_sized_new (10000);
3083 gboolean first
= TRUE
;
3085 g_string_append (target
, " ");
3086 g_string_append (target
, cellpos_as_string (&depsingle
->pos
));
3087 g_string_append (target
, " -> ");
3089 micro_hash_foreach_dep (depsingle
->deps
, dep
, {
3093 g_string_append (target
, ", ");
3094 dependent_debug_name_for_sheet (dep
, sheet
, target
);
3097 g_printerr ("%s\n", target
->str
);
3098 g_string_free (target
, TRUE
);
3102 dump_dynamic_dep (gpointer key
, G_GNUC_UNUSED gpointer value
,
3103 G_GNUC_UNUSED gpointer closure
)
3105 GnmDependent
*dep
= key
;
3106 DynamicDep
*dyn
= value
;
3109 GnmConventionsOut out
;
3111 out
.accum
= g_string_new (NULL
);
3113 out
.convs
= gnm_conventions_default
;
3115 pp
.wb
= dep
->sheet
->workbook
;
3116 pp
.sheet
= dep
->sheet
;
3117 pp
.eval
= *dependent_pos (dyn
->container
);
3119 g_string_append (out
.accum
, " ");
3120 dependent_debug_name (dep
, out
.accum
);
3121 g_string_append (out
.accum
, " -> ");
3122 dependent_debug_name (&dyn
->base
, out
.accum
);
3123 g_string_append (out
.accum
, " { c=");
3124 dependent_debug_name (dyn
->container
, out
.accum
);
3126 g_string_append (out
.accum
, ", s=[");
3127 for (l
= dyn
->singles
; l
; l
= l
->next
) {
3128 rangeref_as_string (&out
, l
->data
);
3130 g_string_append (out
.accum
, ", ");
3133 g_string_append (out
.accum
, "], r=[");
3134 for (l
= dyn
->ranges
; l
; l
= l
->next
) {
3135 rangeref_as_string (&out
, l
->data
);
3137 g_string_append (out
.accum
, ", ");
3140 g_string_append (out
.accum
, "] }");
3141 g_printerr ("%s\n", out
.accum
->str
);
3142 g_string_free (out
.accum
, TRUE
);
3146 * gnm_dep_container_dump :
3149 * A useful utility for checking the state of the dependency data structures.
3152 gnm_dep_container_dump (GnmDepContainer
const *deps
,
3157 g_return_if_fail (deps
!= NULL
);
3159 gnm_dep_container_sanity_check (deps
);
3161 for (i
= deps
->buckets
- 1; i
>= 0 ; i
--) {
3162 GHashTable
*hash
= deps
->range_hash
[i
];
3163 if (hash
!= NULL
&& g_hash_table_size (hash
) > 0) {
3164 g_printerr (" Bucket %d (rows %d-%d): Range hash size %d: range over which cells in list depend\n",
3166 BUCKET_START_ROW (i
) + 1,
3167 BUCKET_END_ROW (i
) + 1,
3168 g_hash_table_size (hash
));
3169 g_hash_table_foreach (hash
,
3175 if (deps
->single_hash
&& g_hash_table_size (deps
->single_hash
) > 0) {
3176 g_printerr (" Single hash size %d: cell on which list of cells depend\n",
3177 g_hash_table_size (deps
->single_hash
));
3178 g_hash_table_foreach (deps
->single_hash
,
3183 if (deps
->dynamic_deps
&& g_hash_table_size (deps
->dynamic_deps
) > 0) {
3184 g_printerr (" Dynamic hash size %d: cells that depend on dynamic dependencies\n",
3185 g_hash_table_size (deps
->dynamic_deps
));
3186 g_hash_table_foreach (deps
->dynamic_deps
,
3187 dump_dynamic_dep
, NULL
);
3190 if (deps
->referencing_names
&& g_hash_table_size (deps
->referencing_names
) > 0) {
3191 GSList
*l
, *names
= NULL
;
3193 g_hash_table_foreach (deps
->referencing_names
,
3194 (GHFunc
)cb_collect_names
,
3197 g_printerr (" Names whose expressions explicitly reference this sheet\n ");
3198 for (l
= names
; l
; l
= l
->next
) {
3199 GnmNamedExpr
*nexpr
= l
->data
;
3201 expr_name_name (nexpr
),
3202 l
->next
? ", " : "\n");
3204 g_slist_free (names
);
3209 dependents_dump (Workbook
*wb
)
3211 WORKBOOK_FOREACH_SHEET
3215 SHEET_FOREACH_DEPENDENT (sheet
, dep
, count
++;);
3216 g_printerr ("Dependencies for %s (count=%d):\n",
3217 sheet
->name_unquoted
, count
);
3218 gnm_dep_container_dump (sheet
->deps
, sheet
);
3223 gnm_dep_container_sanity_check (GnmDepContainer
const *deps
)
3225 GnmDependent
const *dep
;
3228 if (deps
->head
&& !deps
->tail
)
3229 g_warning ("Dependency container %p has head, but no tail.", (void *)deps
);
3230 if (deps
->tail
&& !deps
->head
)
3231 g_warning ("Dependency container %p has tail, but no head.", (void *)deps
);
3232 if (deps
->head
&& deps
->head
->prev_dep
)
3233 g_warning ("Dependency container %p has head, but not at the beginning.", (void *)deps
);
3234 if (deps
->tail
&& deps
->tail
->next_dep
)
3235 g_warning ("Dependency container %p has tail, but not at the end.", (void *)deps
);
3237 seenb4
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
3238 for (dep
= deps
->head
; dep
; dep
= dep
->next_dep
) {
3239 if (dep
->prev_dep
&& (dep
->prev_dep
->next_dep
!= dep
))
3240 g_warning ("Dependency container %p has left double-link failure at %p.", (void *)deps
, (void *)dep
);
3241 if (dep
->next_dep
&& (dep
->next_dep
->prev_dep
!= dep
))
3242 g_warning ("Dependency container %p has right double-link failure at %p.", (void *)deps
, (void *)dep
);
3243 if (!dep
->next_dep
&& dep
!= deps
->tail
)
3244 g_warning ("Dependency container %p ends before its tail.", (void *)deps
);
3245 if (!dependent_is_linked (dep
))
3246 g_warning ("Dependency container %p contains unlinked dependency %p.", (void *)deps
, (void *)dep
);
3247 if (g_hash_table_lookup (seenb4
, dep
)) {
3248 g_warning ("Dependency container %p is circular.", (void *)deps
);
3251 g_hash_table_insert (seenb4
, (gpointer
)dep
, (gpointer
)dep
);
3253 g_hash_table_destroy (seenb4
);
3256 /* ------------------------------------------------------------------------- */