1162 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			1162 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*  $Id$
 | ||
|  | 
 | ||
|  |     Part of SWI-Prolog | ||
|  | 
 | ||
|  |     Author:        Jan Wielemaker | ||
|  |     E-mail:        wielemak@science.uva.nl | ||
|  |     WWW:           http://www.swi-prolog.org
 | ||
|  |     Copyright (C): 2006, University of Amsterdam | ||
|  | 
 | ||
|  |     This program is free software; you can redistribute it and/or | ||
|  |     modify it under the terms of the GNU General Public License | ||
|  |     as published by the Free Software Foundation; either version 2 | ||
|  |     of the License, or (at your option) any later version. | ||
|  | 
 | ||
|  |     This program is distributed in the hope that it will be useful, | ||
|  |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
|  |     GNU General Public License for more details. | ||
|  | 
 | ||
|  |     You should have received a copy of the GNU Lesser General Public | ||
|  |     License along with this library; if not, write to the Free Software | ||
|  |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||
|  | */ | ||
|  | 
 | ||
|  | #ifdef HAVE_CONFIG_H
 | ||
|  | #include <config.h>
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #include <SWI-Stream.h>
 | ||
|  | #include <SWI-Prolog.h>
 | ||
|  | #include "avl.h"
 | ||
|  | #include "lock.h"
 | ||
|  | #include "atom.h"
 | ||
|  | #include "debug.h"
 | ||
|  | #include <string.h>
 | ||
|  | #include <assert.h>
 | ||
|  | #ifdef __WINDOWS__
 | ||
|  | #define inline __inline
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||
|  | This file realises the low-level support   for  indexing literals in the | ||
|  | semantic web library. The idea is to   make a map from abstracted tokens | ||
|  | from each literal to  the  exact   literals.  Abstraction  introduces  a | ||
|  | certain amount of ambiguity that  makes   fuzzy  matching possible. Good | ||
|  | abstraction candidates are the Porter Stem  or Snowbal algorithm and the | ||
|  | Double Metaphone algorithm. Both  are  provide   by  the  SWI-Prolog NLP | ||
|  | package. | ||
|  | 
 | ||
|  | Basic query provides a  set  of   abstracted  terms  and  requests those | ||
|  | literals containing all of them. We   maintain  ordered sets of literals | ||
|  | and do set-intersection on them to achieve good linear performance. | ||
|  | 
 | ||
|  | Some current E-culture project statistics (porter stem) | ||
|  | 
 | ||
|  | 	  # stems: 0.4 million
 | ||
|  | 	  # literals: 0.9 million
 | ||
|  | 	  # stem->literal relations: 3.1 million
 | ||
|  | 
 | ||
|  | Av. literals/stem: about 8. | ||
|  | 
 | ||
|  | Searching is done using | ||
|  | 
 | ||
|  | 	rdf_find_literal_map(Map, SetOfAbstract, -Literals) | ||
|  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
|  | 
 | ||
|  | #define AM_MAGIC	0x6ab19e8e
 | ||
|  | 
 | ||
|  | typedef struct atom_map | ||
|  | { long		magic;			/* AM_MAGIC */ | ||
|  |   size_t	value_count;		/* total # values */ | ||
|  |   rwlock	lock;			/* Multi-threaded access */ | ||
|  |   avl_tree	tree;			/* AVL tree */ | ||
|  | } atom_map; | ||
|  | 
 | ||
|  | typedef void *datum; | ||
|  | 
 | ||
|  | #define S_MAGIC 0x8734abcd
 | ||
|  | 
 | ||
|  | typedef struct atom_set | ||
|  | { size_t  size;				/* # cells in use */ | ||
|  |   size_t  allocated;			/* # cells allocated */ | ||
|  |   datum *atoms;			/* allocated cells */ | ||
|  | #ifdef O_SECURE
 | ||
|  |   long	  magic; | ||
|  | #endif
 | ||
|  | } atom_set; | ||
|  | 
 | ||
|  | 
 | ||
|  | #define ND_MAGIC 0x67b49a23
 | ||
|  | #define ND_MAGIC_EX 0x753ab3c
 | ||
|  | 
 | ||
|  | typedef struct node_data | ||
|  | { datum		key; | ||
|  |   atom_set     *values; | ||
|  | #ifdef O_SECURE
 | ||
|  |   long		magic; | ||
|  | #endif
 | ||
|  | } node_data; | ||
|  | 
 | ||
|  | typedef struct node_data_ex | ||
|  | { node_data	data; | ||
|  |   atom_info	atom; | ||
|  | #ifdef O_SECURE
 | ||
|  |   long		magic; | ||
|  | #endif
 | ||
|  | } node_data_ex; | ||
|  | 
 | ||
|  | 
 | ||
|  | #define RDLOCK(map)			rdlock(&map->lock)
 | ||
|  | #define WRLOCK(map, allowreaders)	wrlock(&map->lock, allowreaders)
 | ||
|  | #define LOCKOUT_READERS(map)		lockout_readers(&map->lock)
 | ||
|  | #define REALLOW_READERS(map)		reallow_readers(&map->lock)
 | ||
|  | #define WRUNLOCK(map)			unlock(&map->lock, FALSE)
 | ||
