1 /*  Part of SWI-Prolog
2 
3     Author:        Jan Wielemaker
4     E-mail:        J.Wielemaker@vu.nl
5     WWW:           http://www.swi-prolog.org
6     Copyright (c)  2016-2020, VU University Amsterdam
7 			      CWI, Amsterdam
8     All rights reserved.
9 
10     Redistribution and use in source and binary forms, with or without
11     modification, are permitted provided that the following conditions
12     are met:
13 
14     1. Redistributions of source code must retain the above copyright
15        notice, this list of conditions and the following disclaimer.
16 
17     2. Redistributions in binary form must reproduce the above copyright
18        notice, this list of conditions and the following disclaimer in
19        the documentation and/or other materials provided with the
20        distribution.
21 
22     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33     POSSIBILITY OF SUCH DAMAGE.
34 */
35 
36 #include "pl-incl.h"
37 #include "pl-comp.h"
38 #include "pl-trie.h"
39 #include "pl-tabling.h"
40 #include "pl-indirect.h"
41 #define NO_AC_TERM_WALK 1
42 #define AC_TERM_WALK_POP 1
43 #include "pl-termwalk.c"
44 #include "pl-dbref.h"
45 
46 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
47 This file implements tries of  terms.  The   trie  itself  lives  in the
48 program space and is represented by a (symbol) handle. This implies that
49 tries are subject to garbage collection.
50 
51 A path through a trie represents a  sequence of tokens. For representing
52 terms, these tokens are functor symbols,   variables  and atomic values.
53 The _value_ associated with a  term  always   appears  in  a _leaf_ node
54 because a sequence that represents a term   is  _never_ the prefix of of
55 the sequence of another term.
56 
57 TODO
58   - Limit size of the tries
59   - Avoid using a hash-table for small number of branches
60   - Thread safe reclaiming
61     - Reclaim single-child node after moving to a hash
62     - Make pruning the trie thread-safe
63   - Provide deletion from a trie
64   - Make trie_gen/3 take the known prefix into account
65 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
66 
67 #define RESERVED_TRIE_VAL(n) (((word)((uintptr_t)n)<<LMASK_BITS) | \
68 			      TAG_VAR|STG_LOCAL)
69 #define TRIE_ERROR_VAL       RESERVED_TRIE_VAL(1)
70 #define TRIE_KEY_POP(n)      RESERVED_TRIE_VAL(10+(n))
71 
72 #define IS_TRIE_KEY_POP(w)   ((tagex(w) == (TAG_VAR|STG_LOCAL) && \
73 			       ((w)>>LMASK_BITS) > 10) ? ((w)>>LMASK_BITS) - 10 \
74 						       : 0)
75 
76 #define NVARS_FAST 100
77 
78 /* Will eventually be shared in pl-wam.c */
79 typedef enum
80 { uread = 0,				/* Unification in read-mode */
81   uwrite				/* Unification in write mode */
82 } unify_mode;
83 
84 typedef struct ukey_state
85 { trie	       *trie;			/* Trie for indirects */
86   Word		ptr;			/* current location */
87   unify_mode	umode;			/* unification mode */
88   size_t	max_var_seen;
89   size_t	vars_allocated;		/* # variables allocated */
90   Word*		vars;
91   size_t        a_offset;		/* For resetting the argument stack */
92   Word		var_buf[NVARS_FAST];	/* quick var buffer */
93 } ukey_state;
94 
95 static int	unify_key(ukey_state *state, word key ARG_LD);
96 static void	init_ukey_state(ukey_state *state, trie *trie, Word p ARG_LD);
97 static void	destroy_ukey_state(ukey_state *state ARG_LD);
98 static void	set_trie_clause_general_undefined(Clause cl);
99 
100 
101 		 /*******************************
102 		 *	       SYMBOL		*
103 		 *******************************/
104 
105 typedef struct tref
106 { trie *trie;				/* represented trie */
107 } tref;
108 
109 static int
write_trie_ref(IOSTREAM * s,atom_t aref,int flags)110 write_trie_ref(IOSTREAM *s, atom_t aref, int flags)
111 { tref *ref = PL_blob_data(aref, NULL, NULL);
112   (void)flags;
113 
114   Sfprintf(s, "<trie>(%p)", ref->trie);
115   return TRUE;
116 }
117 
118 
119 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
120 GC a trie. Note that the  Prolog predicate trie_destroy/1 merely empties
121 the trie, leaving its destruction to the atom garbage collector.
122 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
123 
124 static int
release_trie_ref(atom_t aref)125 release_trie_ref(atom_t aref)
126 { tref *ref = PL_blob_data(aref, NULL, NULL);
127   trie *t;
128 
129   if ( (t=ref->trie) )
130     trie_destroy(t);			/* can be called twice */
131 
132   return TRUE;
133 }
134 
135 
136 static int
save_trie(atom_t aref,IOSTREAM * fd)137 save_trie(atom_t aref, IOSTREAM *fd)
138 { tref *ref = PL_blob_data(aref, NULL, NULL);
139   (void)fd;
140 
141   return PL_warning("Cannot save reference to <trie>(%p)", ref->trie);
142 }
143 
144 
145 static atom_t
load_trie(IOSTREAM * fd)146 load_trie(IOSTREAM *fd)
147 { (void)fd;
148 
149   return PL_new_atom("<saved-trie-ref>");
150 }
151 
152 
153 static PL_blob_t trie_blob =
154 { PL_BLOB_MAGIC,
155   PL_BLOB_UNIQUE,
156   "trie",
157   release_trie_ref,
158   NULL,
159   write_trie_ref,
160   NULL,
161   save_trie,
162   load_trie
163 };
164 
165 		 /*******************************
166 		 *	     THE TRIE		*
167 		 *******************************/
168 
169 static trie_node       *new_trie_node(trie *trie, word key);
170 static void		destroy_node(trie *trie, trie_node *n);
171 static void		clear_node(trie *trie, trie_node *n, int dealloc);
172 static inline void	release_value(word value);
173 
174 
175 static inline void
acquire_key(word key)176 acquire_key(word key)
177 { if ( isAtom(key) )
178     PL_register_atom(key);
179 }
180 
181 static inline void
release_key(word key)182 release_key(word key)
183 { if ( isAtom(key) )
184     PL_unregister_atom(key);
185 }
186 
187 
188 trie *
trie_create(alloc_pool * pool)189 trie_create(alloc_pool *pool)
190 { trie *trie;
191 
192   if ( (trie = alloc_from_pool(pool, sizeof(*trie))) )
193   { memset(trie, 0, sizeof(*trie));
194     trie->magic = TRIE_MAGIC;
195     trie->node_count = 1;		/* the root */
196     trie->alloc_pool = pool;
197   }
198 
199   return trie;
200 }
201 
202 
203 void
trie_destroy(trie * trie)204 trie_destroy(trie *trie)
205 { DEBUG(MSG_TRIE_GC, Sdprintf("Destroying trie %p\n", trie));
206   trie->magic = TRIE_CMAGIC;
207   trie_empty(trie);
208   free_to_pool(trie->alloc_pool, trie, sizeof(*trie));
209 }
210 
211 
212 static void
trie_discard_clause(trie * trie)213 trie_discard_clause(trie *trie)
214 { atom_t dbref;
215 
216   if ( (dbref=trie->clause) )
217   { if ( COMPARE_AND_SWAP_WORD(&trie->clause, dbref, 0) &&
218 	 GD->cleaning == CLN_NORMAL )		/* otherwise reclaims clause */
219     { ClauseRef cref = clause_clref(dbref);	/* from two ends */
220 
221       if ( cref )
222       { Clause cl = cref->value.clause;
223 	set_trie_clause_general_undefined(cl);	/* TBD: only if undefined */
224 	retractClauseDefinition(cl->predicate, cl);
225       }
226       PL_unregister_atom(dbref);
227     }
228   }
229 }
230 
231 
232 void
trie_empty(trie * trie)233 trie_empty(trie *trie)
234 { trie_discard_clause(trie);
235 
236   if ( !trie->references )
237   { indirect_table *it = trie->indirects;
238 
239     clear_node(trie, &trie->root, FALSE);	/* TBD: verify not accessed */
240     if ( it && COMPARE_AND_SWAP_PTR(&trie->indirects, it, NULL) )
241       destroy_indirect_table(it);
242     trie->node_count = 1;
243     trie->value_count = 0;
244   }
245 }
246 
247 
248 void
trie_clean(trie * trie)249 trie_clean(trie *trie)
250 { if ( trie->magic == TRIE_CMAGIC )
251     trie_empty(trie);
252 }
253 
254 
255 static trie_node *
get_child(trie_node * n,word key ARG_LD)256 get_child(trie_node *n, word key ARG_LD)
257 { trie_children children = n->children;
258 
259   if ( children.any )
260   { switch( children.any->type )
261     { case TN_KEY:
262 	if ( children.key->key == key )
263 	  return children.key->child;
264         return NULL;
265       case TN_HASHED:
266 	return lookupHTable(children.hash->table, (void*)key);
267       default:
268 	assert(0);
269     }
270   }
271 
272   return NULL;
273 }
274 
275 
276 static trie_node *
new_trie_node(trie * trie,word key)277 new_trie_node(trie *trie, word key)
278 { trie_node *n;
279 
280   if ( (n = alloc_from_pool(trie->alloc_pool, sizeof(*n))) )
281   { ATOMIC_INC(&trie->node_count);
282     memset(n, 0, sizeof(*n));
283     acquire_key(key);
284     n->key = key;
285   }
286 
287   return n;
288 }
289 
290 
291 static void
clear_node(trie * trie,trie_node * n,int dealloc)292 clear_node(trie *trie, trie_node *n, int dealloc)
293 { trie_children children;
294 
295 next:
296   children = n->children;
297 
298   if ( trie->release_node )
299     (*trie->release_node)(trie, n);
300 
301   release_key(n->key);
302   if ( n->value )
303     release_value(n->value);
304 
305   if ( dealloc )
306   { ATOMIC_DEC(&trie->node_count);
307     free_to_pool(trie->alloc_pool, n, sizeof(trie_node));
308   } else
309   { n->children.any = NULL;
310     clear(n, TN_PRIMARY|TN_SECONDARY);
311   }
312 
313   if ( children.any )
314   { switch( children.any->type )
315     { case TN_KEY:
316       { n = children.key->child;
317 	free_to_pool(trie->alloc_pool, children.key, sizeof(*children.key));
318 	dealloc = TRUE;
319 	goto next;
320       }
321       case TN_HASHED:
322       { Table table = children.hash->table;
323 	TableEnum e = newTableEnum(table);
324 	void *k, *v;
325 	trie_children_key *os;
326 
327 	if ( (os=children.hash->old_single) )	/* see insert_child() (*) note */
328 	  free_to_pool(trie->alloc_pool, os, sizeof(*os));
329 	free_to_pool(trie->alloc_pool, children.hash, sizeof(*children.hash));
330 
331 	while(advanceTableEnum(e, &k, &v))
332 	{ clear_node(trie, v, TRUE);
333 	}
334 
335 	freeTableEnum(e);
336 	destroyHTable(table);
337 	break;
338       }
339     }
340   }
341 }
342 
343 static void
destroy_node(trie * trie,trie_node * n)344 destroy_node(trie *trie, trie_node *n)
345 { clear_node(trie, n, TRUE);
346 }
347 
348 
349 /*
350  * Prune a branch of the trie that does not end in a node.  This should
351  * be used after deletion or unsuccessful insertion, e.g., by trying to
352  * insert a cyclic term
353  *
354  * TBD: Need to think about concurrency here.
355  */
356 
357 void
prune_node(trie * trie,trie_node * n)358 prune_node(trie *trie, trie_node *n)
359 { trie_node *p;
360   int empty = TRUE;
361 
362   for(; empty && n->parent && false(n, TN_PRIMARY|TN_SECONDARY); n = p)
363   { trie_children children;
364 
365     p = n->parent;
366     children = p->children;
367 
368     if ( children.any )
369     { switch( children.any->type )
370       { case TN_KEY:
371 	  if ( COMPARE_AND_SWAP_PTR(&p->children.any, children.any, NULL) )
372 	    PL_free(children.any);
373 	  break;
374 	case TN_HASHED:
375 	  deleteHTable(children.hash->table, (void*)n->key);
376 	  empty = children.hash->table->size == 0;
377 	  break;
378       }
379     }
380 
381     destroy_node(trie, n);
382   }
383 }
384 
385 /** Prune all branches below `root` that do not end in a value.
386  *
387  * @param `free` is called for each removed _leaf_ node.
388 */
389 
390 typedef struct prune_state
391 { TableEnum  e;
392   trie_node *n;
393 } prune_state;
394 
395 void
prune_trie(trie * trie,trie_node * root,void (* free)(trie_node * node,void * ctx),void * ctx)396 prune_trie(trie *trie, trie_node *root,
397 	   void (*free)(trie_node *node, void *ctx), void *ctx)
398 { segstack stack;
399   prune_state buffer[64];
400   trie_children children;
401   trie_node *n = root;
402   trie_node *p;
403   prune_state ps = { .e = NULL };
404 
405   initSegStack(&stack, sizeof(prune_state), sizeof(buffer), buffer);
406 
407   for(;;)
408   { children = n->children;
409 
410     if ( children.any )
411     { switch( children.any->type )
412       { case TN_KEY:
413 	{ n = children.key->child;
414 	  continue;
415 	}
416 	case TN_HASHED:
417 	{ Table table = children.hash->table;
418 	  TableEnum e = newTableEnum(table);
419 	  void *k, *v;
420 
421 	  if ( advanceTableEnum(e, &k, &v) )
422 	  { if ( !pushSegStack(&stack, ps, prune_state) )
423 	      outOfCore();
424 	    ps.e = e;
425 	    ps.n = n;
426 
427 	    n = v;
428 	    continue;
429 	  } else
430 	  { freeTableEnum(e);
431 	    break;
432 	  }
433 	}
434       }
435     } else
436     { if ( free )
437 	(*free)(n, ctx);
438     }
439 
440   prune:
441     for(; n != root && false(n, TN_PRIMARY|TN_SECONDARY); n = p)
442     { trie_children children;
443       int choice = FALSE;
444 
445       p = n->parent;
446       children = p->children;
447 
448       if ( children.any )
449       { switch( children.any->type )
450 	{ case TN_KEY:
451 	    if ( COMPARE_AND_SWAP_PTR(&p->children.any, children.any, NULL) )
452 	      PL_free(children.any);
453 	    break;
454 	  case TN_HASHED:
455 	    deleteHTable(children.hash->table, (void*)n->key);
456 	    choice = TRUE;
457 	    break;
458 	}
459       }
460 
461       destroy_node(trie, n);
462       if ( choice )
463 	break;
464     }
465 
466   next_choice:
467     if ( ps.e )
468     { void *k, *v;
469 
470       if ( advanceTableEnum(ps.e, &k, &v) )
471       { n = v;
472 	continue;
473       } else
474       { n = ps.n;
475 	freeTableEnum(ps.e);
476 	popSegStack(&stack, &ps, prune_state);
477 	assert(n->children.any->type == TN_HASHED);
478 	if ( n->children.hash->table->size == 0 )
479 	  goto prune;
480 	goto next_choice;
481       }
482     } else
483     { break;
484     }
485   }
486 
487   clearSegStack(&stack);
488 }
489 
490 
491 #define VMASKBITS  (sizeof(unsigned)*8)
492 #define VMASK_SCAN (0x1<<(VMASKBITS-1))
493 
494 static inline void
update_var_mask(trie_children_hashed * hnode,word key)495 update_var_mask(trie_children_hashed *hnode, word key)
496 { if ( tagex(key) == TAG_VAR )
497   { size_t vn = (size_t)(key>>LMASK_BITS); /* 1.. */
498     unsigned mask;
499 
500     if ( vn < VMASKBITS )
501       mask = 0x1<<(vn-1);
502     else
503       mask = VMASK_SCAN;
504 
505     ATOMIC_OR(&hnode->var_mask, mask);
506   }
507 }
508 
509 
510 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
511 (*) The single node may be  in  use   with  another  thread. We have two
512 options:
513 
514   - Use one of the LD _active_ pointers to acquire/release access to the
515     trie nodes and use safe delayed release.
516   - Add the ond _single_ node to the new hash node and delete it along
517     with the hash node when we clean the table.  We have opted for this
518     option as it is simple and the single key is neglectable in size
519     compared to the hash table anyway.
520 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
521 
522 static trie_node *
insert_child(trie * trie,trie_node * n,word key ARG_LD)523 insert_child(trie *trie, trie_node *n, word key ARG_LD)
524 { for(;;)
525   { trie_children children = n->children;
526     trie_node *new = new_trie_node(trie, key);
527 
528     if ( !new )
529       return NULL;			/* resource error */
530 
531     if ( children.any )
532     { switch( children.any->type )
533       { case TN_KEY:
534 	{ if ( children.key->key == key )
535 	  { return children.key->child;
536 	  } else
537 	  { trie_children_hashed *hnode;
538 
539 	    if ( !(hnode=alloc_from_pool(trie->alloc_pool, sizeof(*hnode))) )
540 	    { destroy_node(trie, new);
541 	      return NULL;
542 	    }
543 
544 	    hnode->type     = TN_HASHED;
545 	    hnode->table    = newHTable(4);
546 	    hnode->var_mask = 0;
547 	    addHTable(hnode->table, (void*)children.key->key,
548 				    children.key->child);
549 	    addHTable(hnode->table, (void*)key, (void*)new);
550 	    update_var_mask(hnode, children.key->key);
551 	    update_var_mask(hnode, new->key);
552 	    new->parent = n;
553 
554 	    if ( COMPARE_AND_SWAP_PTR(&n->children.hash, children.hash, hnode) )
555 	    { hnode->old_single = children.key;			/* See (*) */
556 	      return new;
557 	    } else
558 	    { hnode->old_single = NULL;
559 	      destroy_node(trie, new);
560 	      destroyHTable(hnode->table);
561 	      free_to_pool(trie->alloc_pool, hnode, sizeof(*hnode));
562 	      continue;
563 	    }
564 	  }
565 	}
566 	case TN_HASHED:
567 	{ trie_node *old = addHTable(children.hash->table,
568 				     (void*)key, (void*)new);
569 
570 	  if ( new == old )
571 	  { new->parent = n;
572 	    update_var_mask(children.hash, new->key);
573 	  } else
574 	  { destroy_node(trie, new);
575 	  }
576 	  return old;
577 	}
578 	default:
579 	  assert(0);
580       }
581     } else
582     { trie_children_key *child;
583 
584       if ( !(child=alloc_from_pool(trie->alloc_pool, sizeof(*child))) )
585       { destroy_node(trie, new);
586 	return NULL;
587       }
588 
589       child->type  = TN_KEY;
590       child->key   = key;
591       child->child = new;
592 
593       if ( COMPARE_AND_SWAP_PTR(&n->children.key, NULL, child) )
594       { child->child->parent = n;
595 	return child->child;
596       }
597       destroy_node(trie, new);
598       free_to_pool(trie->alloc_pool, child, sizeof(*child));
599     }
600   }
601 }
602 
603 
604 static trie_node *
follow_node(trie * trie,trie_node * n,word value,int add ARG_LD)605 follow_node(trie *trie, trie_node *n, word value, int add ARG_LD)
606 { trie_node *child;
607 
608   if ( (child=get_child(n, value PASS_LD)) )
609     return child;
610 
611   if ( add )
612     return insert_child(trie, n, value PASS_LD);
613   else
614     return NULL;
615 }
616 
617 
618 static word
trie_intern_indirect(trie * trie,word w,int add ARG_LD)619 trie_intern_indirect(trie *trie, word w, int add ARG_LD)
620 { for(;;)
621   { if ( trie->indirects )
622     { return intern_indirect(trie->indirects, w, add PASS_LD);
623     } else if ( add )
624     { indirect_table *newtab = new_indirect_table();
625 
626       if ( !COMPARE_AND_SWAP_PTR(&trie->indirects, NULL, newtab) )
627 	destroy_indirect_table(newtab);
628     } else
629     { return 0;
630     }
631   }
632 }
633 
634 
635 /* If there is an error, we prune the part that we have created.
636  * We should only start the prune from a new node though.  To be sure
637  * we do so we first add a new node.  As this is for exception handling
638  * only, the performance loss is not vital.
639  */
640 
641 static void
prune_error(trie * trie,trie_node * node ARG_LD)642 prune_error(trie *trie, trie_node *node ARG_LD)
643 { prune_node(trie, follow_node(trie, node, TRIE_ERROR_VAL, TRUE PASS_LD));
644 }
645 
646 
647 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
648 Lookup `k` in `trie` and on  success   fill  `nodep`  with the leaf node
649 under which `k` is represented in the trie.  If `add` is `TRUE`, add `k`
650 to the trie if it is not already in the true.
651 
652 `vars` is either NULL or a _buffer_ In the latter case it is filled with
653 pointers to the variables found  in  `k`.   This  is  used by tabling to
654 create the `ret` term.
655 
656 Return:
657 
658   - FALSE
659     Could not find term while `add` is FALSE or exception
660   - TRUE
661     Ok (found or inserted)
662   - TRIE_ABSTRACTED
663     Ok, but abstracted
664   - TRIE_LOOKUP_CONTAINS_ATTVAR
665   - TRIE_LOOKUP_CYCLIC
666 
667 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
668 
669 int
trie_lookup_abstract(trie * trie,trie_node * node,trie_node ** nodep,Word k,int add,size_abstract * abstract,TmpBuffer vars ARG_LD)670 trie_lookup_abstract(trie *trie, trie_node *node, trie_node **nodep,
671 		     Word k, int add, size_abstract *abstract,
672 		     TmpBuffer vars ARG_LD)
673 { term_agenda_P agenda;
674   size_t var_number = 0;
675   int rc = TRUE;
676   size_t compounds = 0;
677   tmp_buffer varb;
678   size_abstract sa = {.from_depth = 1, .size = (size_t)-1};
679   size_t aleft = (size_t)-1;
680 
681   TRIE_STAT_INC(trie, lookups);
682   if ( !node )
683     node = &trie->root;
684   if ( abstract )
685     sa = *abstract;
686 
687   initTermAgenda_P(&agenda, 1, k);
688   while( node )
689   { Word p;
690     word w;
691     size_t popn;
692 
693     if ( !(p=nextTermAgenda_P(&agenda)) )
694       break;
695     if ( (popn = IS_AC_TERM_POP(p)) )
696     { compounds -= popn;
697       if ( compounds > 0 )
698       { if ( !(node = follow_node(trie, node, TRIE_KEY_POP(popn), add PASS_LD)) )
699 	  break;
700 	continue;
701       } else
702 	break;				/* finished toplevel */
703     }
704 
705     if ( compounds == sa.from_depth )
706       aleft = sa.size;
707 
708     w = *p;
709     switch( tag(w) )
710     { case TAG_VAR:
711 	if ( isVar(w) )
712 	{ word w2;
713 
714 	add_var:
715 	  if ( var_number++ == 0 && !vars )
716 	  { vars = &varb;
717 	    initBuffer(vars);
718 	  }
719 	  addBuffer(vars, p, Word);
720 	  w2 = ((((word)var_number))<<LMASK_BITS)|TAG_VAR;
721 	  if ( tag(w) == TAG_VAR )
722 	    *p = w2;
723 	  w = w2;
724 	}
725         node = follow_node(trie, node, w, add PASS_LD);
726 	break;
727       case TAG_ATTVAR:
728 	rc = TRIE_LOOKUP_CONTAINS_ATTVAR;
729 
730         prune_error(trie, node PASS_LD);
731         node = NULL;
732         break;
733       case TAG_COMPOUND:
734       { if ( unlikely(aleft == 0) )
735 	{ rc = TRIE_ABSTRACTED;
736 	  goto add_var;
737 	} else
738 	{ Functor f = valueTerm(w);
739 	  size_t arity = arityFunctor(f->definition);
740 
741 	  if ( aleft != (size_t)-1 )
742 	    aleft--;
743 	  if ( ++compounds == 1000 && add && !is_acyclic(p PASS_LD) )
744 	  { rc = TRIE_LOOKUP_CYCLIC;
745 	    prune_error(trie, node PASS_LD);
746 	    node = NULL;
747 	  } else
748 	  { node = follow_node(trie, node, f->definition, add PASS_LD);
749 	    pushWorkAgenda_P(&agenda, arity, f->arguments);
750 	  }
751 	}
752 	break;
753       }
754       default:
755       { if ( !isIndirect(w) )
756 	{ node = follow_node(trie, node, w, add PASS_LD);
757 	} else
758 	{ word i = trie_intern_indirect(trie, w, add PASS_LD);
759 
760 	  if ( i )
761 	    node = follow_node(trie, node, i, add PASS_LD);
762 	  else
763 	    node = NULL;
764 	}
765       }
766     }
767   }
768   clearTermAgenda_P(&agenda);
769 
770   if ( var_number )
771   { Word *pp = baseBuffer(vars, Word);
772     Word *ep = topBuffer(vars, Word);
773 
774     for(; pp < ep; pp++)
775     { Word vp = *pp;
776       if ( tag(*vp) == TAG_VAR )
777 	setVar(*vp);
778     }
779     if ( vars == &varb )
780       discardBuffer(vars);
781   }
782 
783   if ( rc > 0 )
784   { if ( node )
785       *nodep = node;
786     else
787       rc = FALSE;
788   }
789 
790   return rc;
791 }
792 
793 
794 trie *
get_trie_from_node(trie_node * node)795 get_trie_from_node(trie_node *node)
796 { trie *trie_ptr;
797 
798   for( ; node->parent; node = node->parent )
799     ;
800   trie_ptr = (trie *)((char*)node - offsetof(trie, root));
801   assert(trie_ptr->magic == TRIE_MAGIC || trie_ptr->magic == TRIE_CMAGIC);
802 
803   return trie_ptr;
804 }
805 
806 
807 int
is_ground_trie_node(trie_node * node)808 is_ground_trie_node(trie_node *node)
809 { for( ; node->parent; node = node->parent )
810   { if ( tagex(node->key) == TAG_VAR )
811       return FALSE;
812   }
813 
814   return TRUE;
815 }
816 
817 
818 		 /*******************************
819 		 *    BUILD TERM FROM PATH	*
820 		 *******************************/
821 
822 #if 0					/* debugging help */
823 static int
824 print_key(word k)
825 { size_t pop;
826 
827   if ( (pop = IS_TRIE_KEY_POP(k)) )
828   { Sdprintf("POP(%zd)\n", pop);
829   } else
830   { char buf[64];
831 
832     Sdprintf("%s\n", print_val(k, buf));
833   }
834 
835   return TRUE;
836 }
837 
838 static int
839 print_keys(Word k, size_t kc)
840 { size_t i;
841 
842   for(i=kc; i-->0; )
843     print_key(k[i]);
844 
845   return TRUE;
846 }
847 #endif
848 
849 
850 #define MAX_FAST 256
851 
852 int
unify_trie_term(trie_node * node,trie_node ** parent,term_t term ARG_LD)853 unify_trie_term(trie_node *node, trie_node **parent, term_t term ARG_LD)
854 { word fast[MAX_FAST];
855   Word keys = fast;
856   size_t keys_allocated = MAX_FAST;
857   size_t kc = 0;
858   int rc = TRUE;
859   trie *trie_ptr;
860   mark m;
861   int is_secondary = true(node, TN_SECONDARY);
862 						/* get the keys */
863   for( ; node->parent; node = node->parent )
864   { if ( is_secondary && true(node, TN_PRIMARY) )
865     { if ( parent )
866 	*parent = node;
867       break;
868     }
869     if ( kc == keys_allocated )
870     { keys_allocated *= 2;
871       if ( keys == fast )
872       { if ( (keys = malloc(sizeof(*keys)*keys_allocated)) )
873 	  memcpy(keys, fast, sizeof(fast));
874 	else
875 	  return PL_resource_error("memory");
876       } else
877       { Word newkeys;
878 	if ( !(newkeys=realloc(keys, sizeof(*keys)*keys_allocated)) )
879 	{ free(keys);
880 	  return PL_resource_error("memory");
881 	}
882 	keys = newkeys;
883       }
884     }
885 
886     keys[kc++] = node->key;
887   }
888   for( ; node->parent; node = node->parent )
889     ;
890   trie_ptr = (trie *)((char*)node - offsetof(trie, root));
891   assert(trie_ptr->magic == TRIE_MAGIC);
892 
893   for(;;)
894   { ukey_state ustate;
895     size_t i;
896 
897   retry:
898     Mark(m);
899     init_ukey_state(&ustate, trie_ptr, valTermRef(term) PASS_LD);
900     for(i=kc; i-- > 0; )
901     { if ( (rc=unify_key(&ustate, keys[i] PASS_LD)) != TRUE )
902       { destroy_ukey_state(&ustate PASS_LD);
903 	if ( rc == FALSE )
904 	  goto out;
905 	Undo(m);
906 	if ( (rc=makeMoreStackSpace(rc, ALLOW_GC)) )
907 	  goto retry;
908 	else
909 	  goto out;
910       }
911     }
912     destroy_ukey_state(&ustate PASS_LD);
913     break;
914   }
915 
916 out:
917   if ( keys != fast )
918     free(keys);
919 
920   return rc;
921 }
922 
923 
924 void *
map_trie_node(trie_node * n,void * (* map)(trie_node * n,void * ctx),void * ctx)925 map_trie_node(trie_node *n,
926 	      void* (*map)(trie_node *n, void *ctx), void *ctx)
927 { trie_children children;
928   void *rc;
929 
930 next:
931   children = n->children;
932 
933   if ( (rc=(*map)(n, ctx)) != NULL )
934     return rc;
935 
936   if ( children.any  )
937   { switch( children.any->type )
938     { case TN_KEY:
939       { n = children.key->child;
940 	goto next;
941       }
942       case TN_HASHED:
943       { Table table = children.hash->table;
944 	TableEnum e = newTableEnum(table);
945 	void *k, *v;
946 
947 	while(advanceTableEnum(e, &k, &v))
948 	{ if ( (rc=map_trie_node(v, map, ctx)) != NULL )
949 	  { freeTableEnum(e);
950 	    return rc;
951 	  }
952 	}
953 
954 	freeTableEnum(e);
955 	break;
956       }
957     }
958   }
959 
960   return NULL;
961 }
962 
963 
964 typedef struct trie_stats
965 { size_t bytes;
966   size_t nodes;
967   size_t hashes;
968   size_t values;
969 } trie_stats;
970 
971 
972 static void *
stat_node(trie_node * n,void * ctx)973 stat_node(trie_node *n, void *ctx)
974 { trie_stats *stats = ctx;
975   trie_children children = n->children;
976 
977   stats->nodes++;
978   stats->bytes += sizeof(*n);
979   if ( n->value )
980     stats->values++;
981 
982   if ( children.any )
983   { switch( children.any->type )
984     { case TN_KEY:
985 	stats->bytes += sizeof(*children.key);
986         break;
987       case TN_HASHED:
988 	stats->bytes += sizeofTable(children.hash->table);
989 	stats->hashes++;
990 	break;
991       default:
992 	assert(0);
993     }
994   }
995 
996   return NULL;
997 }
998 
999 
1000 static void
stat_trie(trie * t,trie_stats * stats)1001 stat_trie(trie *t, trie_stats *stats)
1002 { stats->bytes  = sizeof(*t) - sizeof(t->root);
1003   stats->nodes  = 0;
1004   stats->hashes = 0;
1005   stats->values = 0;
1006 
1007   acquire_trie(t);
1008   map_trie_node(&t->root, stat_node, stats);
1009   release_trie(t);
1010 }
1011 
1012 
1013 
1014 
1015 		 /*******************************
1016 		 *	  PROLOG BINDING	*
1017 		 *******************************/
1018 
1019 atom_t
trie_symbol(trie * trie)1020 trie_symbol(trie *trie)
1021 { if ( !trie->symbol )
1022   { tref ref;
1023     int new;
1024 
1025     ref.trie = trie;
1026     trie->symbol = lookupBlob((void*)&ref, sizeof(ref),
1027 			      &trie_blob, &new);
1028   }
1029 
1030   return trie->symbol;
1031 }
1032 
1033 
1034 trie *
symbol_trie(atom_t symbol)1035 symbol_trie(atom_t symbol)
1036 { void *data;
1037   PL_blob_t *type;
1038 
1039   if ( (data = PL_blob_data(symbol, NULL, &type)) && type == &trie_blob )
1040   { tref *ref = data;
1041 
1042     if ( ref->trie->magic == TRIE_MAGIC )
1043       return ref->trie;
1044   }
1045 
1046   return NULL;
1047 }
1048 
1049 
1050 #define unify_trie(t, trie) unify_trie__LD(t, trie PASS_LD)
1051 
1052 static int
unify_trie__LD(term_t t,trie * trie ARG_LD)1053 unify_trie__LD(term_t t, trie *trie ARG_LD)
1054 { return PL_unify_atom(t, trie->symbol);
1055 }
1056 
1057 int
get_trie(term_t t,trie ** tp)1058 get_trie(term_t t, trie **tp)
1059 { void *data;
1060   PL_blob_t *type;
1061 
1062   if ( PL_get_blob(t, &data, NULL, &type) && type == &trie_blob )
1063   { tref *ref = data;
1064 
1065     if ( ref->trie->magic == TRIE_MAGIC )
1066     { *tp = ref->trie;
1067       return TRUE;
1068     }
1069 
1070     PL_existence_error("trie", t);
1071   } else
1072     PL_type_error("trie", t);
1073 
1074   return FALSE;
1075 }
1076 
1077 
1078 int
get_trie_noex(term_t t,trie ** tp)1079 get_trie_noex(term_t t, trie **tp)
1080 { void *data;
1081   PL_blob_t *type;
1082 
1083   if ( PL_get_blob(t, &data, NULL, &type) && type == &trie_blob )
1084   { tref *ref = data;
1085 
1086     *tp = ref->trie;
1087     return TRUE;
1088   }
1089 
1090   return FALSE;
1091 }
1092 
1093 
1094 int
trie_error(int rc,term_t culprit)1095 trie_error(int rc, term_t culprit)
1096 { switch(rc)
1097   { case TRIE_LOOKUP_CONTAINS_ATTVAR:
1098       return PL_type_error("free_of_attvar", culprit);
1099     case TRIE_LOOKUP_CYCLIC:
1100       return PL_type_error("acyclic_term", culprit);
1101     default:
1102       return FALSE;
1103   }
1104 }
1105 
1106 int
trie_trie_error(int rc,trie * trie)1107 trie_trie_error(int rc, trie *trie)
1108 { GET_LD
1109   term_t t;
1110 
1111   return ( (t= PL_new_term_ref()) &&
1112 	   PL_put_atom(t, trie->symbol) &&
1113 	   trie_error(rc, t) );
1114 }
1115 
1116 
1117 static
1118 PRED_IMPL("trie_new", 1, trie_new, 0)
1119 { PRED_LD
1120   trie *trie;
1121 
1122   if ( (trie = trie_create(NULL)) )
1123   { atom_t symbol = trie_symbol(trie);
1124     int rc;
1125 
1126     rc = unify_trie(A1, trie);
1127     PL_unregister_atom(symbol);
1128 
1129     return rc;
1130   }
1131 
1132   return FALSE;
1133 }
1134 
1135 
1136 static
1137 PRED_IMPL("is_trie", 1, is_trie, 0)
1138 { void *data;
1139   PL_blob_t *type;
1140 
1141   if ( PL_get_blob(A1, &data, NULL, &type) && type == &trie_blob )
1142   { tref *ref = data;
1143 
1144     if ( ref->trie->magic == TRIE_MAGIC )
1145       return TRUE;
1146   }
1147 
1148   return FALSE;
1149 }
1150 
1151 
1152 static
1153 PRED_IMPL("trie_destroy", 1, trie_destroy, 0)
1154 { trie *trie;
1155 
1156   if ( get_trie(A1, &trie) )
1157   { trie->magic = TRIE_CMAGIC;
1158     trie_empty(trie);
1159 
1160     return TRUE;
1161   }
1162 
1163   return FALSE;
1164 }
1165 
1166 
1167 #define isRecord(w) (((w)&0x3) == 0)
1168 
1169 static word
intern_value(term_t value ARG_LD)1170 intern_value(term_t value ARG_LD)
1171 { if ( value )
1172   { Word vp = valTermRef(value);
1173 
1174     DEBUG(0, assert((TAG_INTEGER&0x3) && (TAG_ATOM&0x3)));
1175 
1176     deRef(vp);
1177     if ( isAtom(*vp) || isTaggedInt(*vp) )
1178       return *vp;
1179 
1180     return (word)PL_record(value);
1181   } else
1182   { return ATOM_trienode;
1183   }
1184 }
1185 
1186 
1187 static inline void
release_value(word value)1188 release_value(word value)
1189 { if ( isAtom(value) )
1190     PL_unregister_atom(value);
1191   else if ( isRecord(value) )
1192     PL_erase((record_t)value);
1193 }
1194 
1195 
1196 static int
equal_value(word v1,word v2)1197 equal_value(word v1, word v2)
1198 { if ( v1 == v2 )
1199     return TRUE;
1200 
1201   if ( isRecord(v1) && isRecord(v2) )
1202     return variantRecords((record_t)v1, (record_t)v2);
1203 
1204   return FALSE;
1205 }
1206 
1207 
1208 static int
unify_value(term_t t,word value ARG_LD)1209 unify_value(term_t t, word value ARG_LD)
1210 { if ( !isRecord(value) )
1211   { return _PL_unify_atomic(t, value);
1212   } else
1213   { term_t t2;
1214 
1215     return ( (t2=PL_new_term_ref()) &&
1216 	     PL_recorded((record_t)value, t2) &&
1217 	     PL_unify(t, t2)
1218 	   );
1219   }
1220 }
1221 
1222 
1223 int
put_trie_value(term_t t,trie_node * node ARG_LD)1224 put_trie_value(term_t t, trie_node *node ARG_LD)
1225 { if ( !isRecord(node->value) )
1226   { *valTermRef(t) = node->value;
1227     return TRUE;
1228   } else
1229   { return PL_recorded((record_t)node->value, t);
1230   }
1231 }
1232 
1233 
1234 int
set_trie_value_word(trie * trie,trie_node * node,word val)1235 set_trie_value_word(trie *trie, trie_node *node, word val)
1236 { if ( node->value )
1237   { if ( !equal_value(node->value, val) )
1238     { word old = node->value;
1239 
1240       acquire_key(val);
1241       node->value = val;
1242       set(node, TN_PRIMARY);
1243       release_value(old);
1244       trie_discard_clause(trie);
1245 
1246       return TRUE;
1247     } else
1248     { return FALSE;
1249     }
1250   } else
1251   { acquire_key(val);
1252     node->value = val;
1253     set(node, TN_PRIMARY);
1254     ATOMIC_INC(&trie->value_count);
1255     trie_discard_clause(trie);
1256 
1257     return TRUE;
1258   }
1259 }
1260 
1261 int
set_trie_value(trie * trie,trie_node * node,term_t value ARG_LD)1262 set_trie_value(trie *trie, trie_node *node, term_t value ARG_LD)
1263 { word val = intern_value(value PASS_LD);
1264 
1265   if ( !set_trie_value_word(trie, node, val) &&
1266        isRecord(val) )
1267     PL_erase((record_t)val);
1268 
1269   return TRUE;
1270 }
1271 
1272 
1273 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1274 Delete a node from the trie. There are   two options: (1) simply set the
1275 value to 0 or (2), prune the branch   leading to this cell upwards until
1276 we find another existing node.
1277 
1278 TBD: create some sort of  lingering   mechanism  to allow for concurrent
1279 delete and gen_trie(). More modest: link up the deleted nodes and remove
1280 them after the references drop to 0.
1281 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1282 
1283 void
trie_delete(trie * trie,trie_node * node,int prune)1284 trie_delete(trie *trie, trie_node *node, int prune)
1285 { if ( node->value )
1286   { clear(node, (TN_PRIMARY|TN_SECONDARY));
1287     if ( prune && trie->references == 0 )
1288     { prune_node(trie, node);
1289     } else
1290     { word v;
1291 
1292       if ( trie->release_node )
1293 	(*trie->release_node)(trie, node);
1294 
1295       if ( (v=node->value) )
1296       { node->value = 0;
1297 	release_value(v);
1298       }
1299     }
1300     ATOMIC_DEC(&trie->value_count);
1301     trie_discard_clause(trie);
1302   }
1303 }
1304 
1305 
1306 /**
1307  * trie_insert(+Trie, +Key, +Value) is semidet.
1308  *
1309  * True if Key was added as a new   key  to the trie and associated with
1310  * Value. False if Key was already in the trie with Value
1311  *
1312  * @error permission_error if Key was associated with a different value
1313  */
1314 
1315 static int
trie_insert(term_t Trie,term_t Key,term_t Value,trie_node ** nodep,int update,size_abstract * abstract ARG_LD)1316 trie_insert(term_t Trie, term_t Key, term_t Value, trie_node **nodep,
1317 	    int update, size_abstract *abstract ARG_LD)
1318 { trie *trie;
1319 
1320   if ( get_trie(Trie, &trie) )
1321   { Word kp;
1322     trie_node *node;
1323     int rc;
1324 
1325     if ( false(trie, TRIE_ISMAP|TRIE_ISSET) )
1326     { if ( Value )
1327 	set(trie, TRIE_ISMAP);
1328       else
1329 	set(trie, TRIE_ISSET);
1330     } else
1331     { if ( (Value  && false(trie, TRIE_ISMAP)) ||
1332 	   (!Value && false(trie, TRIE_ISSET)) )
1333       { return PL_permission_error("insert", "trie", Trie);
1334       }
1335     }
1336 
1337     kp	= valTermRef(Key);
1338 
1339     if ( (rc=trie_lookup_abstract(trie, NULL, &node, kp,
1340 				  TRUE, abstract, NULL PASS_LD)) == TRUE )
1341     { word val = intern_value(Value PASS_LD);
1342 
1343       if ( nodep )
1344 	*nodep = node;
1345 
1346       if ( node->value )
1347       { if ( update )
1348 	{ if ( !equal_value(node->value, val) )
1349 	  { word old = node->value;
1350 
1351 	    acquire_key(val);
1352 	    node->value = val;
1353 	    set(node, TN_PRIMARY);
1354 	    release_value(old);
1355 	    trie_discard_clause(trie);
1356 	  } else if ( isRecord(val) )
1357 	  { PL_erase((record_t)val);
1358 	  }
1359 
1360 	  return TRUE;
1361 	} else
1362 	{ if ( !equal_value(node->value, val) )
1363 	    PL_permission_error("modify", "trie_key", Key);
1364 	  if ( isRecord(val) )
1365 	    PL_erase((record_t)val);
1366 
1367 	  return FALSE;
1368 	}
1369       }
1370       acquire_key(val);
1371       node->value = val;
1372       set(node, TN_PRIMARY);
1373       ATOMIC_INC(&trie->value_count);
1374       trie_discard_clause(trie);
1375 
1376       return TRUE;
1377     }
1378 
1379     return trie_error(rc, Key);
1380   }
1381 
1382   return FALSE;
1383 }
1384 
1385 
1386 /**
1387  * trie_insert(+Trie, +Key, +Value) is semidet.
1388  *
1389  * True if Key was added as a new   key  to the trie and associated with
1390  * Value. False if Key was already in the trie with Value
1391  *
1392  * @error permission_error if Key was associated with a different value
1393  */
1394 
1395 static
1396 PRED_IMPL("trie_insert", 3, trie_insert, 0)
1397 { PRED_LD
1398 
1399   return trie_insert(A1, A2, A3, NULL, FALSE, NULL PASS_LD);
1400 }
1401 
1402 /**
1403  * trie_insert(+Trie, +Key) is semidet.
1404  *
1405  * True if Key was added as a new key to the trie.  False if Key was
1406  * already in the trie.
1407  *
1408  * @error permission_error if Key was associated with a different value
1409  */
1410 
1411 static
1412 PRED_IMPL("trie_insert", 2, trie_insert, 0)
1413 { PRED_LD
1414 
1415   return trie_insert(A1, A2, 0, NULL, FALSE, NULL PASS_LD);
1416 }
1417 
1418 
1419 /**
1420  * trie_insert_abstract(+Trie, +Size, +Key) is semidet.
1421  *
1422  * Insert size-abstracted version of Key
1423  */
1424 
1425 static
1426 PRED_IMPL("$trie_insert_abstract", 3, trie_insert_abstract, 0)
1427 { PRED_LD
1428   size_abstract sa = {.from_depth = 1};
1429 
1430   return ( PL_get_size_ex(A2, &sa.size ) &&
1431 	   trie_insert(A1, A3, 0, NULL, FALSE, &sa PASS_LD) > 0 );
1432 }
1433 
1434 
1435 /**
1436  * trie_update(+Trie, +Key, +Value) is semidet.
1437  *
1438  * Similar to trie_insert/3, but updates the associated value rather
1439  * then failing or raising an error.
1440  *
1441  * @error permission_error if Key was associated with a different value
1442  */
1443 
1444 static
1445 PRED_IMPL("trie_update", 3, trie_update, 0)
1446 { PRED_LD
1447 
1448   return trie_insert(A1, A2, A3, NULL, TRUE, NULL PASS_LD);
1449 }
1450 
1451 
1452 /**
1453  * trie_insert(+Trie, +Term, +Value, -Handle) is semidet.
1454  *
1455  * Add Term to Trie and unify Handle with a handle to the term.
1456  * Fails if Term is already in Trie.
1457  *
1458  * @bug Handle is currently a pointer.  In future versions we will
1459  * use a dynamic array for the trie nodes and return an integer to
1460  * guarantee safe lookup.
1461  */
1462 
1463 static
1464 PRED_IMPL("trie_insert", 4, trie_insert, 0)
1465 { PRED_LD
1466   trie_node *node;
1467 
1468   return ( trie_insert(A1, A2, A3, &node, FALSE, NULL PASS_LD) &&
1469 	   PL_unify_pointer(A4, node) );
1470 }
1471 
1472 
1473 static
1474 PRED_IMPL("trie_delete", 3, trie_delete, 0)
1475 { PRED_LD
1476   trie *trie;
1477 
1478   if ( get_trie(A1, &trie) )
1479   { Word kp;
1480     trie_node *node;
1481     int rc;
1482 
1483     kp = valTermRef(A2);
1484 
1485     if ( (rc=trie_lookup(trie, NULL, &node, kp, FALSE, NULL PASS_LD)) == TRUE )
1486     { if ( node->value )
1487       { if ( unify_value(A3, node->value PASS_LD) )
1488 	{ trie_delete(trie, node, TRUE);
1489 	  return TRUE;
1490 	}
1491       }
1492       return FALSE;
1493     }
1494 
1495     return trie_error(rc, A2);
1496   }
1497 
1498   return FALSE;
1499 }
1500 
1501 
1502 static
1503 PRED_IMPL("trie_lookup", 3, trie_lookup, 0)
1504 { PRED_LD
1505   trie *trie;
1506 
1507   if ( get_trie(A1, &trie) )
1508   { Word kp;
1509     trie_node *node;
1510     int rc;
1511 
1512     kp = valTermRef(A2);
1513 
1514     if ( (rc=trie_lookup(trie, NULL, &node, kp, FALSE, NULL PASS_LD)) == TRUE )
1515     { if ( node->value )
1516 	return unify_value(A3, node->value PASS_LD);
1517       return FALSE;
1518     }
1519 
1520     return trie_error(rc, A2);
1521   }
1522 
1523   return FALSE;
1524 }
1525 
1526 
1527 /**
1528  * trie_term(+Handle, -Term) is det.
1529  *
1530  * Retrieve a term for a handle returned by trie_insert/4.
1531  */
1532 
1533 static
1534 PRED_IMPL("trie_term", 2, trie_term, 0)
1535 { PRED_LD
1536   void *ptr;
1537 
1538   return ( PL_get_pointer_ex(A1, &ptr) &&
1539 	   unify_trie_term(ptr, NULL, A2 PASS_LD)
1540 	 );
1541 }
1542 
1543 
1544 /**
1545  * trie_gen(+Trie, ?Key, -Value) is nondet.
1546  *
1547  * True when Key-Value appears in Trie.
1548  *
1549  * This needs to keep  a  list  of   choice  points  for  each node with
1550  * multiple children. Eventually, this is probably going to be a virtual
1551  * machine extension, using real choice points.
1552  */
1553 
1554 static void
init_ukey_state(ukey_state * state,trie * trie,Word p ARG_LD)1555 init_ukey_state(ukey_state *state, trie *trie, Word p ARG_LD)
1556 { state->trie = trie;
1557   state->ptr  = p;
1558   state->umode = uread;
1559   state->max_var_seen = 0;
1560   state->a_offset = aTop-aBase;
1561 }
1562 
1563 static void
destroy_ukey_state(ukey_state * state ARG_LD)1564 destroy_ukey_state(ukey_state *state ARG_LD)
1565 { if ( state->max_var_seen && state->vars != state->var_buf )
1566     PL_free(state->vars);
1567   aTop = aBase + state->a_offset;
1568 }
1569 
1570 static Word*
find_var(ukey_state * state,size_t index)1571 find_var(ukey_state *state, size_t index)
1572 { if ( index > state->max_var_seen )
1573   { assert(index == state->max_var_seen+1);
1574 
1575     if ( !state->max_var_seen )
1576     { state->vars_allocated = NVARS_FAST;
1577       state->vars = state->var_buf;
1578     } else if ( index >= state->vars_allocated )
1579     { if ( state->vars == state->var_buf )
1580       { state->vars = PL_malloc(sizeof(*state->vars)*NVARS_FAST*2);
1581 	memcpy(state->vars, state->var_buf, sizeof(*state->vars)*NVARS_FAST);
1582       } else
1583       { state->vars = PL_realloc(state->vars,
1584 				 sizeof(*state->vars)*state->vars_allocated*2);
1585       }
1586       state->vars_allocated *= 2;
1587     }
1588     state->vars[index] = NULL;
1589     state->max_var_seen = index;
1590   }
1591 
1592   return &state->vars[index];
1593 }
1594 
1595 
1596 static int
unify_key(ukey_state * state,word key ARG_LD)1597 unify_key(ukey_state *state, word key ARG_LD)
1598 { Word p = state->ptr;
1599 
1600   switch(tagex(key))
1601   { case TAG_VAR|STG_LOCAL:			/* RESERVED_TRIE_VAL */
1602     { size_t popn = IS_TRIE_KEY_POP(key);
1603       Word wp;
1604 
1605       assert(popn);
1606       aTop -= popn;
1607       wp = *aTop;
1608       state->umode = ((int)(uintptr_t)wp & uwrite);
1609       state->ptr   = (Word)((intptr_t)wp&~uwrite);
1610 
1611       DEBUG(MSG_TRIE_PUT_TERM,
1612 	    Sdprintf("U Popped(%zd) %zd, mode=%d\n",
1613 		     popn, state->ptr-gBase, state->umode));
1614       return TRUE;
1615     }
1616     case TAG_ATOM|STG_GLOBAL:			/* functor */
1617     { size_t arity = arityFunctor(key);
1618 
1619       DEBUG(MSG_TRIE_PUT_TERM,
1620 	    Sdprintf("U Pushed %s %zd, mode=%d\n",
1621 		     functorName(key), state->ptr+1-gBase, state->umode));
1622       pushArgumentStack((Word)((intptr_t)(p + 1)|state->umode));
1623 
1624       if ( state->umode == uwrite )
1625       { Word t;
1626 
1627 	if ( (t=allocGlobalNoShift(arity+1)) )
1628 	{ t[0] = key;
1629 	  *p = consPtr(t, TAG_COMPOUND|STG_GLOBAL);
1630 	  state->ptr = &t[1];
1631 	  return TRUE;
1632 	} else
1633 	  return GLOBAL_OVERFLOW;
1634       } else
1635       { deRef(p);
1636 
1637 	if ( canBind(*p) )
1638 	{ state->umode = uwrite;
1639 
1640 	  if ( isAttVar(*p) )
1641 	  { Word t;
1642 	    word w;
1643 	    size_t i;
1644 
1645 	    if ( (t=allocGlobalNoShift(arity+1)) )
1646 	    { if ( !hasGlobalSpace(0) )
1647 		return overflowCode(0);
1648 	      w = consPtr(&t[0], TAG_COMPOUND|STG_GLOBAL);
1649 	      t[0] = key;
1650 	      for(i=0; i<arity; i++)
1651 		setVar(t[i+1]);
1652 	      assignAttVar(p, &w PASS_LD);
1653 	      state->ptr = &t[1];
1654 	      return TRUE;
1655 	    } else
1656 	      return GLOBAL_OVERFLOW;
1657 	  } else
1658 	  { Word t;
1659 	    size_t i;
1660 
1661 	    if ( (t=allocGlobalNoShift(arity+1)) )
1662 	    { if ( unlikely(tTop+1 >= tMax) )
1663 		return  TRAIL_OVERFLOW;
1664 	      t[0] = key;
1665 	      for(i=0; i<arity; i++)
1666 		setVar(t[i+1]);
1667 	      Trail(p, consPtr(t, TAG_COMPOUND|STG_GLOBAL));
1668 	      state->ptr = &t[1];
1669 	      return TRUE;
1670 	    } else
1671 	      return GLOBAL_OVERFLOW;
1672 	  }
1673 	} else if ( isTerm(*p) )
1674 	{ Functor f = valueTerm(*p);
1675 
1676 	  if ( f->definition == key )
1677 	  { state->ptr = &f->arguments[0];
1678 	    return TRUE;
1679 	  } else
1680 	    return FALSE;
1681 	} else
1682 	{ return FALSE;
1683 	}
1684       } /*uread*/
1685     }
1686     assert(0);
1687     case TAG_VAR:
1688     { size_t index = (size_t)(key>>LMASK_BITS);
1689       Word *v = find_var(state, index);
1690 
1691       DEBUG(MSG_TRIE_PUT_TERM,
1692 	    { char b1[64]; char b2[64];
1693 	      Sdprintf("var %zd at %s (v=%p, *v=%s)\n",
1694 		       index,
1695 		       print_addr(state->ptr,b1),
1696 		       v, print_addr(*v,b2));
1697 	    });
1698 
1699       if ( state->umode == uwrite )
1700       { if ( !*v )
1701 	{ setVar(*state->ptr);
1702 	  *v = state->ptr;
1703 	} else
1704 	{ *state->ptr = makeRefG(*v);
1705 	}
1706       } else
1707       { deRef(p);
1708 
1709 	if ( !*v )
1710 	{ *v = state->ptr;
1711 	} else
1712 	{ int rc;
1713 
1714 	  if ( (rc=unify_ptrs(state->ptr, *v, ALLOW_RETCODE PASS_LD)) != TRUE )
1715 	    return rc;
1716 	}
1717       }
1718 
1719       break;
1720     }
1721     assert(0);
1722     case STG_GLOBAL|TAG_INTEGER:		/* indirect data */
1723     case STG_GLOBAL|TAG_STRING:
1724     case STG_GLOBAL|TAG_FLOAT:
1725     { word w;
1726 
1727       w = extern_indirect_no_shift(state->trie->indirects, key PASS_LD);
1728       if ( !w )
1729 	return GLOBAL_OVERFLOW;
1730 
1731       if ( state->umode == uwrite )
1732       { *p = w;
1733       } else
1734       { deRef(p);
1735 
1736 	if ( canBind(*p) )
1737 	{ if ( hasGlobalSpace(0) )
1738 	    bindConst(p, w);
1739 	  else
1740 	    return overflowCode(0);
1741 	} else
1742 	{ if ( !equalIndirect(w, *p) )
1743 	    return FALSE;
1744 	}
1745       }
1746 
1747       break;
1748     }
1749     case TAG_ATOM:
1750       pushVolatileAtom(key);
1751       /*FALLTHROUGH*/
1752     case TAG_INTEGER:
1753     { if ( state->umode == uwrite )
1754       { *p = key;
1755       } else
1756       { deRef(p);
1757 
1758 	if ( canBind(*p) )
1759 	{ if ( hasGlobalSpace(0) )
1760 	    bindConst(p, key);
1761 	  else
1762 	    return overflowCode(0);
1763 	} else
1764 	{ if ( *p != key )
1765 	    return FALSE;
1766 	}
1767       }
1768       break;
1769     }
1770     default:
1771       assert(0);
1772   }
1773 
1774   state->ptr++;
1775 
1776   return TRUE;
1777 }
1778 
1779 
1780 typedef struct trie_choice
1781 { TableEnum  table_enum;
1782   Table      table;
1783   unsigned   var_mask;
1784   unsigned   var_index;
1785   word       novar;
1786   word       key;
1787   trie_node *child;
1788 } trie_choice;
1789 
1790 typedef struct
1791 { trie	      *trie;		/* trie we operate on */
1792   int	       allocated;	/* If TRUE, the state is persistent */
1793   unsigned     vflags;		/* TN_PRIMARY or TN_SECONDARY */
1794   tmp_buffer   choicepoints;	/* Stack of trie state choicepoints */
1795 } trie_gen_state;
1796 
1797 typedef struct desc_tstate
1798 { Word  term;
1799   size_t size;
1800 } desc_tstate;
1801 
1802 typedef struct
1803 { Word	       term;		/* Term we are descending */
1804   size_t       size;		/* Size of the current node */
1805   int	       compound;	/* Initialized for compound */
1806   int	       prune;		/* Use for pruning */
1807   segstack     stack;		/* Stack for argument handling */
1808   desc_tstate  buffer[64];	/* Quick buffer for stack */
1809 } descent_state;
1810 
1811 static int	advance_node(trie_choice *ch ARG_LD);
1812 
1813 static void
init_trie_state(trie_gen_state * state,trie * trie,const trie_node * root)1814 init_trie_state(trie_gen_state *state, trie *trie, const trie_node *root)
1815 { state->trie = trie;
1816   state->allocated = FALSE;
1817   state->vflags = root == &trie->root ? TN_PRIMARY : TN_SECONDARY;
1818   initBuffer(&state->choicepoints);
1819 }
1820 
1821 
1822 #define base_choice(state) baseBuffer(&state->choicepoints, trie_choice)
1823 #define top_choice(state) topBuffer(&state->choicepoints, trie_choice)
1824 
1825 
1826 static void
clear_trie_state(trie_gen_state * state)1827 clear_trie_state(trie_gen_state *state)
1828 { trie_choice *chp = base_choice(state);
1829   trie_choice *top = top_choice(state);
1830 
1831   for(; chp < top; chp++)
1832   { if ( chp->table_enum )
1833       freeTableEnum(chp->table_enum);
1834   }
1835 
1836   discardBuffer(&state->choicepoints);
1837 
1838   release_trie(state->trie);
1839 
1840   if ( state->allocated )
1841     freeForeignState(state, sizeof(*state));
1842 }
1843 
1844 
1845 static void
clear_descent_state(descent_state * dstate)1846 clear_descent_state(descent_state *dstate)
1847 { if ( dstate->compound )
1848     clearSegStack(&dstate->stack);
1849 }
1850 
1851 static int
get_key(trie_gen_state * state,descent_state * dstate,word * key ARG_LD)1852 get_key(trie_gen_state *state, descent_state *dstate, word *key ARG_LD)
1853 { Word p;
1854 
1855   if ( dstate->size == 0 )
1856   { *key = TRIE_KEY_POP(0);
1857     return TRUE;
1858   }
1859 
1860   deRef2(dstate->term, p);
1861   DEBUG(CHK_SECURE, checkData(p));
1862 
1863   if ( canBind(*p) )
1864   { return FALSE;
1865   } else if ( isTerm(*p) )
1866   { Functor f = valueTerm(*p);
1867     desc_tstate dts;
1868 
1869     DEBUG(MSG_TRIE_GEN,
1870 	  Sdprintf("get_key() for %s\n", functorName(f->definition)));
1871 
1872     *key = f->definition;
1873     if ( dstate->size > 1 )
1874     { if ( !dstate->compound )
1875       { dstate->compound = TRUE;
1876 	initSegStack(&dstate->stack, sizeof(desc_tstate),
1877 		     sizeof(dstate->buffer), dstate->buffer);
1878       }
1879       dts.term = dstate->term+1;
1880       dts.size = dstate->size-1;
1881       if ( !pushSegStack(&dstate->stack, dts, desc_tstate) )
1882 	outOfCore();
1883       DEBUG(MSG_TRIE_GEN,
1884 	    Sdprintf("Pushed %p, size %zd\n", dts.term, dts.size));
1885     }
1886     dstate->term = &f->arguments[0];
1887     dstate->size = arityFunctor(f->definition);
1888     return TRUE;
1889   } else
1890   { dstate->term++;
1891     dstate->size--;
1892 
1893     if ( isIndirect(*p) )
1894     { *key = trie_intern_indirect(state->trie, *p, FALSE PASS_LD);
1895       return *key != 0;
1896     } else
1897     { *key = *p;
1898       return TRUE;
1899     }
1900   }
1901 }
1902 
1903 
1904 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1905 Walk a step down the trie, adding  a   node  to the choice stack. If the
1906 term we are walking is instantiated and   the trie node does not contain
1907 variables we walk deterministically. Once we have a variable in the term
1908 we unify against or find a variable in  the trie dstate->prune is set to
1909 FALSE, indicating we must create a real choice.
1910 
1911 If a known input value is matched against   a  trie choice node and this
1912 node contains variables we create a choice   from the value and variable
1913 mask such that  we  perform  a  couple   of  hash  lookups  rather  than
1914 enumerating the entire table.
1915 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1916 
1917 trie_choice *
add_choice(trie_gen_state * state,descent_state * dstate,trie_node * node ARG_LD)1918 add_choice(trie_gen_state *state, descent_state *dstate, trie_node *node ARG_LD)
1919 { trie_children children = node->children;
1920   trie_choice *ch;
1921   int has_key;
1922   word k=0;
1923 
1924   if ( dstate->prune )
1925   { DEBUG(MSG_TRIE_GEN,
1926 	  if ( dstate->size > 0 )
1927 	  { Word p;
1928 	    deRef2(dstate->term, p);
1929 	    Sdprintf("add_choice() for %s\n", print_val(*p, NULL));
1930 	  });
1931     if ( !(has_key = get_key(state, dstate, &k PASS_LD)) )
1932       dstate->prune = FALSE;
1933   } else
1934     has_key = FALSE;
1935 
1936   if ( children.any && false(node, state->vflags) )
1937   { switch( children.any->type )
1938     { case TN_KEY:
1939 	if ( !has_key ||
1940 	     k == children.key->key ||
1941 	     tagex(children.key->key) == TAG_VAR ||
1942 	     IS_TRIE_KEY_POP(children.key->key) )
1943 	{ word key = children.key->key;
1944 
1945 	  if ( tagex(children.key->key) == TAG_VAR )
1946 	    dstate->prune = FALSE;
1947 
1948 	  ch = allocFromBuffer(&state->choicepoints, sizeof(*ch));
1949 	  ch->key        = key;
1950 	  ch->child      = children.key->child;
1951 	  ch->table_enum = NULL;
1952 	  ch->table      = NULL;
1953 
1954 	  if ( IS_TRIE_KEY_POP(children.key->key) && dstate->compound )
1955 	  { desc_tstate dts;
1956 	    popSegStack(&dstate->stack, &dts, desc_tstate);
1957 	    dstate->term = dts.term;
1958 	    dstate->size = dts.size;
1959 	    DEBUG(MSG_TRIE_GEN,
1960 		  Sdprintf("Popped %p, left %zd\n", dstate->term, dstate->size));
1961 	  }
1962 	  break;
1963 	} else
1964 	{ DEBUG(MSG_TRIE_GEN, Sdprintf("Failed\n"));
1965 	  return NULL;
1966 	}
1967       case TN_HASHED:
1968       { void *tk, *tv;
1969 
1970 	if ( has_key )
1971 	{ if ( children.hash->var_mask == 0 )
1972 	  { trie_node *child;
1973 
1974 	    if ( (child = lookupHTable(children.hash->table, (void*)k)) )
1975 	    { ch = allocFromBuffer(&state->choicepoints, sizeof(*ch));
1976 	      ch->key        = k;
1977 	      ch->child	     = child;
1978 	      ch->table_enum = NULL;
1979 	      ch->table      = NULL;
1980 
1981 	      return ch;
1982 	    } else
1983 	      return NULL;
1984 	  } else if ( children.hash->var_mask != VMASK_SCAN )
1985 	  { dstate->prune = FALSE;
1986 
1987 	    DEBUG(MSG_TRIE_GEN,
1988 		  Sdprintf("Created var choice 0x%x\n", children.hash->var_mask));
1989 
1990 	    ch = allocFromBuffer(&state->choicepoints, sizeof(*ch));
1991 	    ch->table_enum = NULL;
1992 	    ch->table      = children.hash->table;
1993 	    ch->var_mask   = children.hash->var_mask;
1994 	    ch->var_index  = 1;
1995 	    ch->novar      = k;
1996 	    if ( advance_node(ch PASS_LD) )
1997 	    { return ch;
1998 	    } else
1999 	    { state->choicepoints.top = (char*)ch;
2000 	      ch--;
2001 	      return NULL;
2002 	    }
2003 	  }
2004 	}
2005 					/* general enumeration */
2006 	dstate->prune = FALSE;
2007 	ch = allocFromBuffer(&state->choicepoints, sizeof(*ch));
2008 	ch->table = NULL;
2009 	ch->table_enum = newTableEnum(children.hash->table);
2010 	advanceTableEnum(ch->table_enum, &tk, &tv);
2011 	ch->key   = (word)tk;
2012 	ch->child = (trie_node*)tv;
2013 	break;
2014       }
2015       default:
2016 	assert(0);
2017         return NULL;
2018     }
2019   } else
2020   { ch = allocFromBuffer(&state->choicepoints, sizeof(*ch));
2021     memset(ch, 0, sizeof(*ch));
2022     ch->child = node;
2023   }
2024 
2025   return ch;
2026 }
2027 
2028 
2029 static trie_choice *
descent_node(trie_gen_state * state,descent_state * dstate,trie_choice * ch ARG_LD)2030 descent_node(trie_gen_state *state, descent_state *dstate, trie_choice *ch ARG_LD)
2031 { while( ch && ch->child->children.any &&
2032 	 false(ch->child, state->vflags) )
2033   { ch = add_choice(state, dstate, ch->child PASS_LD);
2034   }
2035 
2036   return ch;
2037 }
2038 
2039 
2040 static int
advance_node(trie_choice * ch ARG_LD)2041 advance_node(trie_choice *ch ARG_LD)
2042 { if ( ch->table_enum )
2043   { void *k, *v;
2044 
2045     if ( advanceTableEnum(ch->table_enum, &k, &v) )
2046     { ch->key   = (word)k;
2047       ch->child = (trie_node*)v;
2048 
2049       return TRUE;
2050     }
2051   } else if ( ch->table )
2052   { if ( ch->novar )
2053     { if ( (ch->child=lookupHTable(ch->table, (void*)ch->novar)) )
2054       { ch->key = ch->novar;
2055 	ch->novar = 0;
2056 	return TRUE;
2057       }
2058     }
2059     for( ; ch->var_index && ch->var_index < VMASKBITS; ch->var_index++ )
2060     { if ( (ch->var_mask & (0x1<<(ch->var_index-1))) )
2061       { word key = ((((word)ch->var_index))<<LMASK_BITS)|TAG_VAR;
2062 
2063 	if ( (ch->child=lookupHTable(ch->table, (void*)key)) )
2064 	{ ch->key = key;
2065 	  ch->var_index++;
2066 	  return TRUE;
2067 	}
2068       }
2069     }
2070   }
2071 
2072   return FALSE;
2073 }
2074 
2075 
2076 static trie_choice *
next_choice0(trie_gen_state * state,descent_state * dstate ARG_LD)2077 next_choice0(trie_gen_state *state, descent_state *dstate ARG_LD)
2078 { trie_choice *btm = base_choice(state);
2079   trie_choice  *ch = top_choice(state)-1;
2080 
2081   while(ch >= btm)
2082   { if ( advance_node(ch PASS_LD) )
2083       return descent_node(state, dstate, ch PASS_LD);
2084 
2085     if ( ch->table_enum )
2086       freeTableEnum(ch->table_enum);
2087 
2088     state->choicepoints.top = (char*)ch;
2089     ch--;
2090   }
2091 
2092   return NULL;
2093 }
2094 
2095 
2096 static trie_choice *
next_choice(trie_gen_state * state ARG_LD)2097 next_choice(trie_gen_state *state ARG_LD)
2098 { trie_choice *ch;
2099   descent_state dstate;
2100 
2101   dstate.prune    = FALSE;
2102   dstate.compound = FALSE;
2103 
2104   do
2105   { ch = next_choice0(state, &dstate PASS_LD);
2106   } while (ch && false(ch->child, state->vflags));
2107 
2108   return ch;
2109 }
2110 
2111 
2112 static trie_node *
gen_state_leaf(trie_gen_state * state)2113 gen_state_leaf(trie_gen_state *state)
2114 { trie_choice *top = top_choice(state);
2115 
2116   return top[-1].child;
2117 }
2118 
2119 
2120 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2121 Unify term with the term represented a trie path (list of trie_choice).
2122 Returns one of TRUE, FALSE or *_OVERFLOW.
2123 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2124 
2125 static int
unify_trie_path(term_t term,trie_node ** tn,trie_gen_state * gstate ARG_LD)2126 unify_trie_path(term_t term, trie_node **tn, trie_gen_state *gstate ARG_LD)
2127 { ukey_state ustate;
2128   trie_choice *ch = base_choice(gstate);
2129   trie_choice *top = top_choice(gstate);
2130 
2131   init_ukey_state(&ustate, gstate->trie, valTermRef(term) PASS_LD);
2132   for( ; ch < top; ch++ )
2133   { int rc;
2134 
2135     if ( (rc=unify_key(&ustate, ch->key PASS_LD)) != TRUE )
2136     { destroy_ukey_state(&ustate PASS_LD);
2137       return rc;
2138     }
2139   }
2140 
2141   destroy_ukey_state(&ustate PASS_LD);
2142   *tn = ch[-1].child;
2143 
2144   return TRUE;
2145 }
2146 
2147 
2148 foreign_t
trie_gen_raw(trie * trie,trie_node * root,term_t Key,term_t Value,term_t Data,int (* unify_data)(term_t,trie_node *,void * ctx ARG_LD),void * ctx,control_t PL__ctx)2149 trie_gen_raw(trie *trie, trie_node *root, term_t Key, term_t Value,
2150 	     term_t Data, int (*unify_data)(term_t, trie_node*, void *ctx ARG_LD),
2151 	     void *ctx, control_t PL__ctx)
2152 { PRED_LD
2153   trie_gen_state state_buf;
2154   trie_gen_state *state;
2155   trie_node *n;
2156 
2157   switch( CTX_CNTRL )
2158   { case FRG_FIRST_CALL:
2159     { trie_choice *ch;
2160       descent_state dstate;
2161       int rc;
2162 
2163       TRIE_STAT_INC(trie, gen_call);
2164 
2165       dstate.term     = valTermRef(Key);
2166       dstate.size     = 1;
2167       dstate.compound = FALSE;
2168       dstate.prune	  = TRUE;
2169       deRef(dstate.term);
2170 
2171       acquire_trie(trie);
2172       state = &state_buf;
2173       init_trie_state(state, trie, root);
2174       rc = ( (ch = add_choice(state, &dstate, root PASS_LD)) &&
2175 	     (ch = descent_node(state, &dstate, ch PASS_LD)) &&
2176 	     (true(ch->child, state->vflags) || next_choice(state PASS_LD)) );
2177       clear_descent_state(&dstate);
2178       if ( !rc )
2179       { clear_trie_state(state);
2180 	return FALSE;
2181       }
2182       break;
2183     }
2184     case FRG_REDO:
2185       state = CTX_PTR;
2186       if ( true(gen_state_leaf(state), state->vflags) ||
2187 	   next_choice(state PASS_LD) )		/* pending choice was deleted */
2188       { break;
2189       } else
2190       { clear_trie_state(state);
2191 	return FALSE;
2192       }
2193     case FRG_CUTTED:
2194       state = CTX_PTR;
2195       clear_trie_state(state);
2196       return TRUE;
2197     default:
2198       assert(0);
2199       return FALSE;
2200   }
2201 
2202   Mark(fli_context->mark);
2203   for( ; !isEmptyBuffer(&state->choicepoints); next_choice(state PASS_LD) )
2204   { for(;;)
2205     { int rc;
2206       size_t asize = aTop - aBase; /* using the argument stack may be dubious */
2207 
2208       if ( (rc=unify_trie_path(Key, &n, state PASS_LD)) == TRUE )
2209 	break;
2210 
2211       aTop = aBase+asize;
2212       Undo(fli_context->mark);
2213       if ( rc == FALSE )
2214 	goto next;
2215 
2216       if ( makeMoreStackSpace(rc, ALLOW_GC|ALLOW_SHIFT) )
2217 	continue;
2218 
2219       clear_trie_state(state);
2220       return FALSE;				/* resource error */
2221     }
2222 
2223     DEBUG(CHK_SECURE, PL_check_data(Key));
2224 
2225     if ( (!Value || unify_value(Value, n->value PASS_LD)) &&
2226 	 (!Data  || unify_data(Data, n, ctx PASS_LD)) )
2227     { if ( next_choice(state PASS_LD) )
2228       { if ( !state->allocated )
2229 	{ trie_gen_state *nstate = allocForeignState(sizeof(*state));
2230 	  TmpBuffer nchp = &nstate->choicepoints;
2231 	  TmpBuffer ochp = &state->choicepoints;
2232 
2233 	  nstate->trie = state->trie;
2234 	  nstate->vflags = state->vflags;
2235 	  nstate->allocated = TRUE;
2236 	  if ( ochp->base == ochp->static_buffer )
2237 	  { size_t bytes = ochp->top - ochp->base;
2238 	    initBuffer(nchp);
2239 	    nchp->top  = nchp->base + bytes;
2240 	    memcpy(nchp->base, ochp->base, bytes);
2241 	  } else
2242 	  { nchp->base = ochp->base;
2243 	    nchp->top  = ochp->top;
2244 	    nchp->max  = ochp->max;
2245 	  }
2246 
2247 	  state = nstate;
2248 	}
2249 	ForeignRedoPtr(state);
2250       } else
2251       { clear_trie_state(state);
2252 	return TRUE;
2253       }
2254     } else
2255     { Undo(fli_context->mark);
2256     }
2257 
2258 next:;
2259   }
2260 
2261   clear_trie_state(state);
2262   return FALSE;
2263 }
2264 
2265 
2266 foreign_t
trie_gen(term_t Trie,term_t Root,term_t Key,term_t Value,term_t Data,int (* unify_data)(term_t,trie_node *,void * ctx ARG_LD),void * ctx,control_t PL__ctx)2267 trie_gen(term_t Trie, term_t Root, term_t Key, term_t Value,
2268 	 term_t Data, int (*unify_data)(term_t, trie_node*, void *ctx ARG_LD),
2269 	 void *ctx, control_t PL__ctx)
2270 { if ( CTX_CNTRL == FRG_FIRST_CALL )
2271   { trie *trie;
2272     trie_node *root;
2273 
2274     if ( get_trie(Trie, &trie) )
2275     { if ( Root )
2276       { void *ptr;
2277 
2278 	if ( !PL_get_pointer_ex(Root, &ptr) )
2279 	  return FALSE;
2280 	root = ptr;
2281       } else
2282       { root = &trie->root;
2283       }
2284 
2285       if ( root->children.any )
2286 	return trie_gen_raw(trie, root, Key, Value, Data,
2287 			    unify_data, ctx, PL__ctx);
2288     }
2289 
2290     return FALSE;
2291   } else
2292   { return trie_gen_raw(NULL, NULL, Key, Value, Data,
2293 			unify_data, ctx, PL__ctx);
2294   }
2295 }
2296 
2297 static
2298 PRED_IMPL("trie_gen", 3, trie_gen, PL_FA_NONDETERMINISTIC)
2299 { return trie_gen(A1, 0, A2, A3, 0, NULL, NULL, PL__ctx);
2300 }
2301 
2302 static
2303 PRED_IMPL("trie_gen", 2, trie_gen, PL_FA_NONDETERMINISTIC)
2304 { return trie_gen(A1, 0, A2, 0, 0, NULL, NULL, PL__ctx);
2305 }
2306 
2307 static int
unify_node_id(term_t t,trie_node * answer,void * ctx ARG_LD)2308 unify_node_id(term_t t, trie_node *answer, void *ctx ARG_LD)
2309 { (void) ctx;
2310 
2311   return PL_unify_pointer(t, answer);
2312 }
2313 
2314 static
2315 PRED_IMPL("$trie_gen_node", 3, trie_gen_node, PL_FA_NONDETERMINISTIC)
2316 { return trie_gen(A1, 0, A2, 0, A3, unify_node_id, NULL, PL__ctx);
2317 }
2318 
2319 
2320 
2321 static
2322 PRED_IMPL("$trie_property", 2, trie_property, 0)
2323 { PRED_LD
2324   trie *trie;
2325 
2326 #ifdef O_TRIE_STATS
2327   static atom_t ATOM_lookup_count = 0;
2328   static atom_t ATOM_gen_call_count = 0;
2329   static atom_t ATOM_invalidated = 0;
2330   static atom_t ATOM_reevaluated = 0;
2331 
2332   if ( !ATOM_lookup_count )
2333   { ATOM_lookup_count   = PL_new_atom("lookup_count");
2334     ATOM_gen_call_count = PL_new_atom("gen_call_count");
2335     ATOM_invalidated    = PL_new_atom("invalidated");
2336     ATOM_reevaluated    = PL_new_atom("reevaluated");
2337   }
2338 #endif
2339 
2340   if ( get_trie(A1, &trie) )
2341   { atom_t name; size_t arity;
2342     idg_node *idg;
2343 
2344     if ( PL_get_name_arity(A2, &name, &arity) && arity == 1 )
2345     { term_t arg = PL_new_term_ref();
2346 
2347       _PL_get_arg(1, A2, arg);
2348 
2349       if ( name == ATOM_node_count )
2350       { return PL_unify_integer(arg, trie->node_count);
2351       } else if ( name == ATOM_value_count )
2352       { return PL_unify_integer(arg, trie->value_count);
2353       } else if ( name == ATOM_size )
2354       { trie_stats stats;
2355 	stat_trie(trie, &stats);
2356 	if ( stats.nodes != trie->node_count )
2357 	  Sdprintf("OOPS: trie_property/2: counted %zd nodes, admin says %zd\n",
2358 		   stats.nodes, trie->node_count);
2359 	if ( stats.values != trie->value_count )
2360 	  Sdprintf("OOPS: trie_property/2: counted %zd values, admin says %zd\n",
2361 		   stats.values, trie->value_count);
2362 	// assert(stats.nodes  == trie->node_count);
2363 	// assert(stats.values == trie->value_count);
2364 	return PL_unify_int64(arg, stats.bytes);
2365       } else if ( name == ATOM_compiled_size )
2366       { atom_t dbref;
2367 	if ( (dbref = trie->clause) )
2368 	{ ClauseRef cref = clause_clref(dbref);
2369 	  if ( cref )
2370 	  { size_t sz = sizeofClause(cref->value.clause->code_size);
2371 	    return PL_unify_int64(arg, sz);
2372 	  }
2373 	}
2374 	return FALSE;
2375       } else if ( name == ATOM_hashed )
2376       { trie_stats stats;
2377 	stat_trie(trie, &stats);
2378 	return PL_unify_int64(arg, stats.hashes);
2379 #ifdef O_TRIE_STATS
2380       } else if ( name == ATOM_lookup_count )
2381       { return PL_unify_int64(arg, trie->stats.lookups);
2382       } else if ( name == ATOM_gen_call_count)
2383       { return PL_unify_int64(arg, trie->stats.gen_call);
2384 #ifdef O_PLMT
2385       } else if ( name == ATOM_wait )
2386       { return PL_unify_int64(arg, trie->stats.wait);
2387       } else if ( name == ATOM_deadlock )
2388       { return PL_unify_int64(arg, trie->stats.deadlock);
2389 #endif
2390       } else if ( name == ATOM_invalidated && (idg=trie->data.IDG))
2391       { return PL_unify_int64(arg, idg->stats.invalidated);
2392       } else if ( name == ATOM_reevaluated && (idg=trie->data.IDG))
2393       { return PL_unify_int64(arg, idg->stats.reevaluated);
2394 #endif
2395       } else if ( (idg=trie->data.IDG) )
2396       { if ( name == ATOM_idg_affected_count )
2397 	{ return PL_unify_int64(arg, idg->affected ? idg->affected->size : 0);
2398 	} else if ( name == ATOM_idg_dependent_count )
2399 	{ return PL_unify_int64(arg, idg->dependent ? idg->dependent->size : 0);
2400 	} else if ( name == ATOM_idg_size )
2401 	{ size_t size = sizeof(*idg);
2402 
2403 	  if ( idg->affected )  size += sizeofTable(idg->affected);
2404 	  if ( idg->dependent ) size += sizeofTable(idg->dependent);
2405 
2406 	  return PL_unify_int64(arg, size);
2407 	}
2408       }
2409     }
2410   }
2411 
2412   return FALSE;
2413 }
2414 
2415 #ifdef O_NESTED_TRIES
2416 
2417 		 /*******************************
2418 		 *	 HIERARCHICAL TRIES	*
2419 		 *******************************/
2420 
2421 /** trie_insert_insert(+Trie, +Index, +Value)
2422 */
2423 
2424 static
2425 PRED_IMPL("trie_insert_insert", 3, trie_insert_insert, 0)
2426 { PRED_LD
2427   trie *trie;
2428 
2429   if ( get_trie(A1, &trie) )
2430   { Word kp, vp;
2431     trie_node *root, *node;
2432     tmp_buffer vars;
2433     int rc;
2434 
2435     initBuffer(&vars);
2436     kp	= valTermRef(A2);
2437     vp	= valTermRef(A3);
2438 
2439     rc = trie_lookup(trie, NULL, &root, kp, TRUE, &vars PASS_LD);
2440     if ( rc == TRUE )
2441     { rc = trie_lookup(trie, root, &node, vp, TRUE, &vars PASS_LD);
2442 
2443       if ( rc == TRUE )
2444       { set(root, TN_PRIMARY);
2445 	root->value = ATOM_trienode;
2446 	set(node, TN_SECONDARY);
2447 	node->value = ATOM_trienode;
2448       } else
2449       { rc = trie_error(rc, A1);
2450       }
2451     } else
2452     { rc = trie_error(rc, A1);
2453     }
2454 
2455     discardBuffer(&vars);
2456     return rc;
2457   }
2458 
2459   return FALSE;
2460 }
2461 
2462 
2463 static
2464 PRED_IMPL("trie_lookup_gen", 3, trie_lookup_gen, PL_FA_NONDETERMINISTIC)
2465 { PRED_LD
2466   trie *trie;
2467 
2468   if ( get_trie(A1, &trie) )
2469   { Word kp;
2470     trie_node *root;
2471     int rc;
2472 
2473     kp = valTermRef(A2);
2474     rc = trie_lookup(trie, NULL, &root, kp, FALSE, NULL PASS_LD);
2475     if ( rc == TRUE )
2476     { return trie_gen_raw(trie, root, A3, 0, 0, NULL, NULL, PL__ctx);
2477     } else
2478     { rc = trie_error(rc, A1);
2479     }
2480 
2481     return rc;
2482   }
2483 
2484   return FALSE;
2485 }
2486 
2487 
2488 static
2489 PRED_IMPL("trie_lookup_delete", 3, trie_lookup_delete, 0)
2490 { PRED_LD
2491 
2492   trie *trie;
2493 
2494   if ( get_trie(A1, &trie) )
2495   { Word kp;
2496     trie_node *root;
2497     int rc;
2498 
2499     kp = valTermRef(A2);
2500     rc = trie_lookup(trie, NULL, &root, kp, FALSE, NULL PASS_LD);
2501     if ( rc == TRUE )
2502     { Word vp = valTermRef(A3);
2503       trie_node *node;
2504 
2505       rc = trie_lookup(trie, root, &node, vp, FALSE, NULL PASS_LD);
2506       if ( rc == TRUE )
2507       { trie_delete(trie, node, TRUE);
2508       } else
2509       { rc = trie_error(rc, A1);
2510       }
2511     } else
2512     { rc = trie_error(rc, A1);
2513     }
2514 
2515     return rc;
2516   }
2517 
2518   return FALSE;
2519 }
2520 
2521 #endif /*O_NESTED_TRIES*/
2522 
2523 
2524 		 /*******************************
2525 		 *	  COMPILED TRIES	*
2526 		 *******************************/
2527 
2528 typedef struct trie_compile_state
2529 { trie	       *trie;				/* Trie we are working on */
2530   int		try;				/* There are alternatives */
2531   tmp_buffer	codes;				/* Output instructions */
2532   size_t	else_loc;			/* last else */
2533   size_t	maxvar;				/* Highest var index */
2534 } trie_compile_state;
2535 
2536 static void
init_trie_compile_state(trie_compile_state * state,trie * trie)2537 init_trie_compile_state(trie_compile_state *state, trie *trie)
2538 { memset(state, 0, sizeof(*state));
2539   state->trie = trie;
2540   initBuffer(&state->codes);
2541   state->maxvar = 0;
2542 }
2543 
2544 static void
clean_trie_compile_state(trie_compile_state * state)2545 clean_trie_compile_state(trie_compile_state *state)
2546 { discardBuffer(&state->codes);
2547 }
2548 
2549 
2550 static void
add_vmi(trie_compile_state * state,vmi c)2551 add_vmi(trie_compile_state *state, vmi c)
2552 { addBuffer(&state->codes, encode(c), code);
2553 }
2554 
2555 static void
add_vmi_d(trie_compile_state * state,vmi c,code d)2556 add_vmi_d(trie_compile_state *state, vmi c, code d)
2557 { addBuffer(&state->codes, encode(c), code);
2558   addBuffer(&state->codes, d, code);
2559 }
2560 
2561 static void
add_vmi_else_d(trie_compile_state * state,vmi c,code d)2562 add_vmi_else_d(trie_compile_state *state, vmi c, code d)
2563 { size_t el;
2564 
2565   addBuffer(&state->codes, encode(c), code);
2566   el = entriesBuffer(&state->codes, code);
2567   addBuffer(&state->codes, (code)state->else_loc, code);
2568   state->else_loc = el;
2569   addBuffer(&state->codes, d, code);
2570 }
2571 
2572 static void
fixup_else(trie_compile_state * state)2573 fixup_else(trie_compile_state *state)
2574 { Code base = baseBuffer(&state->codes, code);
2575   size_t pc = entriesBuffer(&state->codes, code);
2576   size_t el = state->else_loc;
2577 
2578   state->else_loc = base[el];
2579   base[el] = pc-el-1;
2580 }
2581 
2582 
2583 static int
compile_trie_value(Word v,trie_compile_state * state ARG_LD)2584 compile_trie_value(Word v, trie_compile_state *state ARG_LD)
2585 { term_agenda_P agenda;
2586   size_t var_number = 0;
2587   tmp_buffer varb;
2588   int rc = TRUE;
2589   int compounds = 0;
2590   Word p;
2591 
2592   initTermAgenda_P(&agenda, 1, v);
2593   while( (p=nextTermAgenda_P(&agenda)) )
2594   { size_t popn;
2595 
2596     if ( (popn = IS_AC_TERM_POP(p)) )
2597     { if ( popn == 1 )
2598 	add_vmi(state, T_POP);
2599       else
2600 	add_vmi_d(state, T_POPN, (code)popn);
2601     } else
2602     { word w = *p;
2603 
2604       switch( tag(w) )
2605       { case TAG_VAR:
2606 	{ size_t index;
2607 
2608 	  if ( isVar(w) )
2609 	  { if ( var_number++ == 0 )
2610 	    { initBuffer(&varb);
2611 	    }
2612 	    addBuffer(&varb, p, Word);
2613 	    *p = w = ((((word)var_number))<<LMASK_BITS)|TAG_VAR;
2614 	  }
2615 	  index = (size_t)(w>>LMASK_BITS);
2616 	  if ( index > state->maxvar )
2617 	    state->maxvar = index;
2618 	  add_vmi_d(state, T_VAR, (code)index);
2619 	  break;
2620 	}
2621 	case TAG_ATTVAR:
2622 	  rc = TRIE_LOOKUP_CONTAINS_ATTVAR;
2623 	  goto out;
2624 	case TAG_ATOM:			/* TBD: register */
2625 	  add_vmi_d(state, T_ATOM, (code)w);
2626 	  break;
2627 	case TAG_INTEGER:
2628 	  if ( storage(w) == STG_INLINE)
2629 	  { add_vmi_d(state, T_SMALLINT, (code)w);
2630 	  } else
2631 	  { size_t wsize = wsizeofIndirect(w);
2632 	    Word ip = valIndirectP(w);
2633 
2634 	    if ( wsize == sizeof(int64_t)/sizeof(word))
2635 	    {
2636 #if SIZEOF_VOIDP == 8
2637 	      add_vmi_d(state, T_INTEGER, (code)ip[0]);
2638 #else
2639 	      add_vmi_d(state, T_INT64, (code)ip[0]);
2640 	      addBuffer(&state->codes, (code)ip[1], code);
2641 #endif
2642 #ifdef O_GMP
2643 	    } else
2644 	    { add_vmi_d(state, T_MPZ, (code)ip[-1]);
2645 	      addMultipleBuffer(&state->codes, ip, wsize, code);
2646 #endif
2647 	    }
2648 	  }
2649 	  break;
2650 	case TAG_FLOAT:
2651 	{ Word ip = valIndirectP(w);
2652 	  add_vmi_d(state, T_FLOAT, (code)ip[0]);
2653 #if SIZEOF_VOIDP == 4
2654 	  addBuffer(&state->codes, (code)ip[1], code);
2655 #endif
2656           break;
2657 	}
2658 	case TAG_STRING:
2659 	{ size_t wsize = wsizeofIndirect(w);
2660 	  Word ip = valIndirectP(w);
2661 
2662 	  add_vmi_d(state, T_STRING, (code)ip[-1]);
2663 	  addMultipleBuffer(&state->codes, ip, wsize, code);
2664 	  break;
2665 	}
2666 	case TAG_COMPOUND:
2667 	{ Functor f = valueTerm(w);
2668 	  size_t arity = arityFunctor(f->definition);
2669 
2670 	  if ( ++compounds == 1000 && !is_acyclic(p PASS_LD) )
2671 	  { rc = TRIE_LOOKUP_CYCLIC;
2672 	    goto out;
2673 	  }
2674 	  add_vmi_d(state, T_FUNCTOR, (code)f->definition);
2675 	  pushWorkAgenda_P(&agenda, arity, f->arguments);
2676 	  break;
2677 	}
2678       }
2679     }
2680   }
2681 out:
2682   clearTermAgenda_P(&agenda);
2683 
2684   if ( var_number )
2685   { Word *pp = baseBuffer(&varb, Word);
2686     Word *ep = topBuffer(&varb, Word);
2687 
2688     for(; pp < ep; pp++)
2689     { Word vp = *pp;
2690       setVar(*vp);
2691     }
2692     discardBuffer(&varb);
2693   }
2694 
2695   return rc;
2696 }
2697 
2698 
2699 static int
compile_trie_node(trie_node * n,trie_compile_state * state ARG_LD)2700 compile_trie_node(trie_node *n, trie_compile_state *state ARG_LD)
2701 { trie_children children;
2702   word key;
2703   int rc;
2704 
2705 next:
2706   children = n->children;
2707   if ( n == &state->trie->root )
2708     goto children;
2709   key = n->key;
2710 
2711   switch(tagex(key))
2712   { case TAG_VAR|STG_LOCAL:			/* RESERVED_TRIE_VAL */
2713     { size_t popn = IS_TRIE_KEY_POP(key);
2714 
2715       assert(popn);
2716       if ( popn == 1 )
2717 	add_vmi(state, T_POP);
2718       else
2719 	add_vmi_d(state, T_POPN, (code)popn);
2720       break;
2721     }
2722     case TAG_ATOM|STG_GLOBAL:			/* functor */
2723     { if ( state->try )
2724 	add_vmi_else_d(state, T_TRY_FUNCTOR, (code)key);
2725       else
2726 	add_vmi_d(state, T_FUNCTOR, (code)key);
2727       break;
2728     }
2729     case TAG_VAR:
2730     { size_t index = (size_t)(key>>LMASK_BITS);
2731 
2732       if ( index > state->maxvar )
2733 	state->maxvar = index;
2734 
2735       if ( state->try )
2736 	add_vmi_else_d(state, T_TRY_VAR, (code)index);
2737       else
2738 	add_vmi_d(state, T_VAR, (code)index);
2739       break;
2740     }
2741     case STG_GLOBAL|TAG_INTEGER:		/* indirect data */
2742     case STG_GLOBAL|TAG_STRING:
2743     case STG_GLOBAL|TAG_FLOAT:
2744     { size_t index = key>>LMASK_BITS;
2745       int idx = MSB(index);
2746       indirect *h = &state->trie->indirects->array.blocks[idx][index];
2747       size_t wsize = wsizeofInd(h->header);
2748 
2749       switch(tag(key))
2750       { case TAG_INTEGER:
2751 #if SIZEOF_VOIDP == 8
2752 	  if ( wsize == 1 )			/* 64-bit integer */
2753 	  { if ( state->try )
2754 	      add_vmi_else_d(state, T_TRY_INTEGER, (code)h->data[0]);
2755 	    else
2756 	      add_vmi_d(state, T_INTEGER, (code)h->data[0]);
2757 	    goto indirect_done;
2758 	  } else
2759 #else
2760 	  if ( wsize == 2 )			/* 64-bit integer */
2761 	  { if ( state->try )
2762 	      add_vmi_else_d(state, T_TRY_INT64, (code)h->data[0]);
2763 	    else
2764 	      add_vmi_d(state, T_INT64, (code)h->data[0]);
2765 	    addBuffer(&state->codes, (code)h->data[1], code);
2766 	    goto indirect_done;
2767 	  } else
2768 #endif
2769 	  { if ( state->try )
2770 	      add_vmi_else_d(state, T_TRY_MPZ, (code)h->header);
2771 	    else
2772 	      add_vmi_d(state, T_MPZ, (code)h->header);
2773 	  }
2774 	  break;
2775         case TAG_STRING:
2776 	  if ( state->try )
2777 	    add_vmi_else_d(state, T_TRY_STRING, (code)h->header);
2778 	  else
2779 	    add_vmi_d(state, T_STRING, (code)h->header);
2780 	  break;
2781         case TAG_FLOAT:
2782 	  if ( state->try )
2783 	    add_vmi_else_d(state, T_TRY_FLOAT, (code)h->data[0]);
2784 	  else
2785 	    add_vmi_d(state, T_FLOAT, (code)h->data[0]);
2786 #if SIZEOF_VOIDP == 4
2787 	  addBuffer(&state->codes, (code)h->data[1], code);
2788 #endif
2789 	  goto indirect_done;
2790       }
2791 
2792       addMultipleBuffer(&state->codes, h->data, wsize, code);
2793     indirect_done:
2794       break;
2795     }
2796     case TAG_ATOM:
2797     { if ( state->try )
2798 	add_vmi_else_d(state, T_TRY_ATOM, (code)key);
2799       else
2800 	add_vmi_d(state, T_ATOM, (code)key);
2801       break;
2802     }
2803     case TAG_INTEGER:
2804     { if ( state->try )
2805 	add_vmi_else_d(state, T_TRY_SMALLINT, (code)key);
2806       else
2807 	add_vmi_d(state, T_SMALLINT, (code)key);
2808       break;
2809     }
2810     default:
2811       assert(0);
2812   }
2813 
2814 children:
2815   if ( children.any && false(n, TN_PRIMARY|TN_SECONDARY) )
2816   { switch( children.any->type )
2817     { case TN_KEY:
2818       { state->try = FALSE;
2819 	n = children.key->child;
2820 	goto next;
2821       }
2822       case TN_HASHED:
2823       { Table table = children.hash->table;
2824 	TableEnum e = newTableEnum(table);
2825 	void *k, *v;
2826 
2827 	if ( !advanceTableEnum(e, &k, &v) )
2828 	{ freeTableEnum(e);
2829 	  return TRUE;				/* empty path */
2830 	}
2831 
2832 	for(;;)
2833 	{ n = v;
2834 
2835 	  if ( !(state->try = advanceTableEnum(e, &k, &v)) )
2836 	  { freeTableEnum(e);
2837 	    goto next;
2838 	  }
2839 
2840 	  if ( (rc=compile_trie_node(n, state PASS_LD)) != TRUE )
2841 	  { freeTableEnum(e);
2842 	    return rc;
2843 	  }
2844 	  fixup_else(state);
2845 	}
2846       }
2847     }
2848   } else
2849   { if ( n->value )			/* what if we have none? */
2850     { if ( answer_is_conditional(n) )
2851 	add_vmi_d(state, T_DELAY, (code)n);
2852 
2853       if ( true(state->trie, TRIE_ISMAP) )
2854       { add_vmi(state, T_VALUE);
2855 	if ( !isRecord(n->value) )
2856 	{ if ( isAtom(n->value) )
2857 	  { add_vmi_d(state, T_ATOM, (code)n->value);
2858 	  } else
2859 	  { add_vmi_d(state, T_SMALLINT, (code)n->value);
2860 	  }
2861 	} else
2862 	{ term_t t2;
2863 
2864 	  if ( (t2=PL_new_term_ref()) &&
2865 	       PL_recorded((record_t)n->value, t2) )
2866 	  { Word p = valTermRef(t2);
2867 
2868 	    deRef(p);
2869 	    if ( (rc = compile_trie_value(p, state PASS_LD)) != TRUE )
2870 	      return rc;
2871 	  } else
2872 	  { return FALSE;
2873 	  }
2874 	  PL_reset_term_refs(t2);
2875 	}
2876       }
2877       add_vmi(state, I_EXIT);
2878     } else
2879     { add_vmi(state, I_FAIL);
2880       if ( n == &state->trie->root )
2881 	add_vmi(state, I_EXIT);	/* make sure the clause ends with I_EXIT */
2882     }
2883   }
2884 
2885   return TRUE;
2886 }
2887 
2888 
2889 static int
create_trie_clause(Definition def,Clause * cp,trie_compile_state * state)2890 create_trie_clause(Definition def, Clause *cp, trie_compile_state *state)
2891 { size_t code_size = entriesBuffer(&state->codes, code);
2892   size_t size      = sizeofClause(code_size);
2893   //size_t clsize    = size + SIZEOF_CREF_CLAUSE;
2894   Clause cl;
2895 
2896   cl = PL_malloc_atomic(size);
2897   memset(cl, 0, sizeof(*cl));
2898   cl->predicate = def;
2899   cl->code_size = code_size;
2900   cl->prolog_vars = TRIE_VAR_OFFSET + state->maxvar;
2901   cl->variables = cl->prolog_vars;	/* 2: pseudo arity */
2902   set(cl, UNIT_CLAUSE);			/* no body */
2903   memcpy(cl->codes, baseBuffer(&state->codes, code), sizeOfBuffer(&state->codes));
2904   *cp = cl;
2905 
2906   ATOMIC_ADD(&GD->statistics.codes, cl->code_size);
2907   ATOMIC_INC(&GD->statistics.clauses);
2908 
2909   return TRUE;
2910 }
2911 
2912 
2913 atom_t
compile_trie(Definition def,trie * trie ARG_LD)2914 compile_trie(Definition def, trie *trie ARG_LD)
2915 { atom_t dbref;
2916 
2917 retry:
2918   if ( !(dbref = trie->clause) )
2919   { if ( trie->value_count == 0 )
2920     { dbref = ATOM_fail;
2921       if ( !COMPARE_AND_SWAP_WORD(&trie->clause, 0, dbref) )
2922 	goto retry;
2923     } else
2924     { trie_compile_state state;
2925       Clause cl;
2926       ClauseRef cref;
2927 
2928       init_trie_compile_state(&state, trie);
2929       add_vmi(&state, def->functor->arity == 2 ? T_TRIE_GEN2 : T_TRIE_GEN3);
2930       if ( compile_trie_node(&trie->root, &state PASS_LD) &&
2931 	   create_trie_clause(def, &cl, &state) )
2932       { cref = assertDefinition(def, cl, CL_END PASS_LD);
2933 	if ( cref )
2934 	{ dbref = lookup_clref(cref->value.clause);
2935 	  if ( !COMPARE_AND_SWAP_WORD(&trie->clause, 0, dbref) )
2936 	  { PL_unregister_atom(dbref);
2937 	    retractClauseDefinition(def, cref->value.clause);
2938 	    goto retry;
2939 	  }
2940 	}
2941       }
2942       assert(state.else_loc == 0);
2943       clean_trie_compile_state(&state);
2944     }
2945   }
2946 
2947   return dbref;
2948 }
2949 
2950 
2951 static
2952 PRED_IMPL("$trie_compile", 2, trie_compile, 0)
2953 { PRED_LD
2954   trie *trie;
2955 
2956   if ( get_trie(A1, &trie) )
2957   { Procedure proc = (true(trie, TRIE_ISMAP)
2958 			     ? GD->procedures.trie_gen_compiled3
2959 			     : GD->procedures.trie_gen_compiled2);
2960     atom_t clref = compile_trie(proc->definition, trie PASS_LD);
2961 
2962     return _PL_unify_atomic(A2, clref);
2963   }
2964 
2965   return FALSE;
2966 }
2967 
2968 
2969 static void
set_trie_clause_general_undefined(Clause clause)2970 set_trie_clause_general_undefined(Clause clause)
2971 { Code PC, ep;
2972 
2973   PC = clause->codes;
2974   ep = PC + clause->code_size;
2975 
2976   for( ; PC < ep; PC = stepPC(PC) )
2977   { code c = fetchop(PC);
2978 
2979     switch(c)
2980     { case T_DELAY:
2981 	PC[1] = (code)NULL;
2982         break;
2983     }
2984   }
2985 }
2986 
2987 		 /*******************************
2988 		 *      PUBLISH PREDICATES	*
2989 		 *******************************/
2990 
2991 #define NDET PL_FA_NONDETERMINISTIC
2992 
2993 BeginPredDefs(trie)
2994   PRED_DEF("is_trie",		    1, is_trie,		     0)
2995   PRED_DEF("trie_new",		    1, trie_new,	     0)
2996   PRED_DEF("trie_destroy",	    1, trie_destroy,	     0)
2997   PRED_DEF("trie_insert",	    2, trie_insert,	     0)
2998   PRED_DEF("trie_insert",	    3, trie_insert,	     0)
2999   PRED_DEF("trie_insert",	    4, trie_insert,	     0)
3000   PRED_DEF("$trie_insert_abstract", 3, trie_insert_abstract, 0)
3001 
3002   PRED_DEF("trie_update",	    3, trie_update,	     0)
3003   PRED_DEF("trie_lookup",	    3, trie_lookup,	     0)
3004   PRED_DEF("trie_delete",	    3, trie_delete,	     0)
3005   PRED_DEF("trie_term",		    2, trie_term,	     0)
3006   PRED_DEF("trie_gen",		    3, trie_gen,	     NDET)
3007   PRED_DEF("trie_gen",		    2, trie_gen,	     NDET)
3008   PRED_DEF("$trie_gen_node",	    3, trie_gen_node,	     NDET)
3009   PRED_DEF("$trie_property",	    2, trie_property,	     0)
3010 #if O_NESTED_TRIES
3011   PRED_DEF("trie_insert_insert",    3, trie_insert_insert,   0)
3012   PRED_DEF("trie_lookup_gen",       3, trie_lookup_gen,      NDET)
3013   PRED_DEF("trie_lookup_delete",    3, trie_lookup_delete,   0)
3014 #endif
3015   PRED_DEF("$trie_compile",         2, trie_compile,         0)
3016 EndPredDefs
3017 
3018 void
initTries(void)3019 initTries(void)
3020 { Procedure proc;
3021   Definition def;
3022 
3023   PL_register_blob_type(&trie_blob);
3024 
3025   proc = PL_predicate("trie_gen_compiled", 2, "system");
3026   def = proc->definition;
3027   set(def, P_LOCKED_SUPERVISOR|P_VOLATILE);
3028   def->codes = SUPERVISOR(trie_gen);
3029   GD->procedures.trie_gen_compiled2 = proc;
3030 
3031   proc = PL_predicate("trie_gen_compiled", 3, "system");
3032   def = proc->definition;
3033   set(def, P_LOCKED_SUPERVISOR|P_VOLATILE);
3034   def->codes = SUPERVISOR(trie_gen);
3035   GD->procedures.trie_gen_compiled3 = proc;
3036 }
3037