1 /*
2  * dependent.c:  Manage calculation dependencies between objects
3  *
4  * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
5  * Copyright (C) 2006-2020 Morten Welinder (terra@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) version 3.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22 #include <gnumeric-config.h>
23 #include <gnumeric.h>
24 #include <dependent.h>
25 
26 #include <workbook-priv.h>
27 #include <value.h>
28 #include <cell.h>
29 #include <sheet.h>
30 #include <expr.h>
31 #include <expr-impl.h>
32 #include <expr-name.h>
33 #include <application.h>
34 #include <workbook-view.h>
35 #include <ranges.h>
36 #include <gutils.h>
37 #include <sheet-view.h>
38 #include <func.h>
39 
40 #include <goffice/goffice.h>
41 #include <string.h>
42 
43 static gboolean gnm_cell_eval_content (GnmCell *cell);
44 static void dependent_changed (GnmDependent *dep);
45 static void dependent_clear_dynamic_deps (GnmDependent *dep);
46 
47 typedef enum {
48 	DEP_LINK_NON_SCALAR = 1,
49 
50 	DEP_LINK_LINK = 0x8000,
51 	DEP_LINK_UNLINK = 0
52 } DepLinkFlags;
53 
54 /* ------------------------------------------------------------------------- */
55 
56 /*
57  * Note: we unconditionally use pools for
58  *   deps->range_pool
59  *   deps->single_pool
60  * since we need the ability to free en masse.
61  */
62 
63 #ifndef USE_POOLS
64 #define USE_POOLS 0
65 #endif
66 
67 #if USE_POOLS
68 static GOMemChunk *micro_few_pool;
69 static GOMemChunk *cset_pool;
70 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
71 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
72 #define MICRO_HASH_FEW 3 /* Odd and small. */
73 #define NEW_FEW CHUNK_ALLOC (gpointer, micro_few_pool)
74 #define FREE_FEW(p) CHUNK_FREE (micro_few_pool, p)
75 #else
76 #define CHUNK_ALLOC(T,c) g_slice_new (T)
77 #define CHUNK_FREE(p,v) g_slice_free1 (sizeof(*v),(v))
78 #define MICRO_HASH_FEW 4 /* Even and small. */
79 #define NEW_FEW (gpointer *)g_slice_alloc (MICRO_HASH_FEW * sizeof (gpointer))
80 #define FREE_FEW(p) g_slice_free1 (MICRO_HASH_FEW * sizeof (gpointer), p)
81 #endif
82 
83 /* ------------------------------------------------------------------------- */
84 /* Maps between row numbers and bucket numbers.  */
85 
86 // The bucket size grows with row number:
87 //
88 // * The first 8 buckets have size 128
89 // * The next 8 buckets have size 256
90 // * The next 8 buckets have size 512
91 // * Etc.
92 //
93 // A 64k row sheet will have 49 buckets; a 1M row sheet will have 81.
94 
95 #define BUCKET_BASE_SIZE_BITS 7
96 #define BUCKET_BASE_SIZE (1u << BUCKET_BASE_SIZE_BITS)
97 #define BUCKET_REP_BITS 3
98 #define BUCKET_REP (1u << BUCKET_REP_BITS)
99 
100 static inline int
bucket_of_row(unsigned row)101 bucket_of_row (unsigned row)
102 {
103 	unsigned sb, sb0, i;
104 	unsigned sbs = BUCKET_REP * BUCKET_BASE_SIZE;
105 
106 #ifdef __GNUC__
107 	// Super block
108 	sb = __builtin_clz (1u) - __builtin_clz (1u + row / sbs);
109 
110 	// Super block start
111 	sb0 = (sbs << sb) - sbs;
112 #else
113 	sb0 = 0;
114 	for (sb = 0; TRUE; sb++) {
115 		unsigned next_sb0 = ((2u << sb) - 1) * sbs;
116 		if (row < next_sb0)
117 			break;
118 		sb0 = next_sb0;
119 	}
120 #endif
121 	// Index (0-7) within super block
122 	i = (row - sb0) >> (BUCKET_BASE_SIZE_BITS + sb);
123 
124 	return (int)(sb * BUCKET_REP + i);
125 }
126 
127 static inline int
bucket_start_row(unsigned b)128 bucket_start_row (unsigned b)
129 {
130 	unsigned sb = b / BUCKET_REP;
131 	unsigned s = ((BUCKET_REP + (b % BUCKET_REP)) << sb) - BUCKET_REP;
132 	return (int)(s << BUCKET_BASE_SIZE_BITS);
133 }
134 
135 static inline int
bucket_end_row(int b)136 bucket_end_row (int b)
137 {
138 	return bucket_start_row (b + 1) - 1;
139 }
140 
141 /* ------------------------------------------------------------------------- */
142 
143 /* Keep this odd */
144 #define CSET_SEGMENT_SIZE 29
145 
146 typedef struct _CSet CSet;
147 struct _CSet {
148         int count;
149         CSet *next;
150         gpointer data[CSET_SEGMENT_SIZE];
151         /* And one pointer for allocation overhead.  */
152 };
153 
154 #if 0
155 static gboolean
156 cset_find (CSet *list, gpointer datum)
157 {
158         while (list) {
159                 guint i = list->count;
160                 while (i-- > 0)
161                         if (list->data[i] == datum)
162                                 return TRUE;
163                 list = list->next;
164         }
165         return FALSE;
166 }
167 #endif
168 
169 static void
cset_free(CSet * list)170 cset_free (CSet *list)
171 {
172         while (list) {
173                 CSet *next = list->next;
174                 CHUNK_FREE (cset_pool, list);
175                 list = next;
176         }
177 }
178 
179 /* NOTE: takes reference.  */
180 static void
cset_insert(CSet ** list,gpointer datum)181 cset_insert (CSet **list, gpointer datum)
182 {
183 	CSet *cs = *list;
184         if (cs == NULL || cs->count == CSET_SEGMENT_SIZE) {
185                 CSet *h = *list = CHUNK_ALLOC (CSet, cset_pool);
186                 h->next = cs;
187                 h->count = 1;
188 		h->data[0] = datum;
189         } else
190 		cs->data[cs->count++] = datum;
191 }
192 
193 /* NOTE: takes reference.  Returns %TRUE if datum was already present.  */
194 static gboolean
cset_insert_checked(CSet ** list,gpointer datum)195 cset_insert_checked (CSet **list, gpointer datum)
196 {
197 	CSet *cs = *list;
198 	CSet *nonfull = NULL;
199 
200         while (cs) {
201                 guint i = cs->count;
202 		if (i != CSET_SEGMENT_SIZE)
203 			nonfull = cs;
204                 while (i-- > 0)
205                         if (cs->data[i] == datum)
206                                 return TRUE;
207                 cs = cs->next;
208         }
209 
210 	if (nonfull)
211 		nonfull->data[nonfull->count++] = datum;
212 	else
213 		cset_insert (list, datum);
214         return FALSE;
215 }
216 
217 
218 /* NOTE: takes reference.  Returns %TRUE if removed.  */
219 static gboolean
cset_remove(CSet ** list,gpointer datum)220 cset_remove (CSet **list, gpointer datum)
221 {
222         CSet *l, *last = NULL;
223 
224         for (l = *list; l; l = l->next) {
225                 guint i;
226 
227                 for (i = l->count; i-- > 0; )
228                         if (l->data[i] == datum) {
229                                 l->count--;
230                                 if (l->count == 0) {
231                                         if (last)
232                                                 last->next = l->next;
233                                         else
234                                                 *list = l->next;
235                                         CHUNK_FREE (cset_pool, l);
236                                 } else
237 					l->data[i] = l->data[l->count];
238                                 return TRUE;
239                         }
240                 last = l;
241         }
242         return FALSE;
243 }
244 
245 #define CSET_FOREACH(list,var,code)			\
246   do {							\
247         CSet *cs_;					\
248         for (cs_ = (list); cs_; cs_ = cs_->next) {	\
249 		guint i_;				\
250                 for (i_ = cs_->count; i_-- > 0; ) {	\
251                         var = cs_->data[i_];		\
252                         code				\
253                 }					\
254         }						\
255   } while (0)
256 
257 
258 /* ------------------------------------------------------------------------- */
259 
260 static void
gnm_dep_set_expr_undo_undo(GnmDependent * dep,GnmExprTop const * texpr)261 gnm_dep_set_expr_undo_undo (GnmDependent *dep, GnmExprTop const *texpr)
262 {
263 	dependent_set_expr (dep, texpr);
264 	dependent_link (dep);
265 	dependent_changed (dep);
266 }
267 
268 static GOUndo *
gnm_dep_set_expr_undo_new(GnmDependent * dep)269 gnm_dep_set_expr_undo_new (GnmDependent *dep)
270 {
271 	gnm_expr_top_ref (dep->texpr);
272 	return go_undo_binary_new (dep, (gpointer)dep->texpr,
273 				   (GOUndoBinaryFunc)gnm_dep_set_expr_undo_undo,
274 				   NULL,
275 				   (GFreeFunc)gnm_expr_top_unref);
276 }
277 
278 static GOUndo *
gnm_dep_unlink_undo_new(GSList * deps)279 gnm_dep_unlink_undo_new (GSList *deps)
280 {
281 	return go_undo_unary_new (deps,
282 				  (GOUndoUnaryFunc)dependents_link,
283 				  (GFreeFunc)g_slist_free);
284 }
285 
286 /* ------------------------------------------------------------------------- */
287 
288 #undef DEBUG_EVALUATION
289 
290 static void
dummy_dep_eval(G_GNUC_UNUSED GnmDependent * dep)291 dummy_dep_eval (G_GNUC_UNUSED GnmDependent *dep)
292 {
293 }
294 
295 static void cell_dep_eval (GnmDependent *dep);
296 static void cell_dep_set_expr (GnmDependent *dep, GnmExprTop const *new_texpr);
297 static GSList *cell_dep_changed (GnmDependent *dep);
298 static GnmCellPos *cell_dep_pos (GnmDependent const *dep);
299 static void cell_dep_debug_name (GnmDependent const *dep, GString *target);
300 static const GnmDependentClass cell_dep_class = {
301 	cell_dep_eval,
302 	cell_dep_set_expr,
303 	cell_dep_changed,
304 	cell_dep_pos,
305 	cell_dep_debug_name,
306 };
307 
308 static GSList *dynamic_dep_changed (GnmDependent *dep);
309 static void dynamic_dep_debug_name (GnmDependent const *dep, GString *target);
310 static const GnmDependentClass dynamic_dep_class = {
311 	dummy_dep_eval,
312 	NULL,
313 	dynamic_dep_changed,
314 	NULL,
315 	dynamic_dep_debug_name,
316 };
317 typedef struct {
318 	GnmDependent base;
319 	GnmDependent *container;
320 	GSList *ranges;
321 	GSList *singles;
322 } DynamicDep;
323 
324 static void name_dep_debug_name (GnmDependent const *dep, GString *target);
325 static const GnmDependentClass name_dep_class = {
326 	dummy_dep_eval,
327 	NULL,
328 	NULL,
329 	NULL,
330 	name_dep_debug_name,
331 };
332 
333 static void managed_dep_debug_name (GnmDependent const *dep, GString *target);
334 static const GnmDependentClass managed_dep_class = {
335 	dummy_dep_eval,
336 	NULL,
337 	NULL,
338 	NULL,
339 	managed_dep_debug_name,
340 };
341 
342 static GPtrArray *dep_classes = NULL;
343 
344 void
dependent_types_init(void)345 dependent_types_init (void)
346 {
347 	g_return_if_fail (dep_classes == NULL);
348 
349 	/* Init with a NULL class so we can access directly */
350 	dep_classes = g_ptr_array_new ();
351 	g_ptr_array_add	(dep_classes, NULL); /* bogus filler */
352 	g_ptr_array_add	(dep_classes, (gpointer)&cell_dep_class);
353 	g_ptr_array_add	(dep_classes, (gpointer)&dynamic_dep_class);
354 	g_ptr_array_add	(dep_classes, (gpointer)&name_dep_class);
355 	g_ptr_array_add	(dep_classes, (gpointer)&managed_dep_class);
356 
357 #if USE_POOLS
358 	micro_few_pool =
359 		go_mem_chunk_new ("micro few pool",
360 				  MICRO_HASH_FEW * sizeof (gpointer),
361 				  16 * 1024 - 128);
362 	cset_pool =
363 		go_mem_chunk_new ("cset pool",
364 				  sizeof (CSet),
365 				  16 * 1024 - 128);
366 #endif
367 }
368 
369 void
dependent_types_shutdown(void)370 dependent_types_shutdown (void)
371 {
372 	g_return_if_fail (dep_classes != NULL);
373 	g_ptr_array_free (dep_classes, TRUE);
374 	dep_classes = NULL;
375 
376 #if USE_POOLS
377 	go_mem_chunk_destroy (micro_few_pool, FALSE);
378 	micro_few_pool = NULL;
379 	go_mem_chunk_destroy (cset_pool, FALSE);
380 	cset_pool = NULL;
381 #endif
382 }
383 
384 /**
385  * dependent_register_type:
386  * @klass: A vtable
387  *
388  * Store the vtable and allocate an ID for a new class
389  * of dependents.
390  */
391 guint32
dependent_type_register(GnmDependentClass const * klass)392 dependent_type_register (GnmDependentClass const *klass)
393 {
394 	guint32 res;
395 
396 	g_return_val_if_fail (dep_classes != NULL, 0);
397 
398 	g_ptr_array_add	(dep_classes, (gpointer)klass);
399 	res = dep_classes->len-1;
400 
401 	g_return_val_if_fail (res <= DEPENDENT_TYPE_MASK, res);
402 
403 	return res;
404 }
405 
406 /*
407  * dependent_flag_recalc:
408  * @dep: the dependent that contains the expression needing recomputation.
409  *
410  * Marks @dep as needing recalculation
411  * NOTE : it does NOT recursively dirty dependencies.
412  */
413 #define dependent_flag_recalc(dep) \
414   do { (dep)->flags |= DEPENDENT_NEEDS_RECALC; } while (0)
415 
416 /**
417  * dependent_changed:
418  * @cell: the dependent that changed
419  *
420  * Queues a recalc.
421  */
422 static void
dependent_changed(GnmDependent * dep)423 dependent_changed (GnmDependent *dep)
424 {
425 	if (dep->sheet &&
426 	    dep->sheet->workbook->recursive_dirty_enabled)
427 		dependent_queue_recalc (dep);
428 	else
429 		dependent_flag_recalc (dep);
430 }
431 
432 /**
433  * dependent_set_expr:
434  * @dep: The dependent we are interested in.
435  * @new_texpr: new expression.
436  *
437  * When the expression associated with @dep needs to change this routine
438  * dispatches to the virtual handler, unlinking @dep if necessary beforehand.
439  * Adds a ref to @new_expr.
440  *
441  * NOTE : it does NOT relink dependents in case they are going to move later.
442  **/
443 void
dependent_set_expr(GnmDependent * dep,GnmExprTop const * new_texpr)444 dependent_set_expr (GnmDependent *dep, GnmExprTop const *new_texpr)
445 {
446 	int const t = dependent_type (dep);
447 	GnmDependentClass *klass = g_ptr_array_index (dep_classes, t);
448 
449 	if (dependent_is_linked (dep))
450 		dependent_unlink (dep);
451 	if (dep->flags & DEPENDENT_HAS_DYNAMIC_DEPS)
452 		dependent_clear_dynamic_deps (dep);
453 
454 	if (klass->set_expr)
455 		klass->set_expr (dep, new_texpr);
456 	else {
457 		if (new_texpr)
458 			gnm_expr_top_ref (new_texpr);
459 		if (dep->texpr)
460 			gnm_expr_top_unref (dep->texpr);
461 		dep->texpr = new_texpr;
462 		if (new_texpr)
463 			dependent_changed (dep);
464 	}
465 }
466 
467 gboolean
dependent_has_pos(GnmDependent const * dep)468 dependent_has_pos (GnmDependent const *dep)
469 {
470 	int const t = dependent_type (dep);
471 	GnmDependentClass *klass = g_ptr_array_index (dep_classes, t);
472 	return klass->pos != NULL;
473 }
474 
475 GnmCellPos const *
dependent_pos(GnmDependent const * dep)476 dependent_pos (GnmDependent const *dep)
477 {
478 	static GnmCellPos const dummy = { 0, 0 };
479 	int const t = dependent_type (dep);
480 	GnmDependentClass *klass = g_ptr_array_index (dep_classes, t);
481 
482 	return klass->pos ? klass->pos (dep) : &dummy;
483 }
484 
485 void
dependent_move(GnmDependent * dep,int dx,int dy)486 dependent_move (GnmDependent *dep, int dx, int dy)
487 {
488 	int const t = dependent_type (dep);
489 	GnmDependentClass *klass = g_ptr_array_index (dep_classes, t);
490 	GnmCellPos *pos;
491 
492 	g_return_if_fail (klass->pos != NULL);
493 	pos = klass->pos (dep);
494 	/* Need a virtual for this? */
495 	pos->col += dx;
496 	pos->row += dy;
497 }
498 
499 
500 /**
501  * dependent_set_sheet:
502  * @dep:
503  * @sheet:
504  */
505 void
dependent_set_sheet(GnmDependent * dep,Sheet * sheet)506 dependent_set_sheet (GnmDependent *dep, Sheet *sheet)
507 {
508 	g_return_if_fail (dep != NULL);
509 	g_return_if_fail (dep->sheet == NULL);
510 	g_return_if_fail (!dependent_is_linked (dep));
511 
512 	dep->sheet = sheet;
513 	if (dep->texpr) {
514 		dependent_link (dep);
515 		dependent_changed (dep);
516 	}
517 }
518 
519 static void
cb_cell_list_deps(GnmDependent * dep,gpointer user)520 cb_cell_list_deps (GnmDependent *dep, gpointer user)
521 {
522 	GSList **list = (GSList **)user;
523 	*list = g_slist_prepend (*list, dep);
524 }
525 
526 static GSList *
cell_list_deps(GnmCell const * cell)527 cell_list_deps (GnmCell const *cell)
528 {
529 	GSList *deps = NULL;
530 	cell_foreach_dep (cell, cb_cell_list_deps, &deps);
531 	return deps;
532 }
533 
534 static void
dependent_queue_recalc_main(GSList * work)535 dependent_queue_recalc_main (GSList *work)
536 {
537 	/*
538 	 * Work is now a list of marked cells whose dependencies need
539 	 * to be marked.  Marking early guarentees that we will not
540 	 * get duplicates.  (And it thus limits the length of the list.)
541 	 * We treat work as a stack.
542 	 */
543 
544 	while (work) {
545 		GnmDependent *dep = work->data;
546 		int const t = dependent_type (dep);
547 		GnmDependentClass *klass = g_ptr_array_index (dep_classes, t);
548 
549 		/* Pop the top element.  */
550 		work = g_slist_delete_link (work, work);
551 
552 		if (klass->changed) {
553 			GSList *extra = klass->changed (dep);
554 			if (extra) {
555 				g_slist_last (extra)->next = work;
556 				work = extra;
557 			}
558 		}
559 	}
560 }
561 
562 
563 /**
564  * dependent_queue_recalc_list:
565  * @list:
566  *
567  * Queues any elements of @list for recalc that are not already queued,
568  * and marks all elements as needing a recalc.
569  */
570 static void
dependent_queue_recalc_list(GSList * list)571 dependent_queue_recalc_list (GSList *list)
572 {
573 	GSList *work = NULL;
574 
575 	for (; list != NULL ; list = list->next) {
576 		GnmDependent *dep = list->data;
577 		if (!dependent_needs_recalc (dep)) {
578 			dependent_flag_recalc (dep);
579 			work = g_slist_prepend (work, dep);
580 		}
581 	}
582 
583 	dependent_queue_recalc_main (work);
584 }
585 
586 void
dependent_queue_recalc(GnmDependent * dep)587 dependent_queue_recalc (GnmDependent *dep)
588 {
589 	g_return_if_fail (dep != NULL);
590 
591 #ifdef DEBUG_EVALUATION
592 	g_printerr ("/* QUEUE (%s) */\n", cell_name (GNM_DEP_TO_CELL (dep)));
593 #endif
594 	if (!dependent_needs_recalc (dep)) {
595 		GSList listrec;
596 		listrec.next = NULL;
597 		listrec.data = dep;
598 		dependent_queue_recalc_list (&listrec);
599 	}
600 }
601 
602 /**************************************************************************/
603 
604 typedef struct {
605 	gint num_buckets;
606 	gint num_elements;
607 	union {
608 		gpointer one;
609 		gpointer *few;
610 		CSet **many;
611 	} u;
612 } MicroHash;
613 
614 #define MICRO_HASH_MIN_SIZE 11
615 #define MICRO_HASH_MAX_SIZE 13845163
616 
617 #define MICRO_HASH_hash(key) ((guint)GPOINTER_TO_UINT(key))
618 
619 static void
micro_hash_many_to_few(MicroHash * hash_table)620 micro_hash_many_to_few (MicroHash *hash_table)
621 {
622 	CSet **buckets = hash_table->u.many;
623 	int nbuckets = hash_table->num_buckets;
624 	int i = 0;
625 
626 	hash_table->u.few = NEW_FEW;
627 
628 	while (nbuckets-- > 0 ) {
629 		gpointer datum;
630 
631 		CSET_FOREACH (buckets[nbuckets], datum, {
632 			hash_table->u.few[i++] = datum;
633 		});
634 		cset_free (buckets[nbuckets]);
635 	}
636 
637 	g_free (buckets);
638 }
639 
640 static void
micro_hash_many_resize(MicroHash * hash_table,int new_nbuckets)641 micro_hash_many_resize (MicroHash *hash_table, int new_nbuckets)
642 {
643 	CSet **buckets = hash_table->u.many;
644 	int nbuckets = hash_table->num_buckets;
645 	CSet **new_buckets = g_new0 (CSet *, new_nbuckets);
646 
647 	hash_table->u.many = new_buckets;
648 	hash_table->num_buckets = new_nbuckets;
649 
650 	while (nbuckets-- > 0 ) {
651 		gpointer datum;
652 
653 		CSET_FOREACH (buckets[nbuckets], datum, {
654 			guint bucket = MICRO_HASH_hash (datum) % new_nbuckets;
655 			cset_insert (&(new_buckets[bucket]), datum);
656 		});
657 		cset_free (buckets[nbuckets]);
658 	}
659 	g_free (buckets);
660 
661 #if 0
662 	{
663 		int nonzero = 0;
664 		int capacity = 0, totlen = 0;
665 		int i;
666 
667 		for (i = 0; i < new_nbuckets; i++) {
668 			CSet *cs = new_buckets[i];
669 			if (cs) {
670 				nonzero++;
671 				while (cs) {
672 					totlen += cs->count;
673 					capacity += CSET_SEGMENT_SIZE;
674 					cs = cs->next;
675 				}
676 			}
677 		}
678 
679 		g_printerr ("resize %p: %d [%d %.1f %.0f%%]\n",
680 			    hash_table,
681 			    new_nbuckets,
682 			    hash_table->num_elements,
683 			    (double)totlen / nonzero,
684 			    100.0 * totlen / capacity);
685 	}
686 #endif
687 }
688 
689 
690 static void
micro_hash_few_to_many(MicroHash * hash_table)691 micro_hash_few_to_many (MicroHash *hash_table)
692 {
693 	int nbuckets = hash_table->num_buckets = MICRO_HASH_MIN_SIZE;
694 	CSet **buckets = g_new0 (CSet *, nbuckets);
695 	int i;
696 
697 	for (i = 0; i < hash_table->num_elements; i++) {
698 		gpointer datum = hash_table->u.few[i];
699 		guint bucket = MICRO_HASH_hash (datum) % nbuckets;
700 		cset_insert (&(buckets[bucket]), datum);
701 	}
702 	FREE_FEW (hash_table->u.few);
703 	hash_table->u.many = buckets;
704 }
705 
706 
707 
708 static void
micro_hash_insert(MicroHash * hash_table,gpointer key)709 micro_hash_insert (MicroHash *hash_table, gpointer key)
710 {
711 	int N = hash_table->num_elements;
712 
713 	g_return_if_fail (key != NULL);
714 
715 	if (N == 0) {
716 		hash_table->u.one = key;
717 	} else if (N == 1) {
718 		gpointer key0 = hash_table->u.one;
719 		if (key == key0)
720 			return;
721 		/* one --> few */
722 		hash_table->u.few = NEW_FEW;
723 		hash_table->u.few[0] = key0;
724 		hash_table->u.few[1] = key;
725 		memset (hash_table->u.few + 2, 0, (MICRO_HASH_FEW - 2) * sizeof (gpointer));
726 	} else if (N <= MICRO_HASH_FEW) {
727 		int i;
728 
729 		for (i = 0; i < N; i++)
730 			if (hash_table->u.few[i] == key)
731 				return;
732 
733 		if (N == MICRO_HASH_FEW) {
734 			guint bucket;
735 
736 			micro_hash_few_to_many (hash_table);
737 			bucket = MICRO_HASH_hash (key) % hash_table->num_buckets;
738 			cset_insert (&(hash_table->u.many[bucket]), key);
739 		} else
740 			hash_table->u.few[N] = key;
741 	} else {
742 		int nbuckets = hash_table->num_buckets;
743 		guint bucket = MICRO_HASH_hash (key) % nbuckets;
744 		CSet **buckets = hash_table->u.many;
745 
746 		if (cset_insert_checked (&(buckets[bucket]), key))
747 			return;
748 
749 		if (N > CSET_SEGMENT_SIZE * nbuckets &&
750 		    nbuckets < MICRO_HASH_MAX_SIZE) {
751 			int new_nbuckets = g_spaced_primes_closest (N / (CSET_SEGMENT_SIZE / 2));
752 			if (new_nbuckets > MICRO_HASH_MAX_SIZE)
753 				new_nbuckets = MICRO_HASH_MAX_SIZE;
754 			micro_hash_many_resize (hash_table, new_nbuckets);
755 		}
756 	}
757 
758 	hash_table->num_elements++;
759 }
760 
761 static void
micro_hash_remove(MicroHash * hash_table,gpointer key)762 micro_hash_remove (MicroHash *hash_table, gpointer key)
763 {
764 	int N = hash_table->num_elements;
765 	guint bucket;
766 
767 	if (N == 0)
768 		return;
769 
770 	if (N == 1) {
771 		if (hash_table->u.one != key)
772 			return;
773 		hash_table->u.one = NULL;
774 		hash_table->num_elements--;
775 		return;
776 	}
777 
778 	if (N <= MICRO_HASH_FEW) {
779 		int i;
780 
781 		for (i = 0; i < N; i++)
782 			if (hash_table->u.few[i] == key) {
783 				hash_table->u.few[i] = hash_table->u.few[N - 1];
784 				hash_table->num_elements--;
785 				if (hash_table->num_elements > 1)
786 					return;
787 				/* few -> one */
788 				key = hash_table->u.few[0];
789 				FREE_FEW (hash_table->u.few);
790 				hash_table->u.one = key;
791 				return;
792 			}
793 		return;
794 	}
795 
796 	bucket = MICRO_HASH_hash (key) % hash_table->num_buckets;
797 	if (cset_remove (&(hash_table->u.many[bucket]), key)) {
798 		hash_table->num_elements--;
799 
800 		if (hash_table->num_elements <= MICRO_HASH_FEW)
801 			micro_hash_many_to_few (hash_table);
802 		else {
803 			/* Maybe resize? */
804 		}
805 		return;
806 	}
807 }
808 
809 
810 static void
micro_hash_release(MicroHash * hash_table)811 micro_hash_release (MicroHash *hash_table)
812 {
813 	int N = hash_table->num_elements;
814 
815 	if (N <= 1)
816 		; /* Nothing */
817 	else if (N <= MICRO_HASH_FEW)
818 		FREE_FEW (hash_table->u.few);
819 	else {
820 		guint i = hash_table->num_buckets;
821 		while (i-- > 0)
822 			cset_free (hash_table->u.many[i]);
823 		g_free (hash_table->u.many);
824 	}
825 	hash_table->num_elements = 0;
826 	hash_table->num_buckets = 1;
827 	hash_table->u.one = NULL;
828 }
829 
830 static void
micro_hash_init(MicroHash * hash_table,gpointer key)831 micro_hash_init (MicroHash *hash_table, gpointer key)
832 {
833 	hash_table->num_elements = 1;
834 	hash_table->u.one = key;
835 }
836 
837 static inline gboolean
micro_hash_is_empty(MicroHash const * hash_table)838 micro_hash_is_empty (MicroHash const *hash_table)
839 {
840 	return hash_table->num_elements == 0;
841 }
842 
843 /*************************************************************************/
844 
845 #define micro_hash_foreach_dep(dc, dep, code) do {			\
846 	guint i_ = dc.num_elements;					\
847 	if (i_ <= MICRO_HASH_FEW) {					\
848 		const gpointer *e_ = (i_ == 1) ? &dc.u.one : dc.u.few;	\
849 		while (i_-- > 0) {					\
850 			GnmDependent *dep = e_[i_];			\
851 			code						\
852 		}							\
853 	} else {							\
854 		GnmDependent *dep;					\
855 		guint b_ = dc.num_buckets;				\
856 		while (b_-- > 0)					\
857 			CSET_FOREACH (dc.u.many[b_], dep, code);	\
858 	}								\
859 } while (0)
860 
861 /**************************************************************************
862  * Data structures for managing dependencies between objects.
863  *
864  * The DependencyRange hash needs to be improved.  It is a huge
865  * performance hit when there are large numbers of range depends.
866  */
867 
868 /*
869  * A DependencyRange defines a range of cells whose values
870  * are used by another objects in the spreadsheet.
871  *
872  * A change in those cells will trigger a recomputation on the
873  * cells listed in deps.
874  */
875 typedef struct {
876 	MicroHash	deps;	/* Must be first */
877 	GnmRange  range;
878 } DependencyRange;
879 
880 /*
881  *  A DependencySingle stores a list of dependents that rely
882  * on the cell at @pos.
883  *
884  * A change in this cell will trigger a recomputation on the
885  * cells listed in deps.
886  */
887 typedef struct {
888 	MicroHash	deps;	/* Must be first */
889 	GnmCellPos pos;
890 } DependencySingle;
891 
892 /* A utility type */
893 typedef struct {
894 	MicroHash	deps;	/* Must be first */
895 } DependencyAny;
896 
897 static guint
deprange_hash(DependencyRange const * r)898 deprange_hash (DependencyRange const *r)
899 {
900 	guint a = r->range.start.row;
901 	guint b = r->range.end.row;
902 	guint c = r->range.start.col;
903 	guint d = r->range.end.col;
904 
905 	return (((((a << 8) + b) << 8) + c) << 8) + d;
906 }
907 
908 static gint
deprange_equal(DependencyRange const * r1,DependencyRange const * r2)909 deprange_equal (DependencyRange const *r1, DependencyRange const *r2)
910 {
911 	return range_equal (&(r1->range), &(r2->range));
912 }
913 
914 static guint
depsingle_hash(DependencySingle const * depsingle)915 depsingle_hash (DependencySingle const *depsingle)
916 {
917 	return (depsingle->pos.row << 8) ^ depsingle->pos.col;
918 }
919 
920 static gint
depsingle_equal(DependencySingle const * a,DependencySingle const * b)921 depsingle_equal (DependencySingle const *a, DependencySingle const *b)
922 {
923 	return (a->pos.row == b->pos.row && a->pos.col == b->pos.col);
924 }
925 
926 static GnmDependentFlags
link_single_dep(GnmDependent * dep,GnmCellPos const * pos,GnmCellRef const * ref)927 link_single_dep (GnmDependent *dep, GnmCellPos const *pos, GnmCellRef const *ref)
928 {
929 	DependencySingle lookup;
930 	DependencySingle *single;
931 	GnmDependentFlags flag = DEPENDENT_NO_FLAG;
932 	Sheet const *sheet = eval_sheet (ref->sheet, dep->sheet);
933 	GnmDepContainer *deps = sheet->deps;
934 
935 	if (sheet != dep->sheet)
936 		flag = (sheet->workbook != dep->sheet->workbook)
937 			? DEPENDENT_GOES_INTERBOOK
938 			: DEPENDENT_GOES_INTERSHEET;
939 
940 	/* Inserts if it is not already there */
941 	gnm_cellpos_init_cellref (&lookup.pos, ref, pos, sheet);
942 	single = g_hash_table_lookup (deps->single_hash, &lookup);
943 	if (single == NULL) {
944 		single = go_mem_chunk_alloc (deps->single_pool);
945 		*single = lookup;
946 		micro_hash_init (&single->deps, dep);
947 		g_hash_table_insert (deps->single_hash, single, single);
948 	} else
949 		micro_hash_insert (&single->deps, dep);
950 
951 	return flag;
952 }
953 
954 static GnmDependentFlags
unlink_single_dep(GnmDependent * dep,GnmCellPos const * pos,GnmCellRef const * a)955 unlink_single_dep (GnmDependent *dep, GnmCellPos const *pos, GnmCellRef const *a)
956 {
957 	DependencySingle lookup;
958 	DependencySingle *single;
959 	GnmDependentFlags flag = DEPENDENT_NO_FLAG;
960 	Sheet const *sheet = eval_sheet (a->sheet, dep->sheet);
961 	GnmDepContainer *deps = sheet->deps;
962 
963 	if (sheet != dep->sheet)
964 		flag = (sheet->workbook != dep->sheet->workbook)
965 			? DEPENDENT_GOES_INTERBOOK
966 			: DEPENDENT_GOES_INTERSHEET;
967 	if (!deps)
968 		return flag;
969 
970 	gnm_cellpos_init_cellref (&lookup.pos, a, pos, sheet);
971 	single = g_hash_table_lookup (deps->single_hash, &lookup);
972 	if (single != NULL) {
973 		micro_hash_remove (&single->deps, dep);
974 		if (micro_hash_is_empty (&single->deps)) {
975 			g_hash_table_remove (deps->single_hash, single);
976 			micro_hash_release (&single->deps);
977 			go_mem_chunk_free (deps->single_pool, single);
978 		}
979 	}
980 
981 	return flag;
982 }
983 
984 static inline GnmDependentFlags
link_unlink_single_dep(GnmDependent * dep,GnmCellPos const * pos,GnmCellRef const * a,DepLinkFlags flags)985 link_unlink_single_dep (GnmDependent *dep, GnmCellPos const *pos,
986 			GnmCellRef const *a, DepLinkFlags flags)
987 {
988 	return (flags & DEP_LINK_LINK)
989 		? link_single_dep (dep, pos, a)
990 		: unlink_single_dep (dep, pos, a);
991 }
992 
993 
994 static void
link_range_dep(GnmDepContainer * deps,GnmDependent * dep,GnmRange const * r)995 link_range_dep (GnmDepContainer *deps, GnmDependent *dep,
996 		GnmRange const *r)
997 {
998 	int i = bucket_of_row (r->start.row);
999 	int end = bucket_of_row (r->end.row);
1000 	DependencyRange dr;
1001 	int next_start;
1002 
1003 	dr.range = *r;
1004 
1005 	/*
1006 	 * It is possible to see ranges bigger than the sheet when
1007 	 * operating with 3D ranges.  See bug #704109.
1008 	 */
1009 	end = MIN (end, deps->buckets - 1);
1010 
1011 	if (i > end)
1012 		return;
1013 
1014 	next_start = bucket_start_row (i);
1015 	for ( ; i <= end; i++) {
1016 		DependencyRange *result;
1017 		int start = next_start;
1018 		next_start = bucket_start_row (i + 1);
1019 
1020 		/* Restrict range to bucket.  */
1021 		dr.range.start.row = MAX (r->start.row, start);
1022 		dr.range.end.row = MIN (r->end.row, next_start - 1);
1023 
1024 		if (deps->range_hash[i] == NULL)
1025 			deps->range_hash[i] = g_hash_table_new (
1026 				(GHashFunc)  deprange_hash,
1027 				(GEqualFunc) deprange_equal);
1028 		else {
1029 			result = g_hash_table_lookup (deps->range_hash[i], &dr);
1030 			if (result) {
1031 				/* Inserts if it is not already there */
1032 				micro_hash_insert (&result->deps, dep);
1033 				continue;
1034 			}
1035 		}
1036 
1037 		/* Create a new DependencyRange structure */
1038 		result = go_mem_chunk_alloc (deps->range_pool);
1039 		*result = dr;
1040 		micro_hash_init (&result->deps, dep);
1041 		g_hash_table_insert (deps->range_hash[i], result, result);
1042 	}
1043 }
1044 
1045 static void
unlink_range_dep(GnmDepContainer * deps,GnmDependent * dep,GnmRange const * r)1046 unlink_range_dep (GnmDepContainer *deps, GnmDependent *dep,
1047 		  GnmRange const *r)
1048 {
1049 	int i = bucket_of_row (r->start.row);
1050 	int end = bucket_of_row (r->end.row);
1051 	DependencyRange dr;
1052 	int next_start;
1053 
1054 	if (!deps)
1055 		return;
1056 	dr.range = *r;
1057 
1058 	end = MIN (end, deps->buckets - 1);
1059 
1060 	if (i > end)
1061 		return;
1062 
1063 	next_start = bucket_start_row (i);
1064 	for ( ; i <= end; i++) {
1065 		DependencyRange *result;
1066 		int start = next_start;
1067 		next_start = bucket_start_row (i + 1);
1068 
1069 		/* Restrict range to bucket.  */
1070 		dr.range.start.row = MAX (r->start.row, start);
1071 		dr.range.end.row = MIN (r->end.row, next_start - 1);
1072 
1073 		result = g_hash_table_lookup (deps->range_hash[i], &dr);
1074 		if (result) {
1075 			micro_hash_remove (&result->deps, dep);
1076 			if (micro_hash_is_empty (&result->deps)) {
1077 				g_hash_table_remove (deps->range_hash[i], result);
1078 				micro_hash_release (&result->deps);
1079 				go_mem_chunk_free (deps->range_pool, result);
1080 			}
1081 		}
1082 	}
1083 }
1084 
1085 static inline void
link_unlink_range_dep(GnmDepContainer * deps,GnmDependent * dep,GnmRange const * r,DepLinkFlags flags)1086 link_unlink_range_dep (GnmDepContainer *deps, GnmDependent *dep,
1087 		       GnmRange const *r, DepLinkFlags flags)
1088 {
1089 	if (flags & DEP_LINK_LINK)
1090 		link_range_dep (deps, dep, r);
1091 	else
1092 		unlink_range_dep (deps, dep, r);
1093 }
1094 
1095 static GnmDependentFlags
link_unlink_cellrange_dep(GnmDependent * dep,GnmCellPos const * pos,GnmCellRef const * a,GnmCellRef const * b,DepLinkFlags flags)1096 link_unlink_cellrange_dep (GnmDependent *dep, GnmCellPos const *pos,
1097 			   GnmCellRef const *a, GnmCellRef const *b,
1098 			   DepLinkFlags flags)
1099 {
1100 	GnmRange range;
1101 	GnmDependentFlags flag = DEPENDENT_NO_FLAG;
1102 
1103 	gnm_cellpos_init_cellref (&range.start, a, pos, dep->sheet);
1104 	gnm_cellpos_init_cellref (&range.end, b, pos, dep->sheet);
1105 	range_normalize (&range);
1106 
1107 	if (a->sheet != NULL) {
1108 		if (a->sheet != dep->sheet)
1109 			flag = (a->sheet->workbook != dep->sheet->workbook)
1110 				? DEPENDENT_GOES_INTERBOOK : DEPENDENT_GOES_INTERSHEET;
1111 
1112 		if (b->sheet != NULL && a->sheet != b->sheet) {
1113 			Workbook const *wb = a->sheet->workbook;
1114 			int i = a->sheet->index_in_wb;
1115 			int stop = b->sheet->index_in_wb;
1116 			if (i > stop) { int tmp = i; i = stop ; stop = tmp; }
1117 
1118 			g_return_val_if_fail (b->sheet->workbook == wb, flag);
1119 
1120 			while (i <= stop) {
1121 				Sheet *sheet = g_ptr_array_index (wb->sheets, i);
1122 				i++;
1123 				link_unlink_range_dep (sheet->deps, dep, &range,
1124 						       flags);
1125 			}
1126 			flag |= DEPENDENT_HAS_3D;
1127 		} else
1128 			link_unlink_range_dep (a->sheet->deps, dep, &range,
1129 					       flags);
1130 	} else
1131 		link_unlink_range_dep (dep->sheet->deps, dep, &range, flags);
1132 
1133 	return flag;
1134 }
1135 
1136 static GnmDependentFlags
1137 link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, DepLinkFlags flags);
1138 
1139 static GnmDependentFlags
link_unlink_constant(GnmEvalPos * ep,GnmValue const * v,DepLinkFlags flags)1140 link_unlink_constant (GnmEvalPos *ep, GnmValue const *v, DepLinkFlags flags)
1141 {
1142 	GnmRange r;
1143 	Sheet *start_sheet, *end_sheet;
1144 	int col, row;
1145 	gboolean found = FALSE;
1146 	GnmCellRef cr;
1147 
1148 	if (!VALUE_IS_CELLRANGE (v))
1149 		return DEPENDENT_NO_FLAG;
1150 
1151 	if (!dependent_is_cell (ep->dep))
1152 		goto everything; // Must figure out semantics first
1153 
1154 	if (flags & DEP_LINK_NON_SCALAR)
1155 		goto everything;
1156 
1157 	if (eval_pos_is_array_context (ep))
1158 		goto everything; // Potential implicit iteration -- bail
1159 
1160 	// Inspiration from value_intersection:
1161 
1162 	gnm_rangeref_normalize (&v->v_range.cell, ep, &start_sheet, &end_sheet, &r);
1163 	if (!(start_sheet == end_sheet || end_sheet == NULL))
1164 		return DEPENDENT_NO_FLAG; // An error
1165 
1166 	col = ep->eval.col;
1167 	row = ep->eval.row;
1168 
1169 	if (range_is_singleton (&r)) {
1170 		col = r.start.col;
1171 		row = r.start.row;
1172 		found = TRUE;
1173 	} else if (r.start.row == r.end.row &&
1174 		   r.start.col <= col && col <= r.end.col) {
1175 		row = r.start.row;
1176 		found = TRUE;
1177 	} else if (r.start.col == r.end.col &&
1178 		   r.start.row <= row && row <= r.end.row) {
1179 		col = r.start.col;
1180 		found = TRUE;
1181 	}
1182 	if (!found)
1183 		goto everything;
1184 
1185 	cr.col_relative = cr.row_relative = FALSE;
1186 	cr.sheet = ep->dep->sheet;
1187 	cr.col = col;
1188 	cr.row = row;
1189 
1190 	return link_unlink_single_dep (ep->dep, dependent_pos (ep->dep),
1191 				       &cr, flags);
1192 
1193 everything:
1194 	return link_unlink_cellrange_dep
1195 		(ep->dep, dependent_pos (ep->dep),
1196 		 &v->v_range.cell.a,
1197 		 &v->v_range.cell.b,
1198 		 flags);
1199 }
1200 
1201 static GnmDependentFlags
link_unlink_funcall(GnmEvalPos * ep,GnmExprFunction const * call,DepLinkFlags flags)1202 link_unlink_funcall (GnmEvalPos *ep, GnmExprFunction const *call, DepLinkFlags flags)
1203 {
1204 	int i;
1205 	GnmFuncEvalInfo fei;
1206 	GnmDependentFlags flag;
1207 	DepLinkFlags pass = (flags & (DEP_LINK_LINK | DEP_LINK_UNLINK));
1208 
1209 	gnm_func_load_if_stub (call->func);
1210 	fei.pos = ep;
1211 	fei.func_call = call;
1212 	flag = gnm_func_link_dep (call->func, &fei, (flags & DEP_LINK_LINK) != 0);
1213 	if (flag & DEPENDENT_IGNORE_ARGS)
1214 		return (flag & ~DEPENDENT_IGNORE_ARGS);
1215 
1216 	for (i = 0; i < call->argc; i++) {
1217 		char t = gnm_func_get_arg_type (call->func, i);
1218 		DepLinkFlags extra =
1219 			(t == 'A' || t == 'r' || t == '?')
1220 			? DEP_LINK_NON_SCALAR
1221 			: 0;
1222 
1223 		if (0)
1224 			g_printerr ("%s, arg %d: %c\n",
1225 				    gnm_func_get_name (call->func, FALSE),
1226 				    i, t);
1227 		flag |= link_unlink_expr_dep (ep, call->argv[i], pass | extra);
1228 	}
1229 	return flag;
1230 }
1231 
1232 
1233 static GnmDependentFlags
link_unlink_expr_dep(GnmEvalPos * ep,GnmExpr const * tree,DepLinkFlags flags)1234 link_unlink_expr_dep (GnmEvalPos *ep, GnmExpr const *tree, DepLinkFlags flags)
1235 {
1236 	g_return_val_if_fail (tree != NULL, DEPENDENT_NO_FLAG);
1237 
1238 	switch (GNM_EXPR_GET_OPER (tree)) {
1239 	case GNM_EXPR_OP_RANGE_CTOR:
1240 	case GNM_EXPR_OP_INTERSECT:
1241 		return (link_unlink_expr_dep (ep, tree->binary.value_a, flags) |
1242 			link_unlink_expr_dep (ep, tree->binary.value_b, flags));
1243 	case GNM_EXPR_OP_ANY_BINARY:
1244 		// See comments in function_iterate_argument_values
1245 		if (!eval_pos_is_array_context (ep))
1246 			flags &= ~DEP_LINK_NON_SCALAR;
1247 		return (link_unlink_expr_dep (ep, tree->binary.value_a, flags) |
1248 			link_unlink_expr_dep (ep, tree->binary.value_b, flags));
1249 	case GNM_EXPR_OP_ANY_UNARY:
1250 		if (!eval_pos_is_array_context (ep))
1251 			flags &= ~DEP_LINK_NON_SCALAR;
1252 		return link_unlink_expr_dep (ep, tree->unary.value, flags);
1253 	case GNM_EXPR_OP_CELLREF:
1254 		return link_unlink_single_dep (ep->dep, dependent_pos (ep->dep), &tree->cellref.ref, flags);
1255 
1256 	case GNM_EXPR_OP_CONSTANT:
1257 		return link_unlink_constant (ep, tree->constant.value, flags);
1258 
1259 	case GNM_EXPR_OP_FUNCALL:
1260 		return link_unlink_funcall (ep, &tree->func, flags);
1261 
1262 	case GNM_EXPR_OP_NAME:
1263 		if (flags & DEP_LINK_LINK)
1264 			expr_name_add_dep (tree->name.name, ep->dep);
1265 		else
1266 			expr_name_remove_dep (tree->name.name, ep->dep);
1267 		if (expr_name_is_active (tree->name.name))
1268 			return link_unlink_expr_dep (ep, tree->name.name->texpr->expr, flags) | DEPENDENT_USES_NAME;
1269 		return DEPENDENT_USES_NAME;
1270 
1271 	case GNM_EXPR_OP_ARRAY_ELEM: {
1272 		/* Non-corner cells depend on the corner */
1273 		GnmCellRef a;
1274 		GnmCellPos const *pos = dependent_pos (ep->dep);
1275 		// We cannot support array expressions unless we have
1276 		// a position.
1277 		g_return_val_if_fail (pos != NULL, DEPENDENT_NO_FLAG);
1278 
1279 		a.col_relative = a.row_relative = FALSE;
1280 		a.sheet = ep->dep->sheet;
1281 		a.col   = pos->col - tree->array_elem.x;
1282 		a.row   = pos->row - tree->array_elem.y;
1283 
1284 		return link_unlink_single_dep (ep->dep, pos, &a, flags);
1285 	}
1286 
1287 	case GNM_EXPR_OP_ARRAY_CORNER: {
1288 		// It's mildly unclean to do this here.  We need the texpr, so get the cell.
1289 		GnmCellPos const *cpos = dependent_pos (ep->dep);
1290 		GnmCell const *cell = sheet_cell_get (ep->dep->sheet, cpos->col, cpos->row);
1291 		GnmEvalPos pos = *ep;
1292 		pos.array_texpr = cell->base.texpr;
1293 		/* Corner cell depends on the contents of the expr */
1294 		return link_unlink_expr_dep (&pos, tree->array_corner.expr,
1295 					     flags | DEP_LINK_NON_SCALAR);
1296 	}
1297 
1298 	case GNM_EXPR_OP_SET: {
1299 		int i;
1300 		GnmDependentFlags res = DEPENDENT_NO_FLAG;
1301 
1302 		// gnm_expr_eval ignores arguments unless non-scalar
1303 		if (flags & DEP_LINK_NON_SCALAR) {
1304 			for (i = 0; i < tree->set.argc; i++)
1305 				res |= link_unlink_expr_dep (ep, tree->set.argv[i], flags);
1306 		}
1307 
1308 		return res;
1309 	}
1310 #ifndef DEBUG_SWITCH_ENUM
1311 	default:
1312 		g_assert_not_reached ();
1313 #endif
1314 	}
1315 	return DEPENDENT_NO_FLAG;
1316 }
1317 
1318 static void
workbook_link_3d_dep(GnmDependent * dep)1319 workbook_link_3d_dep (GnmDependent *dep)
1320 {
1321 	Workbook *wb = dep->sheet->workbook;
1322 
1323 	if (wb->being_reordered)
1324 		return;
1325 	if (wb->sheet_order_dependents == NULL)
1326 		wb->sheet_order_dependents =
1327 			g_hash_table_new (g_direct_hash, g_direct_equal);
1328 	g_hash_table_insert (wb->sheet_order_dependents, dep, dep);
1329 }
1330 
1331 static void
workbook_unlink_3d_dep(GnmDependent * dep)1332 workbook_unlink_3d_dep (GnmDependent *dep)
1333 {
1334 	Workbook *wb = dep->sheet->workbook;
1335 
1336 	/* during destruction */
1337 	if (wb->sheet_order_dependents == NULL)
1338 		return;
1339 
1340 	if (wb->being_reordered)
1341 		return;
1342 
1343 	g_hash_table_remove (wb->sheet_order_dependents, dep);
1344 }
1345 
1346 /*****************************************************************************/
1347 
1348 static void
cell_dep_eval(GnmDependent * dep)1349 cell_dep_eval (GnmDependent *dep)
1350 {
1351 	gboolean finished = gnm_cell_eval_content (GNM_DEP_TO_CELL (dep));
1352 	(void)finished; /* We don't currently care */
1353 	dep->flags &= ~GNM_CELL_HAS_NEW_EXPR;
1354 }
1355 
1356 static void
cell_dep_set_expr(GnmDependent * dep,GnmExprTop const * new_texpr)1357 cell_dep_set_expr (GnmDependent *dep, GnmExprTop const *new_texpr)
1358 {
1359 	/*
1360 	 * Explicitly do not check for array subdivision, we may be
1361 	 * replacing the corner of an array.
1362 	 */
1363 	gnm_cell_set_expr_unsafe (GNM_DEP_TO_CELL (dep), new_texpr);
1364 }
1365 
1366 static GSList *
cell_dep_changed(GnmDependent * dep)1367 cell_dep_changed (GnmDependent *dep)
1368 {
1369 	/* When a cell changes, so do its dependents.  */
1370 
1371 	GSList *deps = cell_list_deps (GNM_DEP_TO_CELL (dep));
1372 	GSList *waste = NULL, *work = NULL;
1373 	GSList *next, *list;
1374 	for (list = deps; list != NULL ; list = next) {
1375 		GnmDependent *dep = list->data;
1376 		next = list->next;
1377 		if (dependent_needs_recalc (dep)) {
1378 			list->next = waste;
1379 			waste = list;
1380 		} else {
1381 			dependent_flag_recalc (dep);
1382 			list->next = work;
1383 			work = list;
1384 		}
1385 	}
1386 	g_slist_free (waste);
1387 	return work;
1388 }
1389 
1390 static GnmCellPos *
cell_dep_pos(GnmDependent const * dep)1391 cell_dep_pos (GnmDependent const *dep)
1392 {
1393 	return &GNM_DEP_TO_CELL (dep)->pos;
1394 }
1395 
1396 static void
cell_dep_debug_name(GnmDependent const * dep,GString * target)1397 cell_dep_debug_name (GnmDependent const *dep, GString *target)
1398 {
1399 	g_string_append (target, cell_name (GNM_DEP_TO_CELL (dep)));
1400 }
1401 
1402 /*****************************************************************************/
1403 /*
1404  * "Managed" dependent handling.
1405  *
1406  * A managed dependent is simply an expression set up to be maintained by
1407  * the dependency system so it follows row/column insert/delete nicely.
1408  *
1409  * The expression being managed is typically a cell range, but can be any
1410  * expression.  There are two versions: only with a position and one without.
1411  * In the no-position version, everything is interpreted relative to A1 in the
1412  * sheet.
1413  */
1414 
1415 void
dependent_managed_init(GnmDepManaged * dep,Sheet * sheet)1416 dependent_managed_init (GnmDepManaged *dep, Sheet *sheet)
1417 {
1418 	memset (dep, 0, sizeof (*dep));
1419 	dep->base.flags = DEPENDENT_MANAGED;
1420 	dep->base.sheet = sheet;
1421 }
1422 
1423 GnmExprTop const *
dependent_managed_get_expr(GnmDepManaged const * dep)1424 dependent_managed_get_expr (GnmDepManaged const *dep)
1425 {
1426 	g_return_val_if_fail (dep != NULL, NULL);
1427 	return dep->base.texpr;
1428 }
1429 
1430 void
dependent_managed_set_expr(GnmDepManaged * dep,GnmExprTop const * texpr)1431 dependent_managed_set_expr (GnmDepManaged *dep, GnmExprTop const *texpr)
1432 {
1433 	g_return_if_fail (dep != NULL);
1434 #if 0
1435 	/* We need some kind of IS_A */
1436 	g_return_if_fail (dependent_type (dep) == DEPENDENT_MANAGED);
1437 #endif
1438 
1439 	dependent_set_expr (&dep->base, texpr);
1440 	if (texpr && dep->base.sheet)
1441 		dependent_link (&dep->base);
1442 }
1443 
1444 void
dependent_managed_set_sheet(GnmDepManaged * dep,Sheet * sheet)1445 dependent_managed_set_sheet (GnmDepManaged *dep, Sheet *sheet)
1446 {
1447 	GnmExprTop const *texpr;
1448 
1449 	g_return_if_fail (dep != NULL);
1450 #if 0
1451 	/* We need some kind of IS_A */
1452 	g_return_if_fail (dependent_type (dep) == DEPENDENT_MANAGED);
1453 #endif
1454 
1455 	if (dep->base.sheet == sheet)
1456 		return;
1457 
1458 	texpr = dep->base.texpr;
1459 	if (texpr) gnm_expr_top_ref (texpr);
1460 	dependent_set_expr (&dep->base, NULL);
1461 	/* We're now unlinked from everything. */
1462 	dep->base.sheet = sheet;
1463 	dependent_managed_set_expr (dep, texpr);
1464 	if (texpr) gnm_expr_top_unref (texpr);
1465 }
1466 
1467 static void
managed_dep_debug_name(GnmDependent const * dep,GString * target)1468 managed_dep_debug_name (GnmDependent const *dep, GString *target)
1469 {
1470 	g_string_append_printf (target, "Managed%p", (void *)dep);
1471 }
1472 
1473 /*****************************************************************************/
1474 
1475 static void
name_dep_debug_name(GnmDependent const * dep,GString * target)1476 name_dep_debug_name (GnmDependent const *dep, GString *target)
1477 {
1478 	g_string_append_printf (target, "Name%p", (void *)dep);
1479 }
1480 
1481 /*****************************************************************************/
1482 
1483 static GSList *
dynamic_dep_changed(GnmDependent * dep)1484 dynamic_dep_changed (GnmDependent *dep)
1485 {
1486 	DynamicDep const *dyn = (DynamicDep *)dep;
1487 
1488 	/* When a dynamic dependent changes, we mark its container.  */
1489 
1490 	if (dependent_needs_recalc (dyn->container))
1491 		return NULL;
1492 
1493 	dependent_flag_recalc (dyn->container);
1494 	return g_slist_prepend (NULL, dyn->container);
1495 }
1496 
1497 static void
dynamic_dep_debug_name(GnmDependent const * dep,GString * target)1498 dynamic_dep_debug_name (GnmDependent const *dep, GString *target)
1499 {
1500 	g_string_append_printf (target, "DynamicDep%p", (void *)dep);
1501 }
1502 
1503 void
dependent_add_dynamic_dep(GnmDependent * dep,GnmRangeRef const * rr)1504 dependent_add_dynamic_dep (GnmDependent *dep, GnmRangeRef const *rr)
1505 {
1506 	GnmDependentFlags    flags;
1507 	DynamicDep	 *dyn;
1508 	GnmCellPos const *pos;
1509 	DependencyRange   range;
1510 
1511 	g_return_if_fail (dep != NULL);
1512 
1513 	pos = dependent_pos (dep);
1514 
1515 	if (dep->flags & DEPENDENT_HAS_DYNAMIC_DEPS)
1516 		dyn = g_hash_table_lookup (dep->sheet->deps->dynamic_deps, dep);
1517 	else {
1518 		dep->flags |= DEPENDENT_HAS_DYNAMIC_DEPS;
1519 		dyn = g_new (DynamicDep, 1);
1520 		dyn->base.flags		= DEPENDENT_DYNAMIC_DEP;
1521 		dyn->base.sheet		= dep->sheet;
1522 		dyn->base.texpr		= NULL;
1523 		dyn->container		= dep;
1524 		dyn->ranges		= NULL;
1525 		dyn->singles		= NULL;
1526 		g_hash_table_insert (dep->sheet->deps->dynamic_deps, dep, dyn);
1527 	}
1528 
1529 	gnm_cellpos_init_cellref (&range.range.start, &rr->a, pos, dep->sheet);
1530 	gnm_cellpos_init_cellref (&range.range.end, &rr->b, pos, dep->sheet);
1531 	if (range_is_singleton (&range.range)) {
1532 		flags = link_single_dep (&dyn->base, pos, &rr->a);
1533 		dyn->singles = g_slist_prepend (dyn->singles, gnm_rangeref_dup (rr));
1534 	} else {
1535 		flags = link_unlink_cellrange_dep (&dyn->base, pos, &rr->a, &rr->b, DEP_LINK_LINK);
1536 		dyn->ranges = g_slist_prepend (dyn->ranges, gnm_rangeref_dup (rr));
1537 	}
1538 	if (flags & DEPENDENT_HAS_3D)
1539 		workbook_link_3d_dep (dep);
1540 }
1541 
1542 static void
dependent_clear_dynamic_deps(GnmDependent * dep)1543 dependent_clear_dynamic_deps (GnmDependent *dep)
1544 {
1545 	g_hash_table_remove (dep->sheet->deps->dynamic_deps, dep);
1546 }
1547 
1548 /*****************************************************************************/
1549 
1550 /**
1551  * dependent_link:
1552  * @dep: the dependent that changed
1553  *
1554  * Adds the dependent to the workbook wide list of dependents.
1555  */
1556 void
dependent_link(GnmDependent * dep)1557 dependent_link (GnmDependent *dep)
1558 {
1559 	Sheet	   *sheet;
1560 	GnmEvalPos  ep;
1561 
1562 	g_return_if_fail (dep != NULL);
1563 	g_return_if_fail (dep->texpr != NULL);
1564 	g_return_if_fail (!(dep->flags & DEPENDENT_IS_LINKED));
1565 	g_return_if_fail (IS_SHEET (dep->sheet));
1566 	g_return_if_fail (dep->sheet->deps != NULL);
1567 
1568 	sheet = dep->sheet;
1569 
1570 	/* Make this the new tail of the dependent list.  */
1571 	dep->prev_dep = sheet->deps->tail;
1572 	dep->next_dep = NULL;
1573 	if (dep->prev_dep)
1574 		dep->prev_dep->next_dep = dep;
1575 	else
1576 		sheet->deps->head = dep; /* first element */
1577 	sheet->deps->tail = dep;
1578 	dep->flags |= DEPENDENT_IS_LINKED |
1579 		link_unlink_expr_dep (eval_pos_init_dep (&ep, dep),
1580 				      dep->texpr->expr, DEP_LINK_LINK);
1581 
1582 	if (dep->flags & DEPENDENT_HAS_3D)
1583 		workbook_link_3d_dep (dep);
1584 }
1585 
1586 /**
1587  * dependent_unlink:
1588  * @dep: the dependent that changed
1589  *
1590  * Removes the dependent from its container's set of dependents and always
1591  * removes the linkages to what it depends on.
1592  */
1593 void
dependent_unlink(GnmDependent * dep)1594 dependent_unlink (GnmDependent *dep)
1595 {
1596 	GnmDepContainer *contain;
1597 	GnmEvalPos ep;
1598 
1599 	g_return_if_fail (dep != NULL);
1600 	g_return_if_fail (dependent_is_linked (dep));
1601 	g_return_if_fail (dep->texpr != NULL);
1602 	g_return_if_fail (IS_SHEET (dep->sheet));
1603 
1604 	link_unlink_expr_dep (eval_pos_init_dep (&ep, dep),
1605 			      dep->texpr->expr, DEP_LINK_UNLINK);
1606 	contain = dep->sheet->deps;
1607 	if (contain != NULL) {
1608 		if (contain->head == dep)
1609 			contain->head = dep->next_dep;
1610 		if (contain->tail == dep)
1611 			contain->tail = dep->prev_dep;
1612 		if (dep->next_dep)
1613 			dep->next_dep->prev_dep = dep->prev_dep;
1614 		if (dep->prev_dep)
1615 			dep->prev_dep->next_dep = dep->next_dep;
1616 
1617 		if (dep->flags & DEPENDENT_HAS_DYNAMIC_DEPS)
1618 			dependent_clear_dynamic_deps (dep);
1619 	}
1620 
1621 	if (dep->flags & DEPENDENT_HAS_3D)
1622 		workbook_unlink_3d_dep (dep);
1623 	dep->flags &= ~DEPENDENT_LINK_FLAGS;
1624 }
1625 
1626 /**
1627  * gnm_cell_eval_content:
1628  * @cell: the cell to evaluate.
1629  *
1630  * This function evaluates the contents of the cell,
1631  * it should not be used by anyone. It is an internal
1632  * function.
1633  **/
1634 static gboolean
gnm_cell_eval_content(GnmCell * cell)1635 gnm_cell_eval_content (GnmCell *cell)
1636 {
1637 	static GnmCell *iterating = NULL;
1638 	GnmValue   *v;
1639 	GnmEvalPos	 pos;
1640 	int	 max_iteration;
1641 
1642 	if (!gnm_cell_has_expr (cell) ||	/* plain cells without expr */
1643 	    !dependent_is_linked (&cell->base)) /* special case within TABLE */
1644 		return TRUE;
1645 
1646 #ifdef DEBUG_EVALUATION
1647 	{
1648 		GnmParsePos pp;
1649 		char *str = gnm_expr_top_as_string
1650 			(cell->base.texpr,
1651 			 parse_pos_init_cell (&pp, cell),
1652 			 sheet_get_conventions (cell->base.sheet));
1653 		g_printerr ("{\nEvaluating %s!%s: %s;\n",
1654 			    cell->base.sheet->name_quoted, cell_name (cell),
1655 			    str);
1656 		g_free (str);
1657 	}
1658 #endif
1659 
1660 	/* This is the bottom of a cycle */
1661 	if (cell->base.flags & DEPENDENT_BEING_CALCULATED) {
1662 		if (!cell->base.sheet->workbook->iteration.enabled)
1663 			return TRUE;
1664 
1665 		/* but not the first bottom */
1666 		if (cell->base.flags & DEPENDENT_BEING_ITERATED) {
1667 #ifdef DEBUG_EVALUATION
1668 			g_printerr ("}; /* already-iterate (%d) */\n", iterating == NULL);
1669 #endif
1670 			return iterating == NULL;
1671 		}
1672 
1673 		/* if we are still marked as iterating then make this the last
1674 		 * time through.
1675 		 */
1676 		if (iterating == cell) {
1677 #ifdef DEBUG_EVALUATION
1678 			puts ("}; /* NO-iterate (1) */");
1679 #endif
1680 			return TRUE;
1681 		} else if (iterating == NULL) {
1682 			cell->base.flags |= DEPENDENT_BEING_ITERATED;
1683 			iterating = cell;
1684 #ifdef DEBUG_EVALUATION
1685 			puts ("}; /* START iterate = TRUE (0) */");
1686 #endif
1687 			return FALSE;
1688 		} else {
1689 #ifdef DEBUG_EVALUATION
1690 			puts ("}; /* other-iterate (0) */");
1691 #endif
1692 			return FALSE;
1693 		}
1694 	}
1695 
1696 	/* Prepare to calculate */
1697 	eval_pos_init_cell (&pos, cell);
1698 	cell->base.flags |= DEPENDENT_BEING_CALCULATED;
1699 
1700 	/*
1701 	 * I'm somewhat afraid of thinking about the semantics of iteration
1702 	 * if different workbooks have different settings.
1703 	 */
1704 	max_iteration = cell->base.sheet->workbook->iteration.max_number;
1705 
1706 iterate:
1707 	v = gnm_expr_top_eval (cell->base.texpr, &pos,
1708 			       GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
1709 	if (v == NULL)
1710 		v = value_new_error (&pos, "Internal error");
1711 
1712 #ifdef DEBUG_EVALUATION
1713 	{
1714 		char *valtxt = v
1715 			? value_get_as_string (v)
1716 			: g_strdup ("NULL");
1717 		g_printerr ("Evaluation(%d) %s := %s\n",
1718 			    max_iteration, cell_name (cell), valtxt);
1719 		g_free (valtxt);
1720 	}
1721 #endif
1722 
1723 	/* The top of a cycle */
1724 	if (cell->base.flags & DEPENDENT_BEING_ITERATED) {
1725 		cell->base.flags &= ~DEPENDENT_BEING_ITERATED;
1726 
1727 		/* We just completed the last iteration, don't change things */
1728 		if (iterating && max_iteration-- > 0) {
1729 			/* If we are within bounds make this the last round */
1730 			if (value_diff (cell->value, v) < cell->base.sheet->workbook->iteration.tolerance)
1731 				max_iteration = 0;
1732 			else {
1733 #ifdef DEBUG_EVALUATION
1734 				puts ("/* iterate == NULL */");
1735 #endif
1736 				iterating = NULL;
1737 			}
1738 			value_release (cell->value);
1739 			cell->value = v;
1740 
1741 			gnm_cell_unrender (cell);
1742 #ifdef DEBUG_EVALUATION
1743 			puts ("/* LOOP */");
1744 #endif
1745 			goto iterate;
1746 		}
1747 		g_return_val_if_fail (iterating, TRUE);
1748 		iterating = NULL;
1749 	} else {
1750 		gboolean had_value = (cell->value != NULL);
1751 		if (had_value && value_equal (v, cell->value)) {
1752 			/* Value didn't change.  */
1753 			value_release (v);
1754 		} else {
1755 			gboolean was_string = had_value && (VALUE_IS_STRING (cell->value) || VALUE_IS_ERROR (cell->value));
1756 			gboolean is_string = VALUE_IS_STRING (v) || VALUE_IS_ERROR (v);
1757 
1758 			if ((was_string || is_string))
1759 				sheet_cell_queue_respan (cell);
1760 
1761 			if (had_value)
1762 				value_release (cell->value);
1763 			cell->value = v;
1764 
1765 			gnm_cell_unrender (cell);
1766 		}
1767 	}
1768 
1769 	if (iterating == cell)
1770 		iterating = NULL;
1771 
1772 #ifdef DEBUG_EVALUATION
1773 	g_printerr ("} (%d)\n", iterating == NULL);
1774 #endif
1775 	cell->base.flags &= ~DEPENDENT_BEING_CALCULATED;
1776 	return iterating == NULL;
1777 }
1778 
1779 /**
1780  * dependent_eval:
1781  * @dep:
1782  */
1783 static void
dependent_eval(GnmDependent * dep)1784 dependent_eval (GnmDependent *dep)
1785 {
1786 	int const t = dependent_type (dep);
1787 	GnmDependentClass *klass = g_ptr_array_index (dep_classes, t);
1788 
1789 	if (dep->flags & DEPENDENT_HAS_DYNAMIC_DEPS) {
1790 		dependent_clear_dynamic_deps (dep);
1791 		dep->flags &= ~DEPENDENT_HAS_DYNAMIC_DEPS;
1792 	}
1793 
1794 	/*
1795 	 * Problem: this really should be a tail call.
1796 	 */
1797 	klass->eval (dep);
1798 
1799 	/* Don't clear flag until after in case we iterate */
1800 	dep->flags &= ~DEPENDENT_NEEDS_RECALC;
1801 }
1802 
1803 void
gnm_cell_eval(GnmCell * cell)1804 gnm_cell_eval (GnmCell *cell)
1805 {
1806 	if (gnm_cell_needs_recalc (cell)) {
1807 		/*
1808 		 * This should be a tail call so the stack frame can be
1809 		 * eliminated before the call.
1810 		 */
1811 		dependent_eval (GNM_CELL_TO_DEP (cell));
1812 	}
1813 }
1814 
1815 
1816 /**
1817  * cell_queue_recalc:
1818  * @cell:
1819  *
1820  * Queue the cell and everything that depends on it for recalculation.
1821  * If a dependency is already queued ignore it.
1822  */
1823 void
cell_queue_recalc(GnmCell * cell)1824 cell_queue_recalc (GnmCell *cell)
1825 {
1826 	g_return_if_fail (cell != NULL);
1827 
1828 	if (!gnm_cell_needs_recalc (cell)) {
1829 		GSList *deps;
1830 
1831 		if (gnm_cell_has_expr (cell))
1832 			dependent_flag_recalc (GNM_CELL_TO_DEP (cell));
1833 
1834 		deps = cell_list_deps (cell);
1835 		dependent_queue_recalc_list (deps);
1836 		g_slist_free (deps);
1837 	}
1838 }
1839 
1840 static void
cell_foreach_range_dep(GnmCell const * cell,GnmDepFunc func,gpointer user)1841 cell_foreach_range_dep (GnmCell const *cell, GnmDepFunc func, gpointer user)
1842 {
1843 	Sheet *sheet = cell->base.sheet;
1844 	GHashTable *bucket =
1845 		sheet->deps->range_hash[bucket_of_row (cell->pos.row)];
1846 	GHashTableIter hiter;
1847 	gpointer key;
1848 
1849 	if (!bucket)
1850 		return;
1851 
1852 	g_hash_table_iter_init (&hiter, bucket);
1853 	while (g_hash_table_iter_next (&hiter, &key, NULL)) {
1854 		DependencyRange const *deprange = key;
1855 		GnmRange const *range = &(deprange->range);
1856 
1857 		if (!range_contains (range, cell->pos.col, cell->pos.row))
1858 			continue;
1859 
1860 		micro_hash_foreach_dep (deprange->deps, dep,
1861 					func (dep, user););
1862 	}
1863 }
1864 
1865 static void
cell_foreach_single_dep(Sheet const * sheet,int col,int row,GnmDepFunc func,gpointer user)1866 cell_foreach_single_dep (Sheet const *sheet, int col, int row,
1867 			 GnmDepFunc func, gpointer user)
1868 {
1869 	DependencySingle lookup, *single;
1870 	GnmDepContainer *deps = sheet->deps;
1871 
1872 	lookup.pos.col = col;
1873 	lookup.pos.row = row;
1874 
1875 	single = g_hash_table_lookup (deps->single_hash, &lookup);
1876 	if (single != NULL)
1877 		micro_hash_foreach_dep (single->deps, dep,
1878 			(*func) (dep, user););
1879 }
1880 
1881 /**
1882  * cell_foreach_dep:
1883  * @cell: #GnmCell
1884  * @func: (scope call):
1885  * @user: user data.
1886  *
1887  **/
1888 void
cell_foreach_dep(GnmCell const * cell,GnmDepFunc func,gpointer user)1889 cell_foreach_dep (GnmCell const *cell, GnmDepFunc func, gpointer user)
1890 {
1891 	g_return_if_fail (cell != NULL);
1892 
1893 	/* accelerate exit */
1894 	if (!cell->base.sheet->deps)
1895 		return;
1896 
1897 	cell_foreach_range_dep (cell, func, user);
1898 	cell_foreach_single_dep (cell->base.sheet, cell->pos.col, cell->pos.row,
1899 				 func, user);
1900 }
1901 
1902 /**
1903  * sheet_region_queue_recalc:
1904  * @sheet: The sheet.
1905  * @range: (nullable): Range
1906  *
1907  * Queues things that depend on @sheet!@range for recalc.
1908  *
1909  * If @range is %NULL the entire sheet is used.
1910  */
1911 void
sheet_region_queue_recalc(Sheet const * sheet,GnmRange const * r)1912 sheet_region_queue_recalc (Sheet const *sheet, GnmRange const *r)
1913 {
1914 	int i, sb, eb;
1915 	GList *keys, *l;
1916 
1917 	g_return_if_fail (IS_SHEET (sheet));
1918 	g_return_if_fail (sheet->deps != NULL);
1919 
1920 	sb = r ? bucket_of_row (r->start.row) : 0;
1921 	eb = r ? bucket_of_row (r->end.row) : sheet->deps->buckets - 1;
1922 
1923 	/* mark the contained depends dirty non recursively */
1924 	SHEET_FOREACH_DEPENDENT (sheet, dep, {
1925 		GnmCell *cell = GNM_DEP_TO_CELL (dep);
1926 		if (!r || (dependent_is_cell (dep) &&
1927 			   range_contains (r, cell->pos.col, cell->pos.row)))
1928 			dependent_flag_recalc (dep);
1929 	});
1930 
1931 	// Look for things that depend on target region
1932 	// Note: we gather the keys first; we may change the hashes as we
1933 	// queue deps.
1934 	for (i = eb; i >= sb; i--) {
1935 		GHashTable *hash = sheet->deps->range_hash[i];
1936 		if (!hash) continue;
1937 		keys = g_hash_table_get_keys (hash);
1938 		for (l = keys; l; l = l->next) {
1939 			DependencyRange const *dr  = l->data;
1940 			GSList *work = NULL;
1941 
1942 			if (r && !range_overlap (r, &dr->range))
1943 				continue;
1944 			micro_hash_foreach_dep (dr->deps, dep, {
1945 				if (!dependent_needs_recalc (dep)) {
1946 					dependent_flag_recalc (dep);
1947 					work = g_slist_prepend (work, dep);
1948 				}
1949 			});
1950 			dependent_queue_recalc_main (work);
1951 		}
1952 		g_list_free (keys);
1953 	}
1954 
1955 	keys = g_hash_table_get_keys (sheet->deps->single_hash);
1956 	for (l = keys; l; l = l->next) {
1957 		DependencySingle const *ds = l->data;
1958 		GSList *work = NULL;
1959 		if (r && !range_contains (r, ds->pos.col, ds->pos.row))
1960 			continue;
1961 		micro_hash_foreach_dep (ds->deps, dep, {
1962 			if (!dependent_needs_recalc (dep)) {
1963 				dependent_flag_recalc (dep);
1964 				work = g_slist_prepend (work, dep);
1965 			}
1966 		});
1967 		dependent_queue_recalc_main (work);
1968 	}
1969 	g_list_free (keys);
1970 }
1971 
1972 typedef struct
1973 {
1974 	int dep_type;
1975 	union {
1976 		GnmParsePos   pos;
1977 		GnmDependent *dep;
1978 	} u;
1979 	GnmExprTop const *oldtree;
1980 } ExprRelocateStorage;
1981 
1982 /**
1983  * dependents_unrelocate_free:
1984  * @info:
1985  *
1986  * Free the undo info associated with a dependent relocation.
1987  */
1988 static void
dependents_unrelocate_free(GSList * info)1989 dependents_unrelocate_free (GSList *info)
1990 {
1991 	GSList *ptr = info;
1992 	for (; ptr != NULL ; ptr = ptr->next) {
1993 		ExprRelocateStorage *tmp = ptr->data;
1994 		gnm_expr_top_unref (tmp->oldtree);
1995 		g_free (tmp);
1996 	}
1997 	g_slist_free (info);
1998 }
1999 
2000 /**
2001  * dependents_unrelocate:
2002  * @info:
2003  *
2004  * Apply the undo info associated with a dependent relocation.
2005  */
2006 static void
dependents_unrelocate(GSList * info)2007 dependents_unrelocate (GSList *info)
2008 {
2009 	GSList *ptr = info;
2010 	for (; ptr != NULL ; ptr = ptr->next) {
2011 		ExprRelocateStorage *tmp = ptr->data;
2012 
2013 		if (tmp->dep_type == DEPENDENT_CELL) {
2014 			if (!IS_SHEET (tmp->u.pos.sheet)) {
2015 				/* FIXME : happens when undoing a move from a deleted
2016 				 * sheet.  Do we want to do something here */
2017 			} else {
2018 				GnmCell *cell = sheet_cell_get
2019 					(tmp->u.pos.sheet,
2020 					 tmp->u.pos.eval.col, tmp->u.pos.eval.row);
2021 
2022 				/* It is possible to have a NULL if the relocation info
2023 				 * is not really relevant.  eg when undoing a pasted
2024 				 * cut that was relocated but also saved to a buffer */
2025 				if (cell != NULL) {
2026 					if (gnm_expr_top_is_array_corner (tmp->oldtree)) {
2027 						int cols, rows;
2028 
2029 						gnm_expr_top_get_array_size (tmp->oldtree, &cols, &rows);
2030 						gnm_cell_set_array_formula (tmp->u.pos.sheet,
2031 									    tmp->u.pos.eval.col,
2032 									    tmp->u.pos.eval.row,
2033 									    tmp->u.pos.eval.col + cols - 1,
2034 									    tmp->u.pos.eval.row + rows - 1,
2035 									    gnm_expr_top_new (gnm_expr_copy (gnm_expr_top_get_array_expr (tmp->oldtree))));
2036 						cell_queue_recalc (cell);
2037 						sheet_flag_status_update_cell (cell);
2038 					} else
2039 						sheet_cell_set_expr (cell, tmp->oldtree);
2040 				}
2041 			}
2042 		} else if (tmp->dep_type == DEPENDENT_NAME) {
2043 		} else {
2044 			dependent_set_expr (tmp->u.dep, tmp->oldtree);
2045 			dependent_flag_recalc (tmp->u.dep);
2046 			dependent_link (tmp->u.dep);
2047 		}
2048 	}
2049 }
2050 
2051 /**
2052  * dependents_link:
2053  * @deps: (element-type GnmDependent): An slist of dependents.
2054  *
2055  * link a list of dependents.  (The list used to get freed, but is not
2056  * freed anymore.)
2057  */
2058 void
dependents_link(GSList * deps)2059 dependents_link (GSList *deps)
2060 {
2061 	GSList *ptr = deps;
2062 
2063 	/* put them back */
2064 	for (; ptr != NULL ; ptr = ptr->next) {
2065 		GnmDependent *dep = ptr->data;
2066 		if (dep->sheet->being_invalidated)
2067 			continue;
2068 		if (dep->sheet->deps != NULL && !dependent_is_linked (dep)) {
2069 			dependent_link (dep);
2070 			dependent_queue_recalc (dep);
2071 		}
2072 	}
2073 }
2074 
2075 typedef struct {
2076 	GnmRange const *target;
2077 	GSList *list;
2078 } CollectClosure;
2079 
2080 static void
cb_range_contained_collect(DependencyRange const * deprange,G_GNUC_UNUSED gpointer ignored,CollectClosure * user)2081 cb_range_contained_collect (DependencyRange const *deprange,
2082 			    G_GNUC_UNUSED gpointer ignored,
2083 			    CollectClosure *user)
2084 {
2085 	GnmRange const *range = &deprange->range;
2086 
2087 	if (range_overlap (user->target, range))
2088 		micro_hash_foreach_dep (deprange->deps, dep, {
2089 			if (!(dep->flags & (DEPENDENT_FLAGGED | DEPENDENT_CAN_RELOCATE)) &&
2090 			    dependent_type (dep) != DEPENDENT_DYNAMIC_DEP) {
2091 				dep->flags |= DEPENDENT_FLAGGED;
2092 				user->list = g_slist_prepend (user->list, dep);
2093 			}});
2094 }
2095 
2096 static void
cb_single_contained_collect(DependencySingle const * depsingle,G_GNUC_UNUSED gpointer ignored,CollectClosure * user)2097 cb_single_contained_collect (DependencySingle const *depsingle,
2098 			     G_GNUC_UNUSED gpointer ignored,
2099 			     CollectClosure *user)
2100 {
2101 	if (range_contains (user->target, depsingle->pos.col, depsingle->pos.row))
2102 		micro_hash_foreach_dep (depsingle->deps, dep, {
2103 			if (!(dep->flags & (DEPENDENT_FLAGGED | DEPENDENT_CAN_RELOCATE)) &&
2104 			    dependent_type (dep) != DEPENDENT_DYNAMIC_DEP) {
2105 				dep->flags |= DEPENDENT_FLAGGED;
2106 				user->list = g_slist_prepend (user->list, dep);
2107 			}});
2108 }
2109 
2110 struct cb_remote_names {
2111 	GSList *names;
2112 	Workbook *wb;
2113 };
2114 
2115 static void
cb_remote_names1(G_GNUC_UNUSED gconstpointer key,GnmNamedExpr * nexpr,struct cb_remote_names * data)2116 cb_remote_names1 (G_GNUC_UNUSED gconstpointer key,
2117 		  GnmNamedExpr *nexpr,
2118 		  struct cb_remote_names *data)
2119 {
2120 	data->names = g_slist_prepend (data->names, nexpr);
2121 }
2122 
2123 static void
cb_remote_names2(GnmNamedExpr * nexpr,G_GNUC_UNUSED gpointer value,struct cb_remote_names * data)2124 cb_remote_names2 (GnmNamedExpr *nexpr,
2125 		  G_GNUC_UNUSED gpointer value,
2126 		  struct cb_remote_names *data)
2127 {
2128 	Workbook *wb =
2129 		nexpr->pos.sheet ? nexpr->pos.sheet->workbook : nexpr->pos.wb;
2130 	if (wb != data->wb)
2131 		data->names = g_slist_prepend (data->names, nexpr);
2132 }
2133 
2134 /*
2135  * Get a list of all names that (may) reference data in a given sheet.
2136  * This is approximated as all names in the sheet, all global names in
2137  * its workbook, and all external references that actually mention the
2138  * sheet explicitly.
2139  */
2140 static GSList *
names_referencing_sheet(Sheet * sheet)2141 names_referencing_sheet (Sheet *sheet)
2142 {
2143 	struct cb_remote_names data;
2144 
2145 	data.names = NULL;
2146 	data.wb = sheet->workbook;
2147 
2148 	workbook_foreach_name (sheet->workbook, TRUE,
2149 			       (GHFunc)cb_remote_names1,
2150 			       &data);
2151 	gnm_sheet_foreach_name (sheet, (GHFunc)cb_remote_names1, &data);
2152 
2153 	if (sheet->deps->referencing_names)
2154 		g_hash_table_foreach (sheet->deps->referencing_names,
2155 				      (GHFunc)cb_remote_names2,
2156 				      &data);
2157 
2158 	return data.names;
2159 }
2160 
2161 
2162 /**
2163  * dependents_relocate:
2164  * @info: the descriptor record for what is being moved where.
2165  *
2166  * Fixes references to or from a region that is going to be moved.
2167  * Returns: (transfer full): a list of the locations and expressions that were changed outside of
2168  * the region.
2169  **/
2170 GOUndo *
dependents_relocate(GnmExprRelocateInfo const * rinfo)2171 dependents_relocate (GnmExprRelocateInfo const *rinfo)
2172 {
2173 	GnmExprRelocateInfo local_rinfo;
2174 	GSList    *l, *dependents = NULL, *undo_info = NULL;
2175 	Sheet	  *sheet;
2176 	GnmRange const   *r;
2177 	int i;
2178 	CollectClosure collect;
2179 	GOUndo *u_exprs, *u_names;
2180 
2181 	g_return_val_if_fail (rinfo != NULL, NULL);
2182 
2183 	/* short circuit if nothing would move */
2184 	if (rinfo->col_offset == 0 && rinfo->row_offset == 0 &&
2185 	    rinfo->origin_sheet == rinfo->target_sheet)
2186 		return NULL;
2187 
2188 	sheet = rinfo->origin_sheet;
2189 	r     = &rinfo->origin;
2190 
2191 	/* collect contained cells with expressions */
2192 	SHEET_FOREACH_DEPENDENT (rinfo->origin_sheet, dep, {
2193 		GnmCell *cell = GNM_DEP_TO_CELL (dep);
2194 		if (dependent_is_cell (dep) &&
2195 		    range_contains (r, cell->pos.col, cell->pos.row)) {
2196 			dependents = g_slist_prepend (dependents, dep);
2197 			dep->flags |= DEPENDENT_FLAGGED;
2198 		}
2199 	});
2200 
2201 	/* collect the things that depend on source region */
2202 	collect.target = r;
2203 	collect.list = dependents;
2204 	g_hash_table_foreach (sheet->deps->single_hash,
2205 		(GHFunc) &cb_single_contained_collect,
2206 		(gpointer)&collect);
2207 	{
2208 		int const first = bucket_of_row (r->start.row);
2209 		GHashTable *hash;
2210 		for (i = bucket_of_row (r->end.row); i >= first ; i--) {
2211 			hash = sheet->deps->range_hash[i];
2212 			if (hash != NULL)
2213 				g_hash_table_foreach (hash,
2214 					(GHFunc) &cb_range_contained_collect,
2215 					(gpointer)&collect);
2216 		}
2217 	}
2218 	dependents = collect.list;
2219 	local_rinfo = *rinfo;
2220 	for (l = dependents; l; l = l->next) {
2221 		GnmExprTop const *newtree;
2222 		GnmDependent *dep = l->data;
2223 
2224 		dep->flags &= ~DEPENDENT_FLAGGED;
2225 		sheet_flag_status_update_range (dep->sheet, NULL);
2226 
2227 		parse_pos_init_dep (&local_rinfo.pos, dep);
2228 
2229 		/* it is possible nothing changed for contained deps
2230 		 * using absolute references */
2231 		newtree = gnm_expr_top_relocate (dep->texpr, &local_rinfo, FALSE);
2232 		if (newtree != NULL) {
2233 			int const t = dependent_type (dep);
2234 			ExprRelocateStorage *tmp =
2235 				g_new (ExprRelocateStorage, 1);
2236 
2237 			tmp->dep_type = t;
2238 			if (t == DEPENDENT_NAME) {
2239 #warning "What should we do here and why do we leak tmp?"
2240 			} else {
2241 				if (t == DEPENDENT_CELL)
2242 					tmp->u.pos = local_rinfo.pos;
2243 				else
2244 					tmp->u.dep = dep;
2245 				tmp->oldtree = dep->texpr;
2246 				gnm_expr_top_ref (tmp->oldtree);
2247 				undo_info = g_slist_prepend (undo_info, tmp);
2248 
2249 				dependent_set_expr (dep, newtree); /* unlinks */
2250 				gnm_expr_top_unref (newtree);
2251 
2252 				/* queue the things that depend on the changed dep
2253 				 * even if it is going to move.
2254 				 */
2255 				dependent_queue_recalc (dep);
2256 
2257 				/* relink if it is not going to move, if it is moving
2258 				 * then the caller is responsible for relinking.
2259 				 * This avoids a link/unlink/link tuple
2260 				 */
2261 				if (t == DEPENDENT_CELL) {
2262 					GnmCellPos const *pos = &GNM_DEP_TO_CELL (dep)->pos;
2263 					if (dep->sheet != sheet ||
2264 					    !range_contains (r, pos->col, pos->row))
2265 						dependent_link (dep);
2266 				} else
2267 					dependent_link (dep);
2268 			}
2269 		} else {
2270 			/*
2271 			 * The expression may not be changing, but it depends
2272 			 * on something that is.  Not-corner array cells go here
2273 			 * too
2274 			 */
2275 			dependent_queue_recalc (dep);
2276 		}
2277 
2278 		/* Not the most efficient, but probably not too bad.  It is
2279 		 * definitely cheaper than finding the set of effected sheets. */
2280 		sheet_flag_status_update_range (dep->sheet, NULL);
2281 	}
2282 	g_slist_free (dependents);
2283 
2284 	u_exprs = go_undo_unary_new (undo_info,
2285 				     (GOUndoUnaryFunc)dependents_unrelocate,
2286 				     (GFreeFunc)dependents_unrelocate_free);
2287 
2288 	u_names = NULL;
2289 
2290 	switch (rinfo->reloc_type) {
2291 	case GNM_EXPR_RELOCATE_INVALIDATE_SHEET:
2292 	case GNM_EXPR_RELOCATE_MOVE_RANGE:
2293 		break;
2294 
2295 	case GNM_EXPR_RELOCATE_COLS:
2296 	case GNM_EXPR_RELOCATE_ROWS: {
2297 		GSList *l, *names = names_referencing_sheet (sheet);
2298 		GnmExprRelocateInfo rinfo2 = *rinfo;
2299 
2300 		for (l = names; l; l = l->next) {
2301 			GnmNamedExpr *nexpr = l->data;
2302 			GnmExprTop const *newtree;
2303 			rinfo2.pos = nexpr->pos;
2304 			newtree = gnm_expr_top_relocate (nexpr->texpr,
2305 							 &rinfo2, TRUE);
2306 			if (newtree) {
2307 				GOUndo *u = expr_name_set_expr_undo_new (nexpr);
2308 				u_names = go_undo_combine (u_names, u);
2309 				expr_name_set_expr (nexpr, newtree);
2310 			}
2311 		}
2312 		g_slist_free (names);
2313 		break;
2314 
2315 	default:
2316 		g_assert_not_reached ();
2317 	}
2318 	}
2319 
2320 	return go_undo_combine (u_exprs, u_names);
2321 }
2322 
2323 /*******************************************************************/
2324 
2325 static gboolean
cb_collect_range(G_GNUC_UNUSED gpointer key,DependencyAny * depany,GSList ** collector)2326 cb_collect_range (G_GNUC_UNUSED gpointer key,
2327 		  DependencyAny *depany,
2328 		  GSList **collector)
2329 {
2330 	*collector = g_slist_prepend (*collector, depany);
2331 	return TRUE;
2332 }
2333 
2334 static void
dep_hash_destroy(GHashTable * hash,GSList ** dyn_deps,Sheet * sheet)2335 dep_hash_destroy (GHashTable *hash, GSList **dyn_deps, Sheet *sheet)
2336 {
2337 	GSList *deps = NULL, *l;
2338 	GnmExprRelocateInfo rinfo;
2339 	GSList *deplist = NULL;
2340 	gboolean destroy = (sheet->revive == NULL);
2341 
2342 	/* We collect first because we will be changing the hash.  */
2343 	if (destroy) {
2344 		g_hash_table_foreach_remove (hash,
2345 					     (GHRFunc)cb_collect_range,
2346 					     &deps);
2347 		g_hash_table_destroy (hash);
2348 	} else {
2349 		g_hash_table_foreach (hash, (GHFunc)cb_collect_range, &deps);
2350 	}
2351 
2352 	for (l = deps; l; l = l->next) {
2353 		DependencyAny *depany = l->data;
2354 
2355 		micro_hash_foreach_dep (depany->deps, dep, {
2356 			if (dependent_type (dep) == DEPENDENT_DYNAMIC_DEP) {
2357 				GnmDependent *c = ((DynamicDep *)dep)->container;
2358 				if (!c->sheet->being_invalidated)
2359 					*dyn_deps =
2360 						g_slist_prepend (*dyn_deps, c);
2361 			} else if (!dep->sheet->being_invalidated) {
2362 				/*
2363 				 * We collect here instead of doing right away as
2364 				 * the dependent_set_expr below can change the
2365 				 * container we are looping over.
2366 				 */
2367 				deplist = g_slist_prepend (deplist, dep);
2368 			}
2369 		});
2370 
2371 		if (destroy)
2372 			micro_hash_release (&depany->deps);
2373 	}
2374 	g_slist_free (deps);
2375 
2376 	/*
2377 	 * We do this after the above loop as this loop will
2378 	 * invalidate some of the DependencyAny pointers from
2379 	 * above.  The testcase for that is 314207, deleting
2380 	 * all but the first sheet in one go.
2381 	 */
2382 	rinfo.reloc_type = GNM_EXPR_RELOCATE_INVALIDATE_SHEET;
2383 	for (l = deplist; l; l = l->next) {
2384 		GnmDependent *dep = l->data;
2385 		GnmExprTop const *te = dep->texpr;
2386 		/* We are told this dependent depends on this region, hence if
2387 		 * newtree is null then either
2388 		 * 1) we did not depend on it (ie., serious breakage )
2389 		 * 2) we had a duplicate reference and we have already removed it.
2390 		 * 3) We depended on things via a name which will be
2391 		 *    invalidated elsewhere */
2392 		GnmExprTop const *newtree = gnm_expr_top_relocate (te, &rinfo, FALSE);
2393 		if (newtree != NULL) {
2394 			if (sheet->revive)
2395 				go_undo_group_add
2396 					(sheet->revive,
2397 					 gnm_dep_set_expr_undo_new (dep));
2398 			dependent_set_expr (dep, newtree);
2399 			gnm_expr_top_unref (newtree);
2400 			dependent_link (dep);
2401 		}
2402 	}
2403 	g_slist_free (deplist);
2404 }
2405 
2406 static void
cb_collect_deps_of_name(GnmDependent * dep,G_GNUC_UNUSED gpointer value,GSList ** accum)2407 cb_collect_deps_of_name (GnmDependent *dep, G_GNUC_UNUSED gpointer value,
2408 			 GSList **accum)
2409 {
2410 	/* grab unflagged linked depends */
2411 	if ((dep->flags & (DEPENDENT_FLAGGED|DEPENDENT_IS_LINKED)) == DEPENDENT_IS_LINKED) {
2412 		dep->flags |= DEPENDENT_FLAGGED;
2413 		*accum = g_slist_prepend (*accum, dep);
2414 	}
2415 }
2416 
2417 struct cb_collect_deps_of_names {
2418 	GSList *names;
2419 	GSList *deps;
2420 };
2421 
2422 static void
cb_collect_deps_of_names(GnmNamedExpr * nexpr,G_GNUC_UNUSED gpointer value,struct cb_collect_deps_of_names * accum)2423 cb_collect_deps_of_names (GnmNamedExpr *nexpr,
2424 			  G_GNUC_UNUSED gpointer value,
2425 			  struct cb_collect_deps_of_names *accum)
2426 {
2427 	accum->names = g_slist_prepend (accum->names, nexpr);
2428 	if (nexpr->dependents)
2429 		g_hash_table_foreach (nexpr->dependents,
2430 				      (GHFunc)cb_collect_deps_of_name,
2431 				      &accum->deps);
2432 }
2433 
2434 static void
invalidate_name(GnmNamedExpr * nexpr,Sheet * sheet)2435 invalidate_name (GnmNamedExpr *nexpr, Sheet *sheet)
2436 {
2437 	GnmExprTop const *old_expr = nexpr->texpr;
2438 	GnmExprTop const *new_expr = NULL;
2439 	gboolean scope_being_killed =
2440 		nexpr->pos.sheet
2441 		? nexpr->pos.sheet->being_invalidated
2442 		: nexpr->pos.wb->during_destruction;
2443 
2444 	if (!scope_being_killed) {
2445 		GnmExprRelocateInfo rinfo;
2446 		rinfo.reloc_type = GNM_EXPR_RELOCATE_INVALIDATE_SHEET;
2447 		new_expr = gnm_expr_top_relocate (old_expr, &rinfo, FALSE);
2448 		g_return_if_fail (new_expr != NULL);
2449 	}
2450 
2451 	if (nexpr->dependents && g_hash_table_size (nexpr->dependents))
2452 		g_warning ("Left-over name dependencies\n");
2453 
2454 	if (sheet->revive)
2455 		go_undo_group_add (sheet->revive,
2456 				   expr_name_set_expr_undo_new (nexpr));
2457 
2458 	expr_name_set_expr (nexpr, new_expr);
2459 }
2460 
2461 static void
handle_dynamic_deps(GSList * dyn_deps)2462 handle_dynamic_deps (GSList *dyn_deps)
2463 {
2464 	GSList *ptr;
2465 
2466 	for (ptr = dyn_deps; ptr != NULL ; ptr = ptr->next) {
2467 		GnmDependent *dep = ptr->data;
2468 		if (dep->flags & DEPENDENT_HAS_DYNAMIC_DEPS) {
2469 			dependent_clear_dynamic_deps (dep);
2470 			dep->flags &= ~DEPENDENT_HAS_DYNAMIC_DEPS;
2471 		}
2472 	}
2473 	dependent_queue_recalc_list (dyn_deps);
2474 	g_slist_free (dyn_deps);
2475 }
2476 
2477 static void
handle_referencing_names(GnmDepContainer * deps,Sheet * sheet)2478 handle_referencing_names (GnmDepContainer *deps, Sheet *sheet)
2479 {
2480 	GSList *ptr;
2481 	GHashTable *names = deps->referencing_names;
2482 	struct cb_collect_deps_of_names accum;
2483 	gboolean destroy = (sheet->revive == NULL);
2484 
2485 	if (!names)
2486 		return;
2487 
2488 	if (destroy)
2489 		deps->referencing_names = NULL;
2490 
2491 	accum.deps = NULL;
2492 	accum.names = NULL;
2493 	g_hash_table_foreach (names,
2494 			      (GHFunc)cb_collect_deps_of_names,
2495 			      (gpointer)&accum);
2496 
2497 	for (ptr = accum.deps; ptr; ptr = ptr->next) {
2498 		GnmDependent *dep = ptr->data;
2499 		dep->flags &= ~DEPENDENT_FLAGGED;
2500 		dependent_unlink (dep);
2501 	}
2502 
2503 	/* now that all of the dependents of these names are unlinked.
2504 	 * change the references in the names to avoid this sheet */
2505 	for (ptr = accum.names; ptr; ptr = ptr->next) {
2506 		GnmNamedExpr *nexpr = ptr->data;
2507 		invalidate_name (nexpr, sheet);
2508 	}
2509 	g_slist_free (accum.names);
2510 
2511 	/* then relink things en-mass in case one of the deps outside
2512 	 * this sheet used multiple names that referenced us */
2513 	dependents_link (accum.deps);
2514 
2515 	if (destroy) {
2516 		g_slist_free (accum.deps);
2517 		g_hash_table_destroy (names);
2518 	} else {
2519 		go_undo_group_add (sheet->revive,
2520 				   gnm_dep_unlink_undo_new (accum.deps));
2521 	}
2522 }
2523 
2524 static void
handle_outgoing_references(GnmDepContainer * deps,Sheet * sheet)2525 handle_outgoing_references (GnmDepContainer *deps, Sheet *sheet)
2526 {
2527 	GnmDependentFlags what = DEPENDENT_USES_NAME;
2528 	GSList *accum = NULL;
2529 
2530 	what |= (sheet->workbook && sheet->workbook->during_destruction)
2531 		? DEPENDENT_GOES_INTERBOOK
2532 		: DEPENDENT_GOES_INTERSHEET;
2533 	DEPENDENT_CONTAINER_FOREACH_DEPENDENT (deps, dep, {
2534 		if (dependent_is_linked (dep) && (dep->flags & what)) {
2535 			dependent_unlink (dep);
2536 			if (sheet->revive)
2537 				accum = g_slist_prepend (accum, dep);
2538 		}
2539 	});
2540 
2541 	if (accum)
2542 		go_undo_group_add (sheet->revive,
2543 				   gnm_dep_unlink_undo_new (accum));
2544 }
2545 
2546 /*
2547  * do_deps_destroy:
2548  * Invalidate references of all kinds to the sheet.
2549  *
2550  * Also destroy the dependency container.
2551  */
2552 static void
do_deps_destroy(Sheet * sheet)2553 do_deps_destroy (Sheet *sheet)
2554 {
2555 	GnmDepContainer *deps;
2556 	GSList *dyn_deps = NULL;
2557 	int i;
2558 
2559 	g_return_if_fail (IS_SHEET (sheet));
2560 	g_return_if_fail (sheet->being_invalidated);
2561 
2562 	/* The GnmDepContainer (i.e., sheet->deps) contains the names that
2563 	 * reference this, not the names it contains.  Remove the locally
2564 	 * defined names here.
2565 	 *
2566 	 * NOTE : they may continue to exist inactively for a bit.  Be
2567 	 * careful to remove them _before_ destroying the deps.  This is
2568 	 * a bit wasteful in that we unlink and relink a few things that
2569 	 * are going to be deleted.  However, it is necessary to catch
2570 	 * all the different life cycles.
2571 	 */
2572 	gnm_named_expr_collection_unref (sheet->names);
2573 	sheet->names = NULL;
2574 
2575 	deps = sheet->deps;
2576 	if (deps == NULL)
2577 		return;
2578 
2579 	/* Destroy the records of what depends on this sheet.  There is no need
2580 	 * to delicately remove individual items from the lists.  The only
2581 	 * purpose that serves is to validate the state of our data structures.
2582 	 * If required this optimization can be disabled for debugging.
2583 	 */
2584 	sheet->deps = NULL;
2585 	if (sheet->revive) {
2586 		g_object_unref (sheet->revive);
2587 		sheet->revive = NULL;
2588 	}
2589 
2590 	for (i = deps->buckets - 1; i >= 0 ; i--) {
2591 		GHashTable *hash = deps->range_hash[i];
2592 		if (hash != NULL)
2593 			dep_hash_destroy (hash, &dyn_deps, sheet);
2594 	}
2595 	dep_hash_destroy (deps->single_hash, &dyn_deps, sheet);
2596 
2597 	g_free (deps->range_hash);
2598 	deps->range_hash = NULL;
2599 	/*
2600 	 * Note: we have not freed the elements in the pool.  This call
2601 	 * frees everything in one go.
2602 	 */
2603 	go_mem_chunk_destroy (deps->range_pool, TRUE);
2604 	deps->range_pool = NULL;
2605 
2606 	deps->single_hash = NULL;
2607 	/*
2608 	 * Note: we have not freed the elements in the pool.  This call
2609 	 * frees everything in one go.
2610 	 */
2611 	go_mem_chunk_destroy (deps->single_pool, TRUE);
2612 	deps->single_pool = NULL;
2613 
2614 	/* Now that we have tossed all deps to this sheet we can queue the
2615 	 * external dyn deps for recalc and free them */
2616 	handle_dynamic_deps (dyn_deps);
2617 
2618 	g_hash_table_destroy (deps->dynamic_deps);
2619 	deps->dynamic_deps = NULL;
2620 
2621 	handle_referencing_names (deps, sheet);
2622 
2623 	/* Now we remove any links from dependents in this sheet to
2624 	 * to other containers.  If the entire workbook is going away
2625 	 * just look for inter-book links.
2626 	 */
2627 	handle_outgoing_references (deps, sheet);
2628 
2629 	g_free (deps);
2630 }
2631 
2632 /*
2633  * do_deps_invalidate:
2634  * Invalidate references of all kinds to the sheet.
2635  */
2636 static void
do_deps_invalidate(Sheet * sheet)2637 do_deps_invalidate (Sheet *sheet)
2638 {
2639 	GnmDepContainer *deps;
2640 	GSList *dyn_deps = NULL;
2641 	int i;
2642 
2643 	g_return_if_fail (IS_SHEET (sheet));
2644 	g_return_if_fail (sheet->being_invalidated);
2645 	g_return_if_fail (sheet->revive == NULL);
2646 
2647 	sheet->revive = go_undo_group_new ();
2648 
2649 	gnm_named_expr_collection_unlink (sheet->names);
2650 
2651 	deps = sheet->deps;
2652 
2653 	for (i = deps->buckets - 1; i >= 0 ; i--) {
2654 		GHashTable *hash = deps->range_hash[i];
2655 		if (hash != NULL)
2656 			dep_hash_destroy (hash, &dyn_deps, sheet);
2657 	}
2658 	dep_hash_destroy (deps->single_hash, &dyn_deps, sheet);
2659 
2660 	/* Now that we have tossed all deps to this sheet we can queue the
2661 	 * external dyn deps for recalc and free them */
2662 	handle_dynamic_deps (dyn_deps);
2663 
2664 	handle_referencing_names (deps, sheet);
2665 
2666 	/* Now we remove any links from dependents in this sheet to
2667 	 * to other containers.  If the entire workbook is going away
2668 	 * just look for inter-book links.
2669 	 */
2670 	handle_outgoing_references (deps, sheet);
2671 }
2672 
2673 static void
cb_tweak_3d(GnmDependent * dep,G_GNUC_UNUSED gpointer value,GSList ** deps)2674 cb_tweak_3d (GnmDependent *dep, G_GNUC_UNUSED gpointer value, GSList **deps)
2675 {
2676 	*deps = g_slist_prepend (*deps, dep);
2677 }
2678 
2679 static void
tweak_3d(Sheet * sheet)2680 tweak_3d (Sheet *sheet)
2681 {
2682 	Workbook *wb = sheet->workbook;
2683 	GSList *deps = NULL, *l;
2684 	GnmExprRelocateInfo rinfo;
2685 
2686 	if (!wb->sheet_order_dependents)
2687 		return;
2688 
2689 	g_hash_table_foreach (wb->sheet_order_dependents,
2690 			      (GHFunc)cb_tweak_3d,
2691 			      &deps);
2692 
2693 	rinfo.reloc_type = GNM_EXPR_RELOCATE_INVALIDATE_SHEET;
2694 	for (l = deps; l; l = l->next) {
2695 		GnmDependent *dep = l->data;
2696 		GnmExprTop const *te = dep->texpr;
2697 		GnmExprTop const *newtree = gnm_expr_top_relocate (te, &rinfo, FALSE);
2698 
2699 		if (newtree != NULL) {
2700 			if (sheet->revive)
2701 				go_undo_group_add
2702 					(sheet->revive,
2703 					 gnm_dep_set_expr_undo_new (dep));
2704 			dependent_set_expr (dep, newtree);
2705 			gnm_expr_top_unref (newtree);
2706 			dependent_link (dep);
2707 			dependent_changed (dep);
2708 		}
2709 	}
2710 	g_slist_free (deps);
2711 }
2712 
2713 static void
dependents_invalidate_sheets(GSList * sheets,gboolean destroy)2714 dependents_invalidate_sheets (GSList *sheets, gboolean destroy)
2715 {
2716 	GSList *tmp;
2717 	Workbook *last_wb;
2718 
2719 	/* Mark all first.  */
2720 	for (tmp = sheets; tmp; tmp = tmp->next) {
2721 		Sheet *sheet = tmp->data;
2722 		sheet->being_invalidated = TRUE;
2723 	}
2724 
2725 	/*
2726 	 * Fixup 3d refs that start or end on one of these sheets.
2727 	 * Ideally we do this one per workbook, but that is not critical
2728 	 * so we are not going to outright sort the sheet list.
2729 	 */
2730 	last_wb = NULL;
2731 	for (tmp = sheets; tmp; tmp = tmp->next) {
2732 		Sheet *sheet = tmp->data;
2733 		Workbook *wb = sheet->workbook;
2734 
2735 		if (wb != last_wb)
2736 			tweak_3d (sheet);
2737 		last_wb = wb;
2738 	}
2739 
2740 	/* Now invalidate.  */
2741 	for (tmp = sheets; tmp; tmp = tmp->next) {
2742 		Sheet *sheet = tmp->data;
2743 		if (destroy)
2744 			do_deps_destroy (sheet);
2745 		else
2746 			do_deps_invalidate (sheet);
2747 	}
2748 
2749 	/* Unmark.  */
2750 	for (tmp = sheets; tmp; tmp = tmp->next) {
2751 		Sheet *sheet = tmp->data;
2752 		sheet->being_invalidated = FALSE;
2753 	}
2754 }
2755 
2756 void
dependents_invalidate_sheet(Sheet * sheet,gboolean destroy)2757 dependents_invalidate_sheet (Sheet *sheet, gboolean destroy)
2758 {
2759 	GSList l;
2760 
2761 	g_return_if_fail (IS_SHEET (sheet));
2762 
2763 	l.next = NULL;
2764 	l.data = sheet;
2765 	dependents_invalidate_sheets (&l, destroy);
2766 }
2767 
2768 void
dependents_workbook_destroy(Workbook * wb)2769 dependents_workbook_destroy (Workbook *wb)
2770 {
2771 	g_return_if_fail (GNM_IS_WORKBOOK (wb));
2772 	g_return_if_fail (wb->during_destruction);
2773 	g_return_if_fail (wb->sheets != NULL);
2774 
2775 	/* Mark all first.  */
2776 	WORKBOOK_FOREACH_SHEET (wb, sheet, sheet->being_invalidated = TRUE;);
2777 
2778 	/* Kill 3d deps and workbook-level names, if needed.  */
2779 	if (wb->sheet_order_dependents) {
2780 		g_hash_table_destroy (wb->sheet_order_dependents);
2781 		wb->sheet_order_dependents = NULL;
2782 	}
2783 	gnm_named_expr_collection_unref (wb->names);
2784 	wb->names = NULL;
2785 
2786 	WORKBOOK_FOREACH_SHEET (wb, sheet, do_deps_destroy (sheet););
2787 
2788 	/* Unmark.  */
2789 	WORKBOOK_FOREACH_SHEET (wb, sheet, sheet->being_invalidated = FALSE;);
2790 }
2791 
2792 /*
2793  * dependents_revive_sheet:
2794  * Undo the effects of dependents_invalidate_sheet (sheet, FALSE).
2795  */
2796 void
dependents_revive_sheet(Sheet * sheet)2797 dependents_revive_sheet (Sheet *sheet)
2798 {
2799 	go_undo_undo (GO_UNDO (sheet->revive));
2800 	g_object_unref (sheet->revive);
2801 	sheet->revive = NULL;
2802 
2803 	/* Re-link local names.  */
2804 	gnm_named_expr_collection_relink (sheet->names);
2805 
2806 	gnm_dep_container_sanity_check (sheet->deps);
2807 }
2808 
2809 void
workbook_queue_all_recalc(Workbook * wb)2810 workbook_queue_all_recalc (Workbook *wb)
2811 {
2812 	/* FIXME: what about dependents in other workbooks?  */
2813 	WORKBOOK_FOREACH_DEPENDENT (wb, dep, dependent_flag_recalc (dep););
2814 }
2815 
2816 void
workbook_queue_volatile_recalc(Workbook * wb)2817 workbook_queue_volatile_recalc (Workbook *wb)
2818 {
2819 	WORKBOOK_FOREACH_DEPENDENT (wb, dep, {
2820 		if (dependent_is_volatile (dep))
2821 			dependent_flag_recalc (dep);
2822 	});
2823 }
2824 
2825 
2826 /**
2827  * workbook_recalc:
2828  * @wb:
2829  *
2830  * Computes all dependents in @wb that have been flaged as requiring
2831  * recomputation.
2832  *
2833  * NOTE! This does not recalc dependents in other workbooks.
2834  */
2835 void
workbook_recalc(Workbook * wb)2836 workbook_recalc (Workbook *wb)
2837 {
2838 	gboolean redraw = FALSE;
2839 
2840 	g_return_if_fail (GNM_IS_WORKBOOK (wb));
2841 
2842 	gnm_app_recalc_start ();
2843 
2844 	// Do a pass computing only cells; this allows style deps to see
2845 	// updated values as needed.
2846 	WORKBOOK_FOREACH_DEPENDENT (wb, dep, {
2847 		if (dependent_is_cell (dep) && dependent_needs_recalc (dep)) {
2848 			redraw = TRUE;
2849 			dependent_eval (dep);
2850 		}
2851 	});
2852 
2853 	WORKBOOK_FOREACH_DEPENDENT (wb, dep, {
2854 		if (dependent_needs_recalc (dep)) {
2855 			redraw = TRUE;
2856 			dependent_eval (dep);
2857 		}
2858 	});
2859 
2860 	gnm_app_recalc_finish ();
2861 
2862 	/*
2863 	 * This is a bit of a band-aid.  If anything is recalculated, we
2864 	 * force a full redraw.  The alternative is to ask for updates
2865 	 * of every cell that is changed and that is probably more
2866 	 * expensive.
2867 	 */
2868 	if (redraw) {
2869 		WORKBOOK_FOREACH_SHEET (wb, sheet, {
2870 			SHEET_FOREACH_VIEW (sheet, sv, gnm_sheet_view_flag_selection_change (sv););
2871 			sheet_redraw_all (sheet, FALSE);});
2872 	}
2873 }
2874 
2875 /**
2876  * workbook_recalc_all:
2877  * @wb:
2878  *
2879  * Queues all dependents for recalc and recalculates.
2880  */
2881 void
workbook_recalc_all(Workbook * wb)2882 workbook_recalc_all (Workbook *wb)
2883 {
2884 	workbook_queue_all_recalc (wb);
2885 
2886 	/* Recalculate this workbook unconditionally.  */
2887 	workbook_recalc (wb);
2888 
2889 	/* Recalculate other workbooks when needed.  */
2890 	gnm_app_recalc ();
2891 
2892 	WORKBOOK_FOREACH_VIEW (wb, view,
2893 		sheet_update (wb_view_cur_sheet (view)););
2894 }
2895 
2896 static void
dynamic_dep_free(DynamicDep * dyn)2897 dynamic_dep_free (DynamicDep *dyn)
2898 {
2899 	GnmDependent *dep = dyn->container;
2900 	GnmCellPos const *pos = dependent_pos (dep);
2901 	GSList *ptr;
2902 
2903 	for (ptr = dyn->singles ; ptr != NULL ; ptr = ptr->next) {
2904 		GnmRangeRef *rr = ptr->data;
2905 		unlink_single_dep (&dyn->base, pos, &rr->a);
2906 		g_free (rr);
2907 	}
2908 	g_slist_free (dyn->singles);
2909 	dyn->singles = NULL;
2910 
2911 	for (ptr = dyn->ranges ; ptr != NULL ; ptr = ptr->next) {
2912 		GnmRangeRef *rr = ptr->data;
2913 		link_unlink_cellrange_dep (&dyn->base, pos,
2914 					   &rr->a, &rr->b, DEP_LINK_UNLINK);
2915 		g_free (rr);
2916 	}
2917 	g_slist_free (dyn->ranges);
2918 	dyn->ranges = NULL;
2919 
2920 	if (dyn->base.flags & DEPENDENT_HAS_3D)
2921 		workbook_unlink_3d_dep (&dyn->base);
2922 	g_free (dyn);
2923 }
2924 
2925 
2926 gboolean
dependent_is_volatile(GnmDependent * dep)2927 dependent_is_volatile (GnmDependent *dep)
2928 {
2929 	/* This might need to be a virtual call. */
2930 	return dep->texpr && gnm_expr_top_is_volatile (dep->texpr);
2931 }
2932 
2933 /**
2934  * gnm_dep_container_new: (skip)
2935  * @sheet: #Sheet
2936  *
2937  * Returns: (transfer full):
2938  **/
2939 GnmDepContainer *
gnm_dep_container_new(Sheet * sheet)2940 gnm_dep_container_new (Sheet *sheet)
2941 {
2942 	GnmDepContainer *deps = g_new (GnmDepContainer, 1);
2943 
2944 	if (gnm_debug_flag ("dep-buckets")) {
2945 		int r, lastb = 0;
2946 		g_assert (bucket_start_row (0) == 0);
2947 		g_assert (bucket_end_row (0) >= 0);
2948 		g_assert (bucket_of_row (0) == 0);
2949 		for (r = 1; r < gnm_sheet_get_max_rows (sheet); r++) {
2950 			int b = bucket_of_row (r);
2951 			if (b > lastb)
2952 				g_printerr ("%d -> %d\n", r, b);
2953 			g_assert (b == lastb || b == lastb + 1);
2954 			g_assert (bucket_start_row (b) <= r);
2955 			g_assert (r <= bucket_end_row (b));
2956 			lastb = b;
2957 		}
2958 	}
2959 
2960 	deps->head = deps->tail = NULL;
2961 
2962 	deps->buckets = 1 + bucket_of_row (gnm_sheet_get_last_row (sheet));
2963 	deps->range_hash  = g_new0 (GHashTable *, deps->buckets);
2964 	deps->range_pool  = go_mem_chunk_new ("range pool",
2965 					       sizeof (DependencyRange),
2966 					       16 * 1024 - 100);
2967 	deps->single_hash = g_hash_table_new ((GHashFunc) depsingle_hash,
2968 					      (GEqualFunc) depsingle_equal);
2969 	deps->single_pool = go_mem_chunk_new ("single pool",
2970 					       sizeof (DependencySingle),
2971 					       16 * 1024 - 100);
2972 	deps->referencing_names = g_hash_table_new (g_direct_hash,
2973 						    g_direct_equal);
2974 
2975 	deps->dynamic_deps = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2976 		NULL, (GDestroyNotify) dynamic_dep_free);
2977 
2978 	return deps;
2979 }
2980 
2981 void
gnm_dep_container_resize(GnmDepContainer * deps,int rows)2982 gnm_dep_container_resize (GnmDepContainer *deps, int rows)
2983 {
2984 	int i, buckets = 1 + bucket_of_row (rows - 1);
2985 
2986 	for (i = buckets; i < deps->buckets; i++) {
2987 		GHashTable *hash = deps->range_hash[i];
2988 		if (hash != NULL) {
2989 			int s = g_hash_table_size (hash);
2990 			if (s)
2991 				g_printerr ("Hash table size: %d\n", s);
2992 			g_hash_table_destroy (hash);
2993 			deps->range_hash[i] = NULL;
2994 		}
2995 	}
2996 
2997 	deps->range_hash = g_renew (GHashTable *, deps->range_hash, buckets);
2998 
2999 	for (i = deps->buckets; i < buckets; i++)
3000 		deps->range_hash[i] = NULL;
3001 
3002 	deps->buckets = buckets;
3003 }
3004 
3005 /****************************************************************************
3006  * Debug utils
3007  */
3008 
3009 static void
dependent_debug_name_for_sheet(GnmDependent const * dep,Sheet * sheet,GString * target)3010 dependent_debug_name_for_sheet (GnmDependent const *dep, Sheet *sheet,
3011 				GString *target)
3012 {
3013 	int t;
3014 	GnmDependentClass *klass;
3015 
3016 	g_return_if_fail (dep != NULL);
3017 	g_return_if_fail (dep_classes);
3018 
3019 	if (!dep->sheet)
3020 		g_warning ("Invalid dep, missing sheet");
3021 
3022 	if (sheet == dep->sheet) {
3023 		/* Nothing */
3024 	} else {
3025 		g_string_append (target,
3026 				 dep->sheet ? dep->sheet->name_quoted : "?");
3027 		g_string_append_c (target, '!');
3028 	}
3029 
3030 	t = dependent_type (dep);
3031 	klass = g_ptr_array_index (dep_classes, t);
3032 	klass->debug_name (dep, target);
3033 
3034 	if (dependent_has_pos (dep) && !dependent_is_cell (dep)) {
3035 		g_string_append_c (target, '@');
3036 		g_string_append (target, cellpos_as_string (dependent_pos (dep)));
3037 	}
3038 }
3039 
3040 
3041 /**
3042  * dependent_debug_name:
3043  * @dep: The dependent we are interested in.
3044  * @target: (inout): location to append @dep's name.
3045  *
3046  * A useful little debugging utility.
3047  */
3048 void
dependent_debug_name(GnmDependent const * dep,GString * target)3049 dependent_debug_name (GnmDependent const *dep, GString *target)
3050 {
3051 	dependent_debug_name_for_sheet (dep, NULL, target);
3052 }
3053 
3054 
3055 static void
dump_range_dep(DependencyRange const * deprange,Sheet * sheet,GHashTable * alldeps)3056 dump_range_dep (DependencyRange const *deprange, Sheet *sheet, GHashTable *alldeps)
3057 {
3058 	GnmRange const *range = &(deprange->range);
3059 	GString *target = g_string_sized_new (10000);
3060 	gboolean first = TRUE;
3061 
3062 	g_string_append (target, "    ");
3063 	g_string_append (target, range_as_string (range));
3064 	g_string_append (target, " -> (");
3065 
3066 	micro_hash_foreach_dep (deprange->deps, dep, {
3067 		if (first)
3068 			first = FALSE;
3069 		else
3070 			g_string_append (target, ", ");
3071 		g_hash_table_remove (alldeps, dep);
3072 		dependent_debug_name_for_sheet (dep, sheet, target);
3073 	});
3074 	g_string_append_c (target, ')');
3075 
3076 	g_printerr ("%s\n", target->str);
3077 	g_string_free (target, TRUE);
3078 }
3079 
3080 static void
dump_single_dep(DependencySingle * depsingle,Sheet * sheet,GHashTable * alldeps)3081 dump_single_dep (DependencySingle *depsingle, Sheet *sheet, GHashTable *alldeps)
3082 {
3083 	GString *target = g_string_sized_new (10000);
3084 	gboolean first = TRUE;
3085 
3086 	g_string_append (target, "    ");
3087 	g_string_append (target, cellpos_as_string (&depsingle->pos));
3088 	g_string_append (target, " -> ");
3089 
3090 	micro_hash_foreach_dep (depsingle->deps, dep, {
3091 		if (first)
3092 			first = FALSE;
3093 		else
3094 			g_string_append (target, ", ");
3095 		g_hash_table_remove (alldeps, dep);
3096 		dependent_debug_name_for_sheet (dep, sheet, target);
3097 	});
3098 
3099 	g_printerr ("%s\n", target->str);
3100 	g_string_free (target, TRUE);
3101 }
3102 
3103 static void
dump_dynamic_dep(GnmDependent * dep,DynamicDep * dyn,GHashTable * alldeps)3104 dump_dynamic_dep (GnmDependent *dep, DynamicDep *dyn, GHashTable *alldeps)
3105 {
3106 	GSList *l;
3107 	GnmParsePos pp;
3108 	GnmConventionsOut out;
3109 
3110 	out.accum = g_string_new (NULL);
3111 	out.pp    = &pp;
3112 	out.convs = gnm_conventions_default;
3113 
3114 	pp.wb    = dep->sheet->workbook;
3115 	pp.sheet = dep->sheet;
3116 	pp.eval  = *dependent_pos (dyn->container);
3117 
3118 	g_string_append (out.accum, "    ");
3119 	dependent_debug_name (dep, out.accum);
3120 	g_hash_table_remove (alldeps, dep); // ???
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);
3125 
3126 	g_string_append (out.accum, ", s=[");
3127 	for (l = dyn->singles; l; l = l->next) {
3128 		rangeref_as_string (&out, l->data);
3129 		if (l->next)
3130 			g_string_append (out.accum, ", ");
3131 	}
3132 
3133 	g_string_append (out.accum, "], r=[");
3134 	for (l = dyn->ranges; l; l = l->next) {
3135 		rangeref_as_string (&out, l->data);
3136 		if (l->next)
3137 			g_string_append (out.accum, ", ");
3138 	}
3139 
3140 	g_string_append (out.accum, "] }");
3141 	g_printerr ("%s\n", out.accum->str);
3142 	g_string_free (out.accum, TRUE);
3143 }
3144 
3145 /**
3146  * gnm_dep_container_dump:
3147  * @deps:
3148  * @sheet:
3149  *
3150  * A useful utility for checking the state of the dependency data structures.
3151  */
3152 void
gnm_dep_container_dump(GnmDepContainer const * deps,Sheet * sheet)3153 gnm_dep_container_dump (GnmDepContainer const *deps,
3154 			Sheet *sheet)
3155 {
3156 	int i;
3157 	GHashTable *alldeps;
3158 
3159 	g_return_if_fail (deps != NULL);
3160 
3161 	gnm_dep_container_sanity_check (deps);
3162 
3163 	alldeps = g_hash_table_new (g_direct_hash, g_direct_equal);
3164 	SHEET_FOREACH_DEPENDENT (sheet, dep,
3165 				 if (!dependent_is_cell (dep))
3166 					 g_hash_table_insert (alldeps, dep, dep);
3167 	);
3168 
3169 	for (i = 0; i < deps->buckets; i++) {
3170 		GHashTable *hash = deps->range_hash[i];
3171 		if (hash != NULL && g_hash_table_size (hash) > 0) {
3172 			GHashTableIter hiter;
3173 			gpointer key;
3174 
3175 			g_printerr ("  Bucket %d (rows %d-%d): Range hash size %d: range over which cells in list depend\n",
3176 				    i,
3177 				    bucket_start_row (i) + 1,
3178 				    bucket_end_row (i) + 1,
3179 				    g_hash_table_size (hash));
3180 			g_hash_table_iter_init (&hiter, hash);
3181 			while (g_hash_table_iter_next (&hiter, &key, NULL)) {
3182 				DependencyRange *deprange = key;
3183 				dump_range_dep (deprange, sheet, alldeps);
3184 			}
3185 		}
3186 	}
3187 
3188 	if (deps->single_hash && g_hash_table_size (deps->single_hash) > 0) {
3189 		GHashTableIter hiter;
3190 		gpointer key;
3191 
3192 		g_printerr ("  Single hash size %d: cell on which list of cells depend\n",
3193 			    g_hash_table_size (deps->single_hash));
3194 
3195 		g_hash_table_iter_init (&hiter, deps->single_hash);
3196 		while (g_hash_table_iter_next (&hiter, &key, NULL)) {
3197 			DependencySingle *depsingle = key;
3198 			dump_single_dep (depsingle, sheet, alldeps);
3199 		}
3200 	}
3201 
3202 	if (deps->dynamic_deps && g_hash_table_size (deps->dynamic_deps) > 0) {
3203 		GHashTableIter hiter;
3204 		gpointer key, value;
3205 
3206 		g_printerr ("  Dynamic hash size %d: dependents that depend on dynamic dependencies\n",
3207 			    g_hash_table_size (deps->dynamic_deps));
3208 		g_hash_table_iter_init (&hiter, deps->dynamic_deps);
3209 		while (g_hash_table_iter_next (&hiter, &key, &value)) {
3210 			GnmDependent *dep = key;
3211 			DynamicDep *dyn = value;
3212 			dump_dynamic_dep (dep, dyn, alldeps);
3213 		}
3214 	}
3215 
3216 	if (deps->referencing_names && g_hash_table_size (deps->referencing_names) > 0) {
3217 		GList *l, *names = g_hash_table_get_keys (deps->referencing_names);
3218 
3219 		g_printerr ("  Names whose expressions explicitly reference this sheet\n    ");
3220 		for (l = names; l; l = l->next) {
3221 			GnmNamedExpr *nexpr = l->data;
3222 			g_printerr ("%s%s",
3223 				    expr_name_name (nexpr),
3224 				    l->next ? ", " : "\n");
3225 		}
3226 		g_list_free (names);
3227 	}
3228 
3229 	if (g_hash_table_size (alldeps) > 0) {
3230 		GHashTableIter hiter;
3231 		gpointer key;
3232 
3233 		g_printerr ("  Dependencies of sheet not listed above (excluding cells):\n");
3234 		g_hash_table_iter_init (&hiter, alldeps);
3235 		while (g_hash_table_iter_next (&hiter, &key, NULL)) {
3236 			GnmDependent *dep = key;
3237 			GString *nstr = g_string_new (NULL);
3238 			GnmParsePos pp;
3239 			char *estr;
3240 
3241 			parse_pos_init_dep (&pp, dep);
3242 			estr = dep->texpr
3243 				? gnm_expr_top_as_string (dep->texpr, &pp, sheet_get_conventions (dep->sheet))
3244 				: g_strdup ("(null)");
3245 
3246 			dependent_debug_name (dep, nstr);
3247 			g_printerr ("    %s: %s\n", nstr->str, estr);
3248 			g_string_free (nstr, TRUE);
3249 			g_free (estr);
3250 		}
3251 	}
3252 
3253 	g_hash_table_destroy (alldeps);
3254 }
3255 
3256 void
dependents_dump(Workbook * wb)3257 dependents_dump (Workbook *wb)
3258 {
3259 	WORKBOOK_FOREACH_SHEET
3260 		(wb, sheet,
3261 		 {
3262 			 int count = 0;
3263 			 SHEET_FOREACH_DEPENDENT (sheet, dep, count++;);
3264 			 g_printerr ("Dependencies for %s (count=%d):\n",
3265 				     sheet->name_unquoted, count);
3266 			 gnm_dep_container_dump (sheet->deps, sheet);
3267 		 });
3268 }
3269 
3270 void
gnm_dep_container_sanity_check(GnmDepContainer const * deps)3271 gnm_dep_container_sanity_check (GnmDepContainer const *deps)
3272 {
3273 	GnmDependent const *dep;
3274 	GHashTable *seenb4;
3275 
3276 	if (deps->head && !deps->tail)
3277 		g_warning ("Dependency container %p has head, but no tail.", (void *)deps);
3278 	if (deps->tail && !deps->head)
3279 		g_warning ("Dependency container %p has tail, but no head.", (void *)deps);
3280 	if (deps->head && deps->head->prev_dep)
3281 		g_warning ("Dependency container %p has head, but not at the beginning.", (void *)deps);
3282 	if (deps->tail && deps->tail->next_dep)
3283 		g_warning ("Dependency container %p has tail, but not at the end.", (void *)deps);
3284 
3285 	seenb4 = g_hash_table_new (g_direct_hash, g_direct_equal);
3286 	for (dep = deps->head; dep; dep = dep->next_dep) {
3287 		if (dep->prev_dep && (dep->prev_dep->next_dep != dep))
3288 			g_warning ("Dependency container %p has left double-link failure at %p.", (void *)deps, (void *)dep);
3289 		if (dep->next_dep && (dep->next_dep->prev_dep != dep))
3290 			g_warning ("Dependency container %p has right double-link failure at %p.", (void *)deps, (void *)dep);
3291 		if (!dep->next_dep && dep != deps->tail)
3292 			g_warning ("Dependency container %p ends before its tail.", (void *)deps);
3293 		if (!dependent_is_linked (dep))
3294 			g_warning ("Dependency container %p contains unlinked dependency %p.", (void *)deps, (void *)dep);
3295 		if (g_hash_table_lookup (seenb4, dep)) {
3296 			g_warning ("Dependency container %p is circular.", (void *)deps);
3297 			break;
3298 		}
3299 		g_hash_table_insert (seenb4, (gpointer)dep, (gpointer)dep);
3300 	}
3301 	g_hash_table_destroy (seenb4);
3302 }
3303 
3304 /* ------------------------------------------------------------------------- */
3305