|  | #define RDUNLOCK(map)			unlock(&map->lock, TRUE)
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	     BASIC STUFF	* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | static functor_t FUNCTOR_error2; | ||
|  | static functor_t FUNCTOR_type_error2; | ||
|  | static functor_t FUNCTOR_domain_error2; | ||
|  | static functor_t FUNCTOR_resource_error1; | ||
|  | static functor_t FUNCTOR_atom_map1; | ||
|  | static functor_t FUNCTOR_size2; | ||
|  | static functor_t FUNCTOR_not1; | ||
|  | static atom_t	 ATOM_all; | ||
|  | static atom_t	 ATOM_case; | ||
|  | static atom_t	 ATOM_prefix; | ||
|  | static atom_t	 ATOM_le; | ||
|  | static atom_t	 ATOM_ge; | ||
|  | static atom_t	 ATOM_between; | ||
|  | static atom_t	 ATOM_key; | ||
|  | 
 | ||
|  | #define MKFUNCTOR(n,a) \
 | ||
|  | 	FUNCTOR_ ## n ## a = PL_new_functor(PL_new_atom(#n), a) | ||
|  | #define MKATOM(n) \
 | ||
|  | 	ATOM_ ## n = PL_new_atom(#n) | ||
|  | 
 | ||
|  | static void | ||
|  | init_functors() | ||
|  | { FUNCTOR_atom_map1 = PL_new_functor(PL_new_atom("$literal_map"), 1); | ||
|  | 
 | ||
|  |   MKFUNCTOR(error, 2); | ||
|  |   MKFUNCTOR(type_error, 2); | ||
|  |   MKFUNCTOR(domain_error, 2); | ||
|  |   MKFUNCTOR(resource_error, 1); | ||
|  |   MKFUNCTOR(size, 2); | ||
|  |   MKFUNCTOR(not, 1); | ||
|  | 
 | ||
|  |   MKATOM(all); | ||
|  |   MKATOM(case); | ||
|  |   MKATOM(prefix); | ||
|  |   MKATOM(le); | ||
|  |   MKATOM(ge); | ||
|  |   MKATOM(between); | ||
|  |   MKATOM(key); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | type_error(term_t actual, const char *expected) | ||
|  | { term_t ex; | ||
|  | 
 | ||
|  |   if ( (ex = PL_new_term_ref()) && | ||
|  |        PL_unify_term(ex, | ||
|  | 		     PL_FUNCTOR, FUNCTOR_error2, | ||
|  | 		       PL_FUNCTOR, FUNCTOR_type_error2, | ||
|  | 		         PL_CHARS, expected, | ||
|  | 		         PL_TERM, actual, | ||
|  | 		       PL_VARIABLE) ) | ||
|  |     return PL_raise_exception(ex); | ||
|  | 
 | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | domain_error(term_t actual, const char *expected) | ||
|  | { term_t ex; | ||
|  | 
 | ||
|  |   if ( (ex = PL_new_term_ref()) && | ||
|  |        PL_unify_term(ex, | ||
|  | 		     PL_FUNCTOR, FUNCTOR_error2, | ||
|  | 		       PL_FUNCTOR, FUNCTOR_domain_error2, | ||
|  | 		         PL_CHARS, expected, | ||
|  | 		         PL_TERM, actual, | ||
|  | 		       PL_VARIABLE) ) | ||
|  |     return PL_raise_exception(ex); | ||
|  | 
 | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | resource_error(const char *what) | ||
|  | { term_t ex; | ||
|  | 
 | ||
|  |   if ( (ex = PL_new_term_ref()) && | ||
|  |        PL_unify_term(ex, | ||
|  | 		     PL_FUNCTOR, FUNCTOR_error2, | ||
|  | 		       PL_FUNCTOR, FUNCTOR_resource_error1, | ||
|  | 		         PL_CHARS, what, | ||
|  | 		       PL_VARIABLE) ) | ||
|  |     return PL_raise_exception(ex); | ||
|  | 
 | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | representation_error(const char *what) | ||
|  | { term_t ex = PL_new_term_ref(); | ||
|  | 
 | ||
|  |   if ( (ex = PL_new_term_ref()) && | ||
|  |        PL_unify_term(ex, | ||
|  | 		     PL_FUNCTOR, FUNCTOR_error2, | ||
|  | 		       PL_FUNCTOR_CHARS, "representation_error", 1, | ||
|  | 		         PL_CHARS, what, | ||
|  | 		       PL_VARIABLE) ) | ||
|  |     return PL_raise_exception(ex); | ||
|  | 
 | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | get_atom_ex(term_t t, atom_t *a) | ||
|  | { if ( PL_get_atom(t, a) ) | ||
|  |     return TRUE; | ||
|  | 
 | ||
|  |   return type_error(t, "atom"); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | get_long_ex(term_t t, long *v) | ||
|  | { if ( PL_get_long(t, v) ) | ||
|  |     return TRUE; | ||
|  | 
 | ||
|  |   return type_error(t, "integer"); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | get_atom_map(term_t t, atom_map **map) | ||
|  | { if ( PL_is_functor(t, FUNCTOR_atom_map1) ) | ||
|  |   { term_t a = PL_new_term_ref(); | ||
|  |     void *ptr; | ||
|  | 
 | ||
|  |     _PL_get_arg(1, t, a); | ||
|  |     if ( PL_get_pointer(a, &ptr) ) | ||
|  |     { atom_map *am = ptr; | ||
|  | 
 | ||
|  |       if ( am->magic == AM_MAGIC ) | ||
|  |       { *map = am; | ||
|  |         return TRUE; | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   return type_error(t, "atom_map"); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | unify_atom_map(term_t t, atom_map *map) | ||
|  | { return PL_unify_term(t, PL_FUNCTOR, FUNCTOR_atom_map1, | ||
|  | 		            PL_POINTER, map); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	       DATUM		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||
|  | Datum is either an atom or a 31-bit  signed integer. Atoms are shifted 7 | ||
|  | bits | ||
|  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
|  | 
 | ||
|  | #define ATOM_TAG_BITS 7
 | ||
|  | #define ATOM_TAG 0x1
 | ||
|  | 
 | ||
|  | #define tag(d)		((long)(d)&0x1)
 | ||
|  | #define isAtomDatum(d)  ((long)(d)&ATOM_TAG)
 | ||
|  | #define isIntDatum(d)	!isAtomDatum(d)
 | ||
|  | 
 | ||
|  | #define MAP_MIN_INT	(-(long)(1L<<(sizeof(long)*8 - 1 - 1)))
 | ||
|  | #define MAP_MAX_INT	(-MAP_MIN_INT - 1)
 | ||
|  | 
 | ||
|  | static intptr_t atom_mask; | ||
|  | 
 | ||
|  | static void | ||
|  | init_datum_store() | ||
|  | { atom_t a = PL_new_atom("[]"); | ||
|  | 
 | ||
|  |   atom_mask = a & ((1<<(ATOM_TAG_BITS-1))-1); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static inline atom_t | ||
|  | atom_from_datum(datum d) | ||
|  | { unsigned long v = (unsigned long)d; | ||
|  |   atom_t a; | ||
|  | 
 | ||
|  |   a  = ((v&~0x1)<<(ATOM_TAG_BITS-1))|atom_mask; | ||
|  |   DEBUG(9, Sdprintf("0x%lx --> %s\n", v, PL_atom_chars(a))); | ||
|  |   return a; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static inline long | ||
|  | long_from_datum(datum d) | ||
|  | { long v = (long)d; | ||
|  | 
 | ||
|  |   return (v>>1); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static inline datum | ||
|  | atom_to_datum(atom_t a) | ||
|  | { uintptr_t v = (a>>(ATOM_TAG_BITS-1))|ATOM_TAG; | ||
|  | 
 | ||
|  |   SECURE(assert(atom_from_datum((datum)v) == a)); | ||
|  |   DEBUG(9, Sdprintf("Atom %s --> 0x%lx\n", PL_atom_chars(a), v)); | ||
|  | 
 | ||
|  |   return (datum)v; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static inline datum | ||
|  | long_to_datum(long v) | ||
|  | { return (datum)(v<<1); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | get_datum(term_t t, datum* d) | ||
|  | { atom_t a; | ||
|  |   long l; | ||
|  | 
 | ||
|  |   if ( PL_get_atom(t, &a) ) | ||
|  |   { *d = atom_to_datum(a); | ||
|  |     return TRUE; | ||
|  |   } else if ( PL_get_long(t, &l) ) | ||
|  |   { if ( l < MAP_MIN_INT || l > MAP_MAX_INT ) | ||
|  |       return representation_error("integer_range"); | ||
|  | 
 | ||
|  |     *d = long_to_datum(l); | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   return type_error(t, "atom or integer"); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | get_search_datum(term_t t, node_data_ex *search) | ||
|  | { atom_t a; | ||
|  |   long l; | ||
|  | 
 | ||
|  |   SECURE(search->magic = ND_MAGIC_EX); | ||
|  | 
 | ||
|  |   if ( PL_get_atom(t, &a) ) | ||
|  |   { search->data.key = atom_to_datum(a); | ||
|  |     search->atom.handle   = a; | ||
|  |     search->atom.resolved = FALSE; | ||
|  |     return TRUE; | ||
|  |   } else if ( PL_get_long(t, &l) ) | ||
|  |   { if ( l < MAP_MIN_INT || l > MAP_MAX_INT ) | ||
|  |       return representation_error("integer_range"); | ||
|  | 
 | ||
|  |     search->data.key = long_to_datum(l); | ||
|  |     return TRUE; | ||
|  |   } | ||
|  | 
 | ||
|  |   return type_error(t, "atom or integer"); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | unify_datum(term_t t, datum d) | ||
|  | { unsigned long v = (unsigned long)d; | ||
|  | 
 | ||
|  |   if ( isAtomDatum(v) ) | ||
|  |     return PL_unify_atom(t, atom_from_datum(d)); | ||
|  |   else | ||
|  |     return PL_unify_integer(t, long_from_datum(d)); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void | ||
|  | lock_datum(datum d) | ||
|  | { unsigned long v = (unsigned long)d; | ||
|  | 
 | ||
|  |   if ( isAtomDatum(v) ) | ||
|  |     PL_register_atom(atom_from_datum(d)); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void | ||
|  | unlock_datum(datum d) | ||
|  | { unsigned long v = (unsigned long)d; | ||
|  | 
 | ||
|  |   if ( isAtomDatum(v) ) | ||
|  |     PL_unregister_atom(atom_from_datum(d)); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static const char * | ||
|  | format_datum(datum d, char *buf) | ||
|  | { static char tmp[20]; | ||
|  | 
 | ||
|  |   if ( isAtomDatum(d) ) | ||
|  |     return PL_atom_chars(atom_from_datum(d)); | ||
|  | 
 | ||
|  |   if ( !buf ) | ||
|  |     buf = tmp; | ||
|  |   Ssprintf(buf, "%ld", long_from_datum(d)); | ||
|  | 
 | ||
|  |   return buf; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	     ATOM SETS		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||
|  | A set of atoms (literals) is a   sorted  array of atom-handles. They are | ||
|  | sorted simply by handle as we are  not   interested  in the value in the | ||
|  | actual atom.  Search is implemeted as binary search. | ||
|  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
|  | 
 | ||
|  | #define AS_INITIAL_SIZE 4
 | ||
|  | 
 | ||
|  | static atom_set * | ||
|  | new_atom_set(datum a0) | ||
|  | { atom_set *as; | ||
|  | 
 | ||
|  |   if ( (as = malloc(sizeof(*as))) && | ||
|  |        (as->atoms = malloc(sizeof(datum)*AS_INITIAL_SIZE)) ) | ||
|  |   { lock_datum(a0); | ||
|  |     as->size = 1; | ||
|  |     as->allocated = AS_INITIAL_SIZE; | ||
|  |     as->atoms[0] = a0; | ||
|  |     SECURE(as->magic = S_MAGIC); | ||
|  |   } | ||
|  | 
 | ||
|  |   return as; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||
|  | find_in_atom_set(atom_set *as, datum  a)  returns   a  pointer  to  the | ||
|  | location of the atom or, if the atom  isn't there, to the first location | ||
|  | *after* the atom | ||
|  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
|  | 
 | ||
|  | static datum * | ||
|  | find_in_atom_set(atom_set *as, datum a, int *found) | ||
|  | { const datum *ap = (const datum *)as->atoms; | ||
|  |   const datum *ep = &ap[as->size]; | ||
|  | 
 | ||
|  |   SECURE(assert(as->magic == S_MAGIC)); | ||
|  | 
 | ||
|  |   for(;;) | ||
|  |   { const datum *cp = ap+(ep-ap)/2; | ||
|  | 
 | ||
|  |     if ( a < *cp ) | ||
|  |     { if ( ep == cp ) | ||
|  |       { *found = FALSE; | ||
|  | 	return (datum*)cp; | ||
|  |       } | ||
|  |       ep = cp; | ||
|  |     } else if ( a > *cp ) | ||
|  |     { if ( ap == cp ) | ||
|  |       { cp++; | ||
|  | 	*found = FALSE; | ||
|  | 	return (datum*)cp; | ||
|  |       } | ||
|  |       ap = cp; | ||
|  |     } else | ||
|  |     { *found = TRUE; | ||
|  |       return (datum*)cp; | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | in_atom_set(atom_set *as, datum a) | ||
|  | { int found; | ||
|  | 
 | ||
|  |   find_in_atom_set(as, a, &found); | ||
|  | 
 | ||
|  |   return found; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | #define ptr_diff(p1, p2) ((char *)(p1) - (char *)(p2))
 | ||
|  | 
 | ||
|  | static int | ||
|  | insert_atom_set(atom_set *as, datum a) | ||
|  | { int found; | ||
|  |   datum *ap = find_in_atom_set(as, a, &found); | ||
|  | 
 | ||
|  |   if ( !found ) | ||
|  |   { lock_datum(a); | ||
|  | 
 | ||
|  |     if ( as->size == as->allocated ) | ||
|  |     { datum *na; | ||
|  |       size_t newsize = as->allocated*2; | ||
|  | 
 | ||
|  |       if ( !(na = realloc(as->atoms, sizeof(datum)*newsize)) ) | ||
|  | 	return -1; | ||
|  |       ap += na-as->atoms; | ||
|  |       as->atoms = na; | ||
|  |       as->allocated = newsize; | ||
|  |     } | ||
|  |     assert(as->size < as->allocated); | ||
|  | 
 | ||
|  |     memmove(ap+1, ap, ptr_diff(&as->atoms[as->size], ap)); | ||
|  |     as->size++; | ||
|  |     *ap = a; | ||
|  | 
 | ||
|  |     return 1; | ||
|  |   } | ||
|  | 
 | ||
|  |   return 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | delete_atom_set(atom_set *as, datum a) | ||
|  | { int found; | ||
|  |   datum *ap = find_in_atom_set(as, a, &found); | ||
|  | 
 | ||
|  |   if ( found ) | ||
|  |   { unlock_datum(a); | ||
|  |     as->size--; | ||
|  |     memmove(ap, ap+1, ptr_diff(&as->atoms[as->size], ap)); | ||
|  |   } | ||
|  | 
 | ||
|  |   return found; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void | ||
|  | destroy_atom_set(atom_set *as) | ||
|  | { size_t i; | ||
|  | 
 | ||
|  |   for(i=0; i<as->size; i++) | ||
|  |     unlock_datum(as->atoms[i]); | ||
|  | 
 | ||
|  |   free(as->atoms); | ||
|  |   free(as); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void | ||
|  | free_node_data(void *ptr) | ||
|  | { node_data *data = ptr; | ||
|  | 
 | ||
|  |   DEBUG(2, | ||
|  | 	char b[20]; | ||
|  | 	Sdprintf("Destroying node with key = %s\n", | ||
|  | 		 format_datum(data->key, b))); | ||
|  | 
 | ||
|  |   unlock_datum(data->key); | ||
|  |   destroy_atom_set(data->values); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	   TREE INTERFACE	* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | static int | ||
|  | cmp_node_data(void *l, void *r, NODE type) | ||
|  | { node_data_ex *e1 = l; | ||
|  |   node_data *n2 = r; | ||
|  |   datum *d1 = e1->data.key; | ||
|  |   datum *d2 = n2->key; | ||
|  |   int d; | ||
|  | 
 | ||
|  |   SECURE(assert(e1->magic == ND_MAGIC_EX)); | ||
|  | 
 | ||
|  |   if ( (d=(tag(d1)-tag(d2))) == 0 ) | ||
|  |   { if ( isAtomDatum(d1) ) | ||
|  |     { return cmp_atom_info(&e1->atom, atom_from_datum(d2)); | ||
|  |     } else | ||
|  |     { long l1 = long_from_datum(d1); | ||
|  |       long l2 = long_from_datum(d2); | ||
|  | 
 | ||
|  |       return l1 > l2 ? 1 : l1 < l2 ? -1 : 0; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   return d; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void | ||
|  | init_tree_map(atom_map *m) | ||
|  | { avlinit(&m->tree, | ||
|  | 	  NULL, sizeof(node_data), | ||
|  | 	  cmp_node_data, | ||
|  | 	  free_node_data,		/* destroy */ | ||
|  | 	  NULL,				/* alloc */ | ||
|  | 	  NULL);			/* free */ | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | new_atom_map(term_t handle) | ||
|  | { atom_map *m; | ||
|  | 
 | ||
|  |   if ( !(m=malloc(sizeof(*m))) ) | ||
|  |     return resource_error("memory"); | ||
|  | 
 | ||
|  |   memset(m, 0, sizeof(*m)); | ||
|  |   init_lock(&m->lock); | ||
|  |   init_tree_map(m); | ||
|  |   m->magic = AM_MAGIC; | ||
|  | 
 | ||
|  |   return unify_atom_map(handle, m); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | destroy_atom_map(term_t handle) | ||
|  | { atom_map *m; | ||
|  | 
 | ||
|  |   if ( !get_atom_map(handle, &m) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   WRLOCK(m, FALSE); | ||
|  |   avlfree(&m->tree); | ||
|  |   m->magic = 0; | ||
|  |   WRUNLOCK(m); | ||
|  |   destroy_lock(&m->lock); | ||
|  |   free(m); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	       INSERT		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | insert_atom_map4(term_t handle, term_t from, term_t to, term_t keys) | ||
|  | { atom_map *map; | ||
|  |   datum a2; | ||
|  |   node_data_ex search; | ||
|  |   node_data *data; | ||
|  | 
 | ||
|  |   if ( !get_atom_map(handle, &map) || | ||
|  |        !get_search_datum(from, &search) || | ||
|  |        !get_datum(to, &a2) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( !WRLOCK(map, FALSE) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( (data=avlfind(&map->tree, &search)) ) | ||
|  |   { int rc; | ||
|  | 
 | ||
|  |     SECURE(assert(data->magic == ND_MAGIC)); | ||
|  | 
 | ||
|  |     if ( (rc=insert_atom_set(data->values, a2)) < 0 ) | ||
|  |       return resource_error("memory"); | ||
|  | 
 | ||
|  |     if ( rc ) | ||
|  |       map->value_count++; | ||
|  |   } else | ||
|  |   { if ( keys && !PL_unify_integer(keys, map->tree.count+1) ) | ||
|  |     { WRUNLOCK(map); | ||
|  |       return FALSE; | ||
|  |     } | ||
|  |     if ( !(search.data.values = new_atom_set(a2)) ) | ||
|  |       return resource_error("memory"); | ||
|  |     lock_datum(search.data.key); | ||
|  |     SECURE(search.magic = ND_MAGIC); | ||
|  | 
 | ||
|  |     data = avlins(&map->tree, &search); | ||
|  |     assert(!data); | ||
|  |     map->value_count++; | ||
|  |   } | ||
|  | 
 | ||
|  |   WRUNLOCK(map); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | insert_atom_map3(term_t handle, term_t from, term_t to) | ||
|  | { return insert_atom_map4(handle, from, to, 0); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	       DELETE		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | delete_atom_map2(term_t handle, term_t from) | ||
|  | { atom_map *map; | ||
|  |   node_data_ex search; | ||
|  |   node_data *data; | ||
|  | 
 | ||
|  |   if ( !get_atom_map(handle, &map) || | ||
|  |        !get_search_datum(from, &search) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( !WRLOCK(map, TRUE) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  | 					/* TBD: Single pass? */ | ||
|  |   if ( (data = avlfind(&map->tree, &search)) ) | ||
|  |   { LOCKOUT_READERS(map); | ||
|  |     map->value_count -= data->values->size; | ||
|  |     search.data = *data; | ||
|  |     avldel(&map->tree, &search); | ||
|  |     REALLOW_READERS(map); | ||
|  |   } | ||
|  | 
 | ||
|  |   WRUNLOCK(map); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | delete_atom_map3(term_t handle, term_t from, term_t to) | ||
|  | { atom_map *map; | ||
|  |   node_data_ex search; | ||
|  |   node_data *data; | ||
|  |   datum a2; | ||
|  | 
 | ||
|  |   if ( !get_atom_map(handle, &map) || | ||
|  |        !get_search_datum(from, &search) || | ||
|  |        !get_datum(to, &a2) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( !WRLOCK(map, TRUE) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( (data = avlfind(&map->tree, &search)) && | ||
|  |        in_atom_set(data->values, a2) ) | ||
|  |   { atom_set *as = data->values; | ||
|  | 
 | ||
|  |     LOCKOUT_READERS(map); | ||
|  |     if ( delete_atom_set(as, a2) ) | ||
|  |     { map->value_count--; | ||
|  |       if ( as->size == 0 ) | ||
|  |       { search.data = *data; | ||
|  | 	avldel(&map->tree, &search); | ||
|  |       } | ||
|  |     } | ||
|  |     REALLOW_READERS(map); | ||
|  |   } | ||
|  | 
 | ||
|  |   WRUNLOCK(map); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	      SEARCH		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | typedef struct | ||
|  | { atom_set *set; | ||
|  |   int      neg;				/* not(Lit) */ | ||
|  | } pn_set; | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | cmp_atom_set_size(const void *p1, const void *p2) | ||
|  | { const pn_set *ap1 = p1; | ||
|  |   const pn_set *ap2 = p2; | ||
|  | 
 | ||
|  |   if ( ap1->neg != ap2->neg ) | ||
|  |     return ap1->neg ? 1 : -1;		/* all negatives at the end */ | ||
|  | 
 | ||
|  |   return ap1->set->size == ap2->set->size ? 0 : | ||
|  |          ap1->set->size < ap2->set->size ? -1 : 1; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | #define MAX_SETS 100
 | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | find_atom_map(term_t handle, term_t keys, term_t literals) | ||
|  | { atom_map  *map; | ||
|  |   pn_set    as[MAX_SETS];		/* TBD */ | ||
|  |   int ns = 0; | ||
|  |   term_t tmp = PL_new_term_ref(); | ||
|  |   term_t tail = PL_copy_term_ref(keys); | ||
|  |   term_t head = PL_new_term_ref(); | ||
|  |   atom_set *s0; | ||
|  |   size_t ca; | ||
|  | 
 | ||
|  |   if ( !get_atom_map(handle, &map) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( !RDLOCK(map) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   while(PL_get_list(tail, head, tail)) | ||
|  |   { node_data *data; | ||
|  |     node_data_ex search; | ||
|  |     int neg = FALSE; | ||
|  | 
 | ||
|  |     if ( PL_is_functor(head, FUNCTOR_not1) ) | ||
|  |     { _PL_get_arg(1, head, tmp); | ||
|  |       if ( !get_search_datum(tmp, &search) ) | ||
|  | 	goto failure; | ||
|  |       neg = TRUE; | ||
|  |     } else | ||
|  |     { if ( !get_search_datum(head, &search) ) | ||
|  | 	goto failure; | ||
|  |     } | ||
|  | 
 | ||
|  |     if ( (data = avlfind(&map->tree, &search)) ) | ||
|  |     { if ( ns+1 >= MAX_SETS ) | ||
|  | 	return resource_error("max_search_atoms"); | ||
|  | 
 | ||
|  |       as[ns].set = data->values; | ||
|  |       as[ns].neg = neg; | ||
|  |       DEBUG(2, Sdprintf("Found atom-set of size %d\n", as[ns].set->size)); | ||
|  |       ns++; | ||
|  |     } else if ( !neg ) | ||
|  |     { RDUNLOCK(map);		/* failure on positive literal: empty */ | ||
|  | 
 | ||
|  |       return PL_unify_nil(literals); | ||
|  |     } | ||
|  |   } | ||
|  |   if ( !PL_get_nil(tail) ) | ||
|  |   { type_error(tail, "list"); | ||
|  |     goto failure; | ||
|  |   } | ||
|  | 
 | ||
|  |   qsort(as, ns, sizeof(*as), cmp_atom_set_size); | ||
|  |   if ( ns==0 || as[0].neg ) | ||
|  |   { domain_error(keys, "keywords"); | ||
|  |     goto failure; | ||
|  |   } | ||
|  | 
 | ||
|  |   s0 = as[0].set; | ||
|  | 
 | ||
|  |   PL_put_term(tail, literals); | ||
|  | 
 | ||
|  |   for(ca=0; ca<s0->size; ca++) | ||
|  |   { datum a = s0->atoms[ca]; | ||
|  |     int i; | ||
|  | 
 | ||
|  |     for(i=1; i<ns; i++) | ||
|  |     { if ( !as[i].neg ) | ||
|  |       { if ( !in_atom_set(as[i].set, a) ) | ||
|  | 	{ if ( a > as[i].set->atoms[as[i].set->size-1] ) | ||
|  | 	    goto empty; | ||
|  | 	  goto next; | ||
|  | 	} | ||
|  |       } else | ||
|  |       { if ( in_atom_set(as[i].set, a) ) | ||
|  | 	  goto next; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     if ( !PL_unify_list(tail, head, tail) || | ||
|  | 	 !unify_datum(head, a) ) | ||
|  |       goto failure; | ||
|  | next:; | ||
|  |   } | ||
|  | 
 | ||
|  | empty: | ||
|  |   RDUNLOCK(map); | ||
|  |   return PL_unify_nil(tail); | ||
|  | 
 | ||
|  | failure: | ||
|  |   RDUNLOCK(map); | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||
|  | rdf_keys_in_literal_map(+Map, +Spec, -Keys) | ||
|  | 
 | ||
|  | Spec is one of | ||
|  | 
 | ||
|  | 	* all | ||
|  | 	* prefix(Text)			atoms only | ||
|  | 	* ge(Low)			integers only | ||
|  | 	* le(High) | ||
|  | 	* between(Low, High) | ||
|  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
|  | 
 | ||
|  | /* TBD: should use avlwalk(), but this isn't fine as it does not allow a
 | ||
|  |    failure return and does not allow passing our term-handles | ||
|  | */ | ||
|  | 
 | ||
|  | static int | ||
|  | unify_keys(term_t head, term_t tail, AVLnode *node) | ||
|  | { node_data *data; | ||
|  | 
 | ||
|  |   if ( node ) | ||
|  |   { if ( node->subtree[LEFT] ) | ||
|  |     { if ( !unify_keys(head, tail, node->subtree[LEFT]) ) | ||
|  | 	return FALSE; | ||
|  |     } | ||
|  | 
 | ||
|  |     data = (node_data*)node->data; | ||
|  |     if ( !PL_unify_list(tail, head, tail) || | ||
|  | 	 !unify_datum(head, data->key) ) | ||
|  |       return FALSE; | ||
|  | 
 | ||
|  |     if ( node->subtree[RIGHT] ) | ||
|  |       return unify_keys(head, tail, node->subtree[RIGHT]); | ||
|  |   } | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int | ||
|  | between_keys(atom_map *map, long min, long max, term_t head, term_t tail) | ||
|  | { avl_enum state; | ||
|  |   node_data *data; | ||
|  |   node_data_ex search; | ||
|  | 
 | ||
|  |   DEBUG(2, Sdprintf("between %ld .. %ld\n", min, max)); | ||
|  | 
 | ||
|  |   search.data.key = long_to_datum(min); | ||
|  |   SECURE(search.magic = ND_MAGIC_EX); | ||
|  | 
 | ||
|  |   if ( (data = avlfindfirst(&map->tree, &search, &state)) && | ||
|  |        isIntDatum(data->key) ) | ||
|  |   { for(;;) | ||
|  |     { if ( long_from_datum(data->key) > max ) | ||
|  | 	break; | ||
|  | 
 | ||
|  |       if ( !PL_unify_list(tail, head, tail) || | ||
|  | 	   !unify_datum(head, data->key) ) | ||
|  |       { avlfinddestroy(&state); | ||
|  | 	return FALSE; | ||
|  |       } | ||
|  | 
 | ||
|  |       if ( !(data = avlfindnext(&state)) || | ||
|  | 	   !isIntDatum(data->key) ) | ||
|  | 	break; | ||
|  |     } | ||
|  | 
 | ||
|  |     avlfinddestroy(&state); | ||
|  |   } | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | rdf_keys_in_literal_map(term_t handle, term_t spec, term_t keys) | ||
|  | { atom_map *map; | ||
|  |   term_t tail = PL_copy_term_ref(keys); | ||
|  |   term_t head = PL_new_term_ref(); | ||
|  |   atom_t name; | ||
|  |   int arity; | ||
|  | 
 | ||
|  |   if ( !get_atom_map(handle, &map) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( !RDLOCK(map) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( !PL_get_name_arity(spec, &name, &arity) ) | ||
|  |     type_error(spec, "key-specifier"); | ||
|  | 
 | ||
|  |   if ( name == ATOM_all ) | ||
|  |   { AVLnode *node = map->tree.root; | ||
|  | 
 | ||
|  |     if ( !unify_keys(head, tail, node) ) | ||
|  |       goto failure; | ||
|  |   } else if ( name == ATOM_key && arity == 1 ) | ||
|  |   { term_t a = PL_new_term_ref(); | ||
|  |     node_data *data; | ||
|  |     node_data_ex search; | ||
|  | 
 | ||
|  |     _PL_get_arg(1, spec, a); | ||
|  |     if ( !get_search_datum(a, &search) ) | ||
|  |       goto failure; | ||
|  | 
 | ||
|  |     if ( (data = avlfind(&map->tree, &search)) ) | ||
|  |     { long size = (long)data->values->size; | ||
|  | 
 | ||
|  |       RDUNLOCK(map); | ||
|  |       assert(size > 0); | ||
|  | 
 | ||
|  |       return PL_unify_integer(keys, size); | ||
|  |     } | ||
|  |     goto failure; | ||
|  |   } else if ( (name == ATOM_prefix || name == ATOM_case) && arity == 1 ) | ||
|  |   { term_t a = PL_new_term_ref(); | ||
|  |     atom_t prefix, first_a; | ||
|  |     avl_enum state; | ||
|  |     node_data *data; | ||
|  |     node_data_ex search; | ||
|  |     int match = (name == ATOM_prefix ? STR_MATCH_PREFIX : STR_MATCH_EXACT); | ||
|  | 
 | ||
|  |     _PL_get_arg(1, spec, a); | ||
|  |     if ( !get_atom_ex(a, &prefix) ) | ||
|  |       goto failure; | ||
|  |     first_a = first_atom(prefix, STR_MATCH_PREFIX); | ||
|  | 
 | ||
|  |     search.data.key = atom_to_datum(first_a); | ||
|  |     search.atom.handle = first_a; | ||
|  |     search.atom.resolved = FALSE; | ||
|  |     SECURE(search.magic = ND_MAGIC_EX); | ||
|  | 
 | ||
|  |     for(data = avlfindfirst(&map->tree, &search, &state); | ||
|  | 	data; | ||
|  | 	data=avlfindnext(&state)) | ||
|  |     { assert(isAtomDatum(data->key)); | ||
|  | 
 | ||
|  |       if ( !match_atoms(match, | ||
|  | 			first_a, atom_from_datum(data->key)) ) | ||
|  | 	break; | ||
|  | 
 | ||
|  |       if ( !PL_unify_list(tail, head, tail) || | ||
|  | 	   !unify_datum(head, data->key) ) | ||
|  |       { avlfinddestroy(&state); | ||
|  | 	goto failure; | ||
|  |       } | ||
|  |     } | ||
|  |     avlfinddestroy(&state); | ||
|  |   } else if ( (name == ATOM_ge || name == ATOM_le) && arity == 1 ) | ||
|  |   { term_t a = PL_new_term_ref(); | ||
|  |     long val, min, max; | ||
|  | 
 | ||
|  |     _PL_get_arg(1, spec, a); | ||
|  |     if ( !get_long_ex(a, &val) ) | ||
|  |       goto failure; | ||
|  | 
 | ||
|  |     if ( name == ATOM_ge ) | ||
|  |       min = val, max = MAP_MAX_INT; | ||
|  |     else | ||
|  |       max = val, min = MAP_MIN_INT; | ||
|  | 
 | ||
|  |     if ( !between_keys(map, min, max, head, tail) ) | ||
|  |       goto failure; | ||
|  |   } else if ( name == ATOM_between && arity == 2 ) | ||
|  |   { term_t a = PL_new_term_ref(); | ||
|  |     long min, max; | ||
|  | 
 | ||
|  |     _PL_get_arg(1, spec, a); | ||
|  |     if ( !get_long_ex(a, &min) ) | ||
|  |       goto failure; | ||
|  |     _PL_get_arg(2, spec, a); | ||
|  |     if ( !get_long_ex(a, &max) ) | ||
|  |       goto failure; | ||
|  | 
 | ||
|  |     if ( !between_keys(map, min, max, head, tail) ) | ||
|  |       goto failure; | ||
|  |   } else | ||
|  |   { type_error(spec, "key-specifier"); | ||
|  |     goto failure; | ||
|  |   } | ||
|  | 
 | ||
|  |   RDUNLOCK(map); | ||
|  | 
 | ||
|  |   return PL_unify_nil(tail); | ||
|  | 
 | ||
|  | failure: | ||
|  |   RDUNLOCK(map); | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	      RESET		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | static foreign_t | ||
|  | rdf_reset_literal_map(term_t handle) | ||
|  | { atom_map *map; | ||
|  | 
 | ||
|  |   if ( !get_atom_map(handle, &map) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( !WRLOCK(map, FALSE) ) | ||
|  |     return FALSE; | ||
|  |   avlfree(&map->tree); | ||
|  |   init_tree_map(map); | ||
|  |   map->value_count = 0; | ||
|  |   WRUNLOCK(map); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	    STATISTICS		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | 
 | ||
|  | term_t | ||
|  | rdf_statistics_literal_map(term_t map, term_t key) | ||
|  | { atom_map *m; | ||
|  | 
 | ||
|  |   if ( !get_atom_map(map, &m) ) | ||
|  |     return FALSE; | ||
|  | 
 | ||
|  |   if ( PL_is_functor(key, FUNCTOR_size2) ) | ||
|  |   { term_t a = PL_new_term_ref(); | ||
|  | 
 | ||
|  |     _PL_get_arg(1, key, a); | ||
|  |     if ( !PL_unify_integer(a, m->tree.count) ) | ||
|  |       return FALSE; | ||
|  |     _PL_get_arg(2, key, a); | ||
|  | 
 | ||
|  |     return PL_unify_integer(a, m->value_count); | ||
|  |   } | ||
|  | 
 | ||
|  |   return type_error(key, "statistics_key"); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 		 /*******************************
 | ||
|  | 		 *	     REGISTER		* | ||
|  | 		 *******************************/ | ||
|  | 
 | ||
|  | #define PRED(n,a,f,o) PL_register_foreign(n,a,f,o)
 | ||
|  | 
 | ||
|  | install_t | ||
|  | install_atom_map() | ||
|  | { init_functors(); | ||
|  |   init_datum_store(); | ||
|  | 
 | ||
|  |   PRED("rdf_new_literal_map",	     1,	new_atom_map,		    0); | ||
|  |   PRED("rdf_destroy_literal_map",    1,	destroy_atom_map,	    0); | ||
|  |   PRED("rdf_reset_literal_map",	     1, rdf_reset_literal_map,	    0); | ||
|  |   PRED("rdf_insert_literal_map",     3,	insert_atom_map3,	    0); | ||
|  |   PRED("rdf_insert_literal_map",     4,	insert_atom_map4,	    0); | ||
|  |   PRED("rdf_delete_literal_map",     3,	delete_atom_map3,	    0); | ||
|  |   PRED("rdf_delete_literal_map",     2,	delete_atom_map2,	    0); | ||
|  |   PRED("rdf_find_literal_map",	     3,	find_atom_map,		    0); | ||
|  |   PRED("rdf_keys_in_literal_map",    3,	rdf_keys_in_literal_map,    0); | ||
|  |   PRED("rdf_statistics_literal_map", 2,	rdf_statistics_literal_map, 0); | ||
|  | } |