ftp.nice.ch/pub/next/connectivity/infosystems/WAIStation.1.9.6.N.b.tar.gz#/WAIS/ir/irhash.c

This is irhash.c in view mode; [Download] [Up]

/* WIDE AREA INFORMATION SERVER SOFTWARE:
   No guarantees or restrictions.  See the readme file for the full standard
   disclaimer.

   Brewster@think.com
*/

/* The memory hashtables for building an index. */
/* -brewster 5/90 */

/* Change log:
 * $Log:	irhash.c,v $
 * Revision 1.16  92/03/20  11:04:33  jonathan
 * Added code to handle switches for word_pairs and word_postition info.
 * 
 * Revision 1.15  92/02/12  13:26:28  jonathan
 * Added "$Log" so RCS will put the log message in the header
 * 
*/

/* main functions:
 *   add_word
 *   finished_add_word
 *   look_up_word
 *
 * The idea is to store up a bunch of words before going to disk.
 * A word entry points to where it will go on disk, and
 * accumulates the entries before doing it.
 *
 * Some of the policy issues in this file are:
 *   How much weight should the first occurance of a word in a document get
 *   over the other occurances.  The first occurance should be worth more
 *   so that words with 3 occurances of "dog" and not "cat"'s should not 
 *   win out over 1 "dog" and 1 "cat" if the question is "Tell me about cats
 *   torture dogs"
 *   The extra weight is 5 at this point.
 *
 */

/* To Do:
 *  done: Improve the hashing functions.
 *  done: stop inserting into hash table after max number have been accumulated
 *  done: make flush not flush buffers that are too big.
 */
 
#include <ctype.h>
#include <string.h> 	/* for strlen(), memset() */

#include "panic.h"
#include "cutil.h"
#include "irfiles.h"
#include "irhash.h"
#include "stoplist.h"
#include "irinv.h"

#ifdef UNIX
#define PRINT_AS_INDEXING true /* also defined in irtfiles.c and irfiles.c */
#else 
#define PRINT_AS_INDEXING false
#endif

/* ================================
   ===  Word Occurance Buffers  ===
   ================================ */

/* Word occurance buffers
 * This is a simple memory allocator for use with the word memory hashtable.
 * Since the buffers are tiny, this is done as a copy-sweep GC scheme.
 * Oh, I long for the storage system of lisp.
 */
unsigned char *first_word_occurance_buffer = NULL;  /* allocate blocks out of this */
unsigned char *last_word_occurance_buffer = NULL;
long word_occurance_block_length = 256000;  /* maybe this should be larger? */
unsigned char * word_occurance_free_ptr = NULL;

unsigned char *make_word_occurrance_block(size)
long size;

{
  /* allocates a word_occurance_block out of the buffers */
  /* old way: s_malloc((size_t)size); */
  /* returns a pointer to a piece of memory */
  if(NULL == first_word_occurance_buffer){
    /* initialize it */
    first_word_occurance_buffer = 
      (unsigned char *)s_malloc(MAX(word_occurance_block_length,
			   sizeof(size_t)+ size));
    *(unsigned char **)first_word_occurance_buffer = NULL; /* set the end */
    last_word_occurance_buffer = first_word_occurance_buffer;
    word_occurance_free_ptr = first_word_occurance_buffer + sizeof(size_t);
  }
  if((long)word_occurance_free_ptr + size >= 
     word_occurance_block_length + (long)last_word_occurance_buffer){
    /* then allocate a new block */
    unsigned char * new_block = (unsigned char *)s_malloc(MAX(word_occurance_block_length,
					    sizeof(size_t)+ size));
    *(unsigned char **)new_block = NULL; /* set the end of the chain */
    *(unsigned char **)last_word_occurance_buffer = new_block;
    word_occurance_free_ptr = new_block + sizeof(size_t);
    last_word_occurance_buffer = new_block;
  }
  /* allocate away */	
  { unsigned char * answer = word_occurance_free_ptr;
    word_occurance_free_ptr += size;	
    return(answer);  
  }
}

void free_word_occurance_block(block)
unsigned char *block;
{
  /* this is not used with the new scheme, but is here in case
     malloc is a win on some systems */
  /* old way s_free(block); */
}

static void flush_word_occur_bufs_internal
  _AP((unsigned char* head_of_list));

