text: /****************************************************************************
   *
   * fthash.c
   *
   *   Hashing functions (body).
   *
   */
  
  /*
   * Copyright 2000 Computing Research Labs, New Mexico State University
   * Copyright 2001-2015
   *   Francesco Zappa Nardelli
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
   * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
   * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
   * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
    /**************************************************************************
     *
     * This file is based on code from bdf.c,v 1.22 2000/03/16 20:08:50
     *
     * taken from Mark Leisher's xmbdfed package
     *
     */
  
  
  #include <freetype/internal/fthash.h>
  #include <freetype/internal/ftmemory.h>
  
  
  #define INITIAL_HT_SIZE  241
  
  
    static FT_ULong
    hash_str_lookup( FT_Hashkey*  key )
    {
      const char*  kp  = key->str;
      FT_ULong     res = 0;
  
  
      /* Mocklisp hash function. */
      while ( *kp )
        res = ( res << 5 ) - res + (FT_ULong)*kp++;
  
      return res;
    }
  
  
    static FT_ULong
    hash_num_lookup( FT_Hashkey*  key )
    {
      FT_ULong  num = (FT_ULong)key->num;
      FT_ULong  res;
  
  
      /* Mocklisp hash function. */
      res = num & 0xFF;
      res = ( res << 5 ) - res + ( ( num >>  8 ) & 0xFF );
      res = ( res << 5 ) - res + ( ( num >> 16 ) & 0xFF );
      res = ( res << 5 ) - res + ( ( num >> 24 ) & 0xFF );
  
      return res;
    }
  
  
    static FT_Bool
    hash_str_compare( FT_Hashkey*  a,
                      FT_Hashkey*  b )
    {
      if ( a->str[0] == b->str[0]           &&
           ft_strcmp( a->str, b->str ) == 0 )
        return 1;
  
      return 0;
    }
  
  
    static FT_Bool
    hash_num_compare( FT_Hashkey*  a,
                      FT_Hashkey*  b )
    {
      if ( a->num == b->num )
        return 1;
  
      return 0;
    }
  
  
    static FT_Hashnode*
    hash_bucket( FT_Hashkey  key,
                 FT_Hash     hash )
    {
      FT_ULong      res = 0;
      FT_Hashnode*  bp  = hash->table;
      FT_Hashnode*  ndp;
  
  
      res = (hash->lookup)( &key );
  
      ndp = bp + ( res % hash->size );
      while ( *ndp )
      {
        if ( (hash->compare)( &(*ndp)->key, &key ) )
          break;
  
        ndp--;
        if ( ndp < bp )
          ndp = bp + ( hash->size - 1 );
      }
  
      return ndp;
    }
  
  
    static FT_Error
    hash_rehash( FT_Hash    hash,
                 FT_Memory  memory )
    {
      FT_Hashnode*  obp = hash->table;
      FT_Hashnode*  bp;
      FT_Hashnode*  nbp;
  
      FT_UInt   i, sz = hash->size;
      FT_Error  error = FT_Err_Ok;
  
  
      hash->size <<= 1;
      hash->limit  = hash->size / 3;
  
      if ( FT_NEW_ARRAY( hash->table, hash->size ) )
        goto Exit;
  
      for ( i = 0, bp = obp; i < sz; i++, bp++ )
      {
        if ( *bp )
        {
          nbp = hash_bucket( (*bp)->key, hash );
          *nbp = *bp;
        }
      }
  
      FT_FREE( obp );
  
    Exit:
      return error;
    }
  
  
    static FT_Error
    hash_init( FT_Hash    hash,
               FT_Bool    is_num,
               FT_Memory  memory )
    {
      FT_UInt   sz = INITIAL_HT_SIZE;
      FT_Error  error;
  
  
      hash->size  = sz;
      hash->limit = sz / 3;
      hash->used  = 0;
  
      if ( is_num )
      {
        hash->lookup  = hash_num_lookup;
        hash->compare = hash_num_compare;
      }
      else
      {
        hash->lookup  = hash_str_lookup;
        hash->compare = hash_str_compare;
      }
  
      FT_MEM_NEW_ARRAY( hash->table, sz );
  
      return error;
    }
  
  
    FT_Error
    ft_hash_str_init( FT_Hash    hash,
                      FT_Memory  memory )
    {
      return hash_init( hash, 0, memory );
    }
  
  
    FT_Error
    ft_hash_num_init( FT_Hash    hash,
                      FT_Memory  memory )
    {
      return hash_init( hash, 1, memory );
    }
  
  
    void
    ft_hash_str_free( FT_Hash    hash,
                      FT_Memory  memory )
    {
      if ( hash )
      {
        FT_UInt       sz = hash->size;
        FT_Hashnode*  bp = hash->table;
        FT_UInt       i;
  
  
        for ( i = 0; i < sz; i++, bp++ )
          FT_FREE( *bp );
  
        FT_FREE( hash->table );
      }
    }
  
  
    /* `ft_hash_num_free' is the same as `ft_hash_str_free' */
  
  
    static FT_Error
    hash_insert( FT_Hashkey  key,
                 size_t      data,
                 FT_Hash     hash,
                 FT_Memory   memory )
    {
      FT_Hashnode   nn;
      FT_Hashnode*  bp    = hash_bucket( key, hash );
      FT_Error      error = FT_Err_Ok;
  
  
      nn = *bp;
      if ( !nn )
      {
        if ( FT_NEW( nn ) )
          goto Exit;
        *bp = nn;
  
        nn->key  = key;
        nn->data = data;
  
        if ( hash->used >= hash->limit )
        {
          error = hash_rehash( hash, memory );
          if ( error )
            goto Exit;
        }
  
        hash->used++;
      }
      else
        nn->data = data;
  
    Exit:
      return error;
    }
  
  
    FT_Error
    ft_hash_str_insert( const char*  key,
                        size_t       data,
                        FT_Hash      hash,
                        FT_Memory    memory )
    {
      FT_Hashkey  hk;
  
  
      hk.str = key;
  
      return hash_insert( hk, data, hash, memory );
    }
  
  
    FT_Error
    ft_hash_num_insert( FT_Int     num,
                        size_t     data,
                        FT_Hash    hash,
                        FT_Memory  memory )
    {
      FT_Hashkey  hk;
  
  
      hk.num = num;
  
      return hash_insert( hk, data, hash, memory );
    }
  
  
    static size_t*
    hash_lookup( FT_Hashkey  key,
                 FT_Hash     hash )
    {
      FT_Hashnode*  np = hash_bucket( key, hash );
  
  
      return (*np) ? &(*np)->data
                   : NULL;
    }
  
  
    size_t*
    ft_hash_str_lookup( const char*  key,
                        FT_Hash      hash )
    {
      FT_Hashkey  hk;
  
  
      hk.str = key;
  
      return hash_lookup( hk, hash );
    }
  
  
    size_t*
    ft_hash_num_lookup( FT_Int   num,
                        FT_Hash  hash )
    {
      FT_Hashkey  hk;
  
  
      hk.num = num;
  
      return hash_lookup( hk, hash );
    }
  
  
  /* END */
  