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