static void flush_word_occur_bufs_internal(head_of_list)
unsigned char* head_of_list;
/* frees all word occurance buffers.  This should be done with care */
{      
  while(1){
    unsigned char * next_block;
    if(NULL == head_of_list)
      break;
    next_block = *(unsigned char **)head_of_list;
    s_free(head_of_list);
    head_of_list = next_block;
  }
}

void flush_word_occurance_buffers()
{
  /* frees all word occurance buffers.  This should be done with care */
  flush_word_occur_bufs_internal(first_word_occurance_buffer);
  first_word_occurance_buffer = NULL;
  word_occurance_free_ptr = NULL;
  last_word_occurance_buffer = NULL;
}


/* ---------------------------------------------------- */
static hash_entry* look_up_word _AP((char* word,hashtable*
				     the_word_memory_hashtable));
  
static hash_entry* 
look_up_word(word,the_word_memory_hashtable)
char* word;
hashtable* the_word_memory_hashtable;
{
  hash_entry * answer = get_hash(word, the_word_memory_hashtable);
  
  if(NULL != answer)
    return(answer);
  else{
    hash_entry wrd_entry;
    answer = put_hash(word, the_word_memory_hashtable, &wrd_entry);
    answer->number_of_occurances = 0;
    answer->memory_ptr = 
      make_word_occurrance_block(WORD_MEMORY_INIT_BLOCK_SIZE);
    answer->current_memory_ptr = answer->memory_ptr;
    answer->memory_size = WORD_MEMORY_INIT_BLOCK_SIZE;
    answer->current_doc_id = 0;
    return(answer);
  }
}

static unsigned char add_weight _AP((long current_weight,long new_weight));

static unsigned char 
add_weight(current_weight,new_weight)
long current_weight;
long new_weight;
/* add a new weight to the existing one */
{
  /* this should be smarter than this, like doing the log or something */
  if(127 < (current_weight + new_weight)){
    /* the max char.  should be 255, but does not work on all compilers */
    return(127);
  }
  else{
    return(current_weight + new_weight);
  }
}

static unsigned char* more_memory _AP((unsigned char* current_memory_ptr,
				       long current_memory_size,
				       long new_size));

static unsigned char* more_memory(current_memory_ptr,current_memory_size,new_size)
unsigned char* current_memory_ptr;
long current_memory_size;
long new_size;
/* Allocates more memory for a hash_entry.  It transfers all the bytes 
 * from the old to the new and then returns the new.
 */
{
  unsigned char* new_memory = NULL;
  if(current_memory_size > new_size){
    panic("trying to contract a hash_entry block.  This is not right\n");
  }
  new_memory = make_word_occurrance_block(new_size);
  if(NULL == new_memory){
    panic("Out of memory.");
  }
  memset(new_memory, 0, new_size);
  memmove(new_memory, current_memory_ptr, (size_t)current_memory_size); 
  return(new_memory);
}

static long more_memory_size _AP((long current_size,
				  long number_of_occurances));

static long more_memory_size(current_size,number_of_occurances)
long current_size;
long number_of_occurances;
/* This is pretty important to get right.  This is a place holder */
{
  return(MAX(2 * current_size, WORD_MEMORY_INIT_BLOCK_SIZE));
}

long write_bytes_to_memory(value,size,ptr)
long value;
long size;
unsigned char* ptr;
{
  /* writes the number into memory lsb first.  
     returns the number of bytes written */
  long i;
  long original_value = value;

  if(size < 0) /* paranoia */
    panic("attempting to write a negative number of bytes");

  ptr += size; /* start at the end of the block and write backwards */
  for (i = 0; i < size; i++){
    ptr--;
    *ptr = (unsigned char)(value & 0xFF);
    value = value >> 8;
  }
  if(value != 0)
    panic("In a call to write_bytes_to_memory, the value %ld can not be represented in %ld bytes", original_value, size);

  return(size);
}
		
/* adds a word to the hashtable. Returns the 0 if successful. 
   See irext.h for more documentation.
 */
