/* Copyright (C) 1996 Bjoern Beutel. */
/* Description. =============================================================*/
/* Manages the storage of analysis results for faster access. */
/* Includes. ================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <stdarg.h>
#include <glib.h>
#include "basic.h"
#include "pools.h"
#include "values.h"
#include "avl_trees.h"
#include "cache.h"
/* Types. ===================================================================*/
typedef struct cache_entry /* An entry of the cache tree. */
{
avl_node_t avl_node; /* A node in an AVL tree. */
struct cache_entry *prev_ref, *next_ref; /* LRU chain. */
string_t surface;
int_t feat_count; /* Number of feature structures in feat_vector. */
value_t *feat_vector; /* A vector of feature structures. */
} cache_entry_t;
/* Global Variables. ========================================================*/
int_t cache_accesses; /* Number of calls of "word_in_cache". */
int_t cache_hits; /* Number of successful calls of "word_in_cache". */
/* Variables. ===============================================================*/
static avl_node_t *cache_tree; /* The cache tree. Actually points to
* cache_entry_t. */
static int_t max_entry_count; /* Maximum of entries in CACHE_TREE. */
static int_t entry_count; /* Actual number of entries in CACHE_TREE. */
static cache_entry_t *first_ref; /* The entry that was referenced first. */
static cache_entry_t *last_ref; /* The entry that was referenced last. */
/* These are needed for "next_result_in_cache". */
static cache_entry_t *current_entry;
static int_t current_result;
/* Functions. ===============================================================*/
static int_t
compare_cache_entries( avl_node_t *node1, avl_node_t *node2 )
/* A callback function for "avl_tree_insert", "avl_tree_remove",
* used to compare "cache_entry_t" nodes. */
{
return strcmp( ((cache_entry_t *) node1)->surface,
((cache_entry_t *) node2)->surface );
}
/*---------------------------------------------------------------------------*/
static cache_entry_t *
new_cache_entry( string_t surf_start, string_t surf_end,
int_t feat_count, value_t feat_vector[] )
/* Create cache entry for string at SURF_START..SURF_END.
* It has FEAT_COUNT feature structures stored in FEAT_VECTOR.
* Return the created entry. */
{
cache_entry_t *cache_entry;
/* Allocate a cache entry. */
cache_entry = new_mem( sizeof( cache_entry_t ) );
cache_entry->surface = new_string( surf_start, surf_end );
cache_entry->feat_count = feat_count;
cache_entry->feat_vector = feat_vector;
entry_count++;
return cache_entry;
}
/*---------------------------------------------------------------------------*/
static void
free_cache_entry( cache_entry_t **cache_entry )
/* Free the memory allocated for *CACHE_ENTRY. */
{
int_t i;
if (*cache_entry != NULL)
{
for (i = 0; i < (*cache_entry)->feat_count; i++)
free_mem( &(*cache_entry)->feat_vector[i] );
free_mem( &(*cache_entry)->feat_vector );
free_mem( &(*cache_entry)->surface );
free_mem( cache_entry );
entry_count--;
}
}
/*---------------------------------------------------------------------------*/
static void
reference_entry( cache_entry_t *entry )
/* Put ENTRY at end of LRU-list if it is not already there. */
{
if (entry != last_ref)
{
/* Remove entry from old position. */
entry->next_ref->prev_ref = entry->prev_ref;
if (entry != first_ref)
entry->prev_ref->next_ref = entry->next_ref;
else
first_ref = entry->next_ref;
/* Enter entry in new position. */
entry->prev_ref = last_ref;
entry->next_ref = NULL;
last_ref->next_ref = entry;
last_ref = entry;
}
}
/*---------------------------------------------------------------------------*/
bool_t
word_in_cache( string_t surf_start, string_t surf_end )
/* Check if word in [SURF_START..SURF_END] is in the cache.
* Return TRUE if word found. Use "next_result_in_cache" to get next result. */
{
cache_entry_t *entry;
int_t result;
cache_accesses++;
entry = (cache_entry_t *) cache_tree;
while (entry != NULL)
{
result = strncmp( surf_start, entry->surface, surf_end - surf_start );
if (result == 0 && entry->surface[ surf_end - surf_start ] != EOS)
result = -1;
if (result < 0)
entry = (cache_entry_t *) entry->avl_node.left;
else if (result > 0)
entry = (cache_entry_t *) entry->avl_node.right;
else /* Word found. */
{
reference_entry( entry );
current_entry = entry;
current_result = 0;
cache_hits++;
return TRUE;
}
}
return FALSE;
}
/*---------------------------------------------------------------------------*/
value_t
next_result_in_cache( void )
/* Return the next feature structure for the word found by "word_in_cache".
* Return NULL if no more feature structure exists. */
{
value_t result;
if (current_result >= current_entry->feat_count)
return NULL;
result = current_entry->feat_vector[ current_result ];
current_result++;
return result;
}
/*---------------------------------------------------------------------------*/
static void
remove_entry_from_cache( void )
/* Delete an entry from cache. */
{
cache_entry_t *entry;
/* Remove first element from LRU list. */
entry = first_ref;
first_ref = entry->next_ref;
if (first_ref != NULL)
first_ref->prev_ref = NULL;
else
last_ref = NULL;
/* Remove ENTRY from tree. */
remove_avl_node( (avl_node_t *) entry, (avl_node_t **) &cache_tree,
compare_cache_entries );
free_cache_entry( &entry );
}
/*---------------------------------------------------------------------------*/
void
enter_in_cache( string_t surf_start, string_t surf_end,
int_t feat_count, value_t feat_vector[] )
/* Enter the word in [SURF_START..SURF_END] in the cache.
* It has FEAT_COUNT feature structures, stored in FEAT_VECTOR[].
* Be sure that the word is not yet in the cache. */
{
cache_entry_t *cache_entry;
if (entry_count >= max_entry_count)
remove_entry_from_cache();
cache_entry = new_cache_entry( surf_start, surf_end,
feat_count, feat_vector );
/* Enter entry in cache tree. */
insert_avl_node( (avl_node_t *) cache_entry, (avl_node_t **) &cache_tree,
compare_cache_entries );
/* Put entry at end of LRU table. */
if (last_ref != NULL)
last_ref->next_ref = cache_entry;
cache_entry->prev_ref = last_ref;
cache_entry->next_ref = NULL;
last_ref = cache_entry;
if (first_ref == NULL)
first_ref = cache_entry;
}
/*---------------------------------------------------------------------------*/
void
clear_cache( void )
/* Remove all entries from the cache. */
{
while (entry_count > 0)
remove_entry_from_cache();
}
/*---------------------------------------------------------------------------*/
void
set_cache_size( int_t size )
/* Set maximum number of cache entries to SIZE. */
{
max_entry_count = size;
while (entry_count > max_entry_count)
remove_entry_from_cache();
}
/*---------------------------------------------------------------------------*/
int_t
get_cache_size( void )
/* Get actual number of cache entries. */
{
return entry_count;
}
/*---------------------------------------------------------------------------*/
int_t
get_cache_maximum( void )
/* Get maximum number of cache entries. */
{
return max_entry_count;
}
/* End of file. =============================================================*/