long add_word(word, char_pos, line_pos,
	      weight, doc_id, date, word_pair, db, word_position)
     char *word;	/* the word to be indexed, this could be a
			   word pair. If NULL there are no more words
			   to be indexed */
     long char_pos;	/* the position of the start of the
			   word */
     long line_pos;	/* this is passed for the best
			   section calculation */
     long weight;	/* how important the word looks
			   syntactically (such as is it bold)
			   NOT used by signature system */
     long doc_id; 	/* current document, this will never be 0 */
     time_t date; /* display day of this document, 0 if not known */
     long word_pair;
     database* db; /* database to insert the document */
     boolean word_position; /* whether to have multiple entries for words in a document */
{
  /* look up the word in the hashtable */
  /* creates it if necessary */	
  hash_entry* wrd_entry;
  hashtable * the_word_memory_hashtable = db->the_word_memory_hashtable;
  /* printf("Word: '%s' doc_id: %ld, pos: %ld, weight: %ld\n",
     word, doc_id, char_pos, weight); */
  
  if(NULL == db->the_word_memory_hashtable){
    panic("The memory word hashtable is not defined.");
  }

  /* if we have indexed enough words flush the memory copies to disk. */
  if(db->number_of_words_in_hashtable == db->flush_after_n_words)
    flush_memory_hashtable_to_disk(db, false);

  wrd_entry = look_up_word(word, the_word_memory_hashtable);
  wrd_entry->number_of_occurances ++;

  if(wrd_entry->number_of_occurances > MAX_OCCURANCES){
    /* do nothing. we have enough of that word */
  }
  else{
    /* we have a word to add */
    db->number_of_words_in_hashtable ++;

    if(word_position || doc_id != wrd_entry->current_doc_id){
      /* then we have a new doc_id to add to the memory block */
          
      /* check to see if we need more memory */
      if((wrd_entry->memory_size -
	  (wrd_entry->current_memory_ptr - 
	   wrd_entry->memory_ptr) 
	  < 
	  INDEX_ELEMENT_SIZE)){
	/* we need more memory. this makes more and frees the old*/
	unsigned char* old_memory_ptr = wrd_entry->memory_ptr;
 
	long new_size = 
	  more_memory_size(wrd_entry->memory_size,
			   wrd_entry->number_of_occurances);
	/* cprintf(PRINT_AS_INDEXING, "Get more memory %ld bytes for %s\n", new_size, word); */
	wrd_entry->memory_ptr = 
	  more_memory(wrd_entry->memory_ptr, wrd_entry->memory_size,
		      new_size);
	wrd_entry->current_memory_ptr = 
	  wrd_entry->memory_ptr + /* new offset */
	    (wrd_entry->current_memory_ptr - old_memory_ptr);
	/* just being paranoid... no longer illegal
	   if(wrd_entry->current_memory_ptr == wrd_entry->memory_ptr)
	   panic("After allocating more memory, the size went to 0");
	   */
	wrd_entry->memory_size = new_size;
      }				/* finished making more memory */

      /* add away */
      wrd_entry->current_memory_ptr +=
	write_bytes_to_memory(doc_id, DOCUMENT_ID_SIZE,
			      wrd_entry->current_memory_ptr);
      wrd_entry->current_memory_ptr +=
	write_bytes_to_memory(char_pos, 
			      CHARACTER_POSITION_SIZE,
			      wrd_entry->current_memory_ptr);
      wrd_entry->current_memory_ptr +=
	write_bytes_to_memory(weight +
			       /* add 5 since for the first one */
			      (doc_id != wrd_entry->current_doc_id)?5:0,
			      WEIGHT_SIZE,
			      wrd_entry->current_memory_ptr);
      wrd_entry->current_doc_id = doc_id;

    }
    else{
      /* The word is already there,
       * just increment the weight in the record.
       * This will change when/if position information is kept (for proximity).
       */
      if(wrd_entry->current_memory_ptr == wrd_entry->memory_ptr){
	panic("Memory hashtable error. Recorded doc_id %ld, current doc_id %ld\n",
	      wrd_entry->current_doc_id, doc_id);
      }
      *(wrd_entry->current_memory_ptr - 1) =
	add_weight(*(wrd_entry->current_memory_ptr - 1), weight);
    }
  }
  return(0L);
}

void add_stop_words(the_word_memory_hashtable)
hashtable *the_word_memory_hashtable;
     /* add the stop words to the hashtable.  this must be done before
	adding other words */
{
  init_stop_list();
  while(true){
    char *word = next_stop_word();
    hash_entry* wrd_entry;

    if(NULL == word)
      break;
    wrd_entry = look_up_word(word, the_word_memory_hashtable);
    wrd_entry->number_of_occurances = STOP_WORD_FLAG;
  }
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.