Package com.aelitis.azureus.core.dht.db.impl

Source Code of com.aelitis.azureus.core.dht.db.impl.DHTDBMapping

/*
* Created on 03-Feb-2005
* Created by Paul Gardner
* Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
* 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/

package com.aelitis.azureus.core.dht.db.impl;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.util.*;

import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.SystemTime;
// import org.gudy.azureus2.core3.util.SHA1Hasher;

import com.aelitis.azureus.core.dht.*;
import com.aelitis.azureus.core.dht.impl.DHTLog;
import com.aelitis.azureus.core.dht.transport.DHTTransportContact;
import com.aelitis.azureus.core.util.bloom.BloomFilter;
import com.aelitis.azureus.core.util.bloom.BloomFilterFactory;

/**
* @author parg
*
*/

public class
DHTDBMapping
{
  private static final boolean  TRACE_ADDS    = false;
 
  private DHTDBImpl      db;
  private HashWrapper      key;
  private ShortHash      short_key;
  private DHTStorageKey    adapter_key;
 
    // maps are access order, most recently used at tail, so we cycle values
   
  private Map<HashWrapper,DHTDBValueImpl>    direct_originator_map_may_be_null;
  private Map<HashWrapper,DHTDBValueImpl>    indirect_originator_value_map    = createLinkedMap();
 
  private int        hits;
 
  private int        direct_data_size;
  private int        indirect_data_size;
  private int        local_size;
 
  private byte      diversification_state  = DHT.DT_NONE;
 
  private static final int    IP_COUNT_BLOOM_SIZE_INCREASE_CHUNK  = 50;

    // 4 bit filter - counts up to 15
 
  private Object   ip_count_bloom_filter;

  protected
  DHTDBMapping(
    DHTDBImpl      _db,
    HashWrapper      _key,
    boolean        _local )
  {
    db      = _db;
    key      = _key;
   
    short_key = new ShortHash( key.getBytes());
   
    try{
      if ( db.getAdapter() != null ){
       
        adapter_key = db.getAdapter().keyCreated( key, _local );
       
        if ( adapter_key != null ){
         
          diversification_state  = adapter_key.getDiversificationType();
        }
      }
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  protected  Map<HashWrapper,DHTDBValueImpl>
  createLinkedMap()
  {   
    return( new LinkedHashMap<HashWrapper,DHTDBValueImpl>(1, 0.75f, true ));
  }
 
  protected HashWrapper
  getKey()
  {
    return( key );
  }

  protected ShortHash
  getShortKey()
  {
    return( short_key );
  }
 
  protected void
  updateLocalContact(
    DHTTransportContact    contact )
  {
      // pull out all the local values, reset the originator and then
      // re-add them
   
    if ( direct_originator_map_may_be_null == null ){
     
      return;
    }
   
    List<DHTDBValueImpl>  changed = new ArrayList<DHTDBValueImpl>();
   
    Iterator<DHTDBValueImpl>  it = direct_originator_map_may_be_null.values().iterator();
   
    while( it.hasNext()){
   
      DHTDBValueImpl  value = it.next();
     
      if ( value.isLocal()){
     
        value.setOriginatorAndSender( contact );
       
        changed.add( value );
       
        direct_data_size -= value.getValue().length;

        local_size  -= value.getValue().length;
       
        it.remove();
       
        informDeleted( value );
      }
    }
   
    for (int i=0;i<changed.size();i++){
     
      add(changed.get(i));
    }
  }
 
  // All values have
  //  1) a key
  //  2) a value
  //  3) an originator (the contact who originally published it)
  //  4) a sender  (the contact who sent it, could be diff for caches)

  // rethink time :P
  // a) for a value where sender + originator are the same we store a single value
  // b) where sender + originator differ we store an entry per originator/value pair as the
  //    send can legitimately forward multiple values but their originator should differ
 
  // c) the code that adds values is responsible for not accepting values that are either
  //    to "far away" from our ID, or that are cache-forwards from a contact "too far"
  //    away.

 
  // for a given key
  //    c) we only allow up to 8 entries per sending IP address (excluding port)
  //    d) if multiple entries have the same value the value is only returned once
  //     e) only the originator can delete an entry

  // a) prevents a single sender from filling up the mapping with garbage
  // b) prevents the same key->value mapping being held multiple times when sent by different caches
  // c) prevents multiple senders from same IP filling up, but supports multiple machines behind NAT
  // d) optimises responses.
 
  // Note that we can't trust the originator value in cache forwards, we therefore
  // need to prevent someone from overwriting a valid originator->value1 mapping
  // with an invalid originator->value2 mapping - that is we can't use uniqueness of
  // originator

  // a value can be "volatile" - this means that the cacher can ping the originator
  // periodically and delete the value if it is dead


  // the aim here is to
  //  1)   reduce ability for single contacts to spam the key while supporting up to 8
  //    contacts on a given IP (assuming NAT is being used)
  //  2)  stop one contact deleting or overwriting another contact's entry
  //  3)  support garbage collection for contacts that don't delete entries on exit

  // TODO: we should enforce a max-values-per-sender restriction to stop a sender from spamming
  // lots of keys - however, for a small DHT we need to be careful
 
  protected void
  add(
    DHTDBValueImpl    new_value )
  {
    // don't replace a closer cache value with a further away one. in particular
    // we have to avoid the case where the original publisher of a key happens to
    // be close to it and be asked by another node to cache it!

    DHTTransportContact  originator     = new_value.getOriginator();
    DHTTransportContact  sender       = new_value.getSender();

    HashWrapper  originator_id = new HashWrapper( originator.getID());
   
    boolean  direct = Arrays.equals( originator.getID(), sender.getID());
   
    if ( direct ){
     
        // direct contact from the originator is straight forward
     
      addDirectValue( originator_id, new_value );
     
        // remove any indirect values we might already have for this
     
      Iterator<Map.Entry<HashWrapper,DHTDBValueImpl>>  it = indirect_originator_value_map.entrySet().iterator();
     
      List<HashWrapper>  to_remove = new ArrayList<HashWrapper>();
     
      while( it.hasNext()){
       
        Map.Entry<HashWrapper,DHTDBValueImpl>  entry = it.next();
       
        HashWrapper    existing_key  = entry.getKey();
       
        DHTDBValueImpl  existing_value  = entry.getValue();
 
        if ( Arrays.equals( existing_value.getOriginator().getID(), originator.getID())){
       
          to_remove.add( existing_key );
        }
      }
     
      for (int i=0;i<to_remove.size();i++){
       
        removeIndirectValue((HashWrapper)to_remove.get(i));
      }
    }else{
     
        // not direct. if we have a value already for this originator then
        // we drop the value as the originator originated one takes precedence
     
      if (   direct_originator_map_may_be_null != null &&
          direct_originator_map_may_be_null.get( originator_id ) != null ){
       
        return;
      }
           
        // rule (b) - one entry per originator/value pair
       
      HashWrapper  originator_value_id = getOriginatorValueID( new_value );

      DHTDBValueImpl existing_value = indirect_originator_value_map.get( originator_value_id );
     
      if ( existing_value != null ){
       
        addIndirectValue( originator_value_id, new_value );
         
          //System.out.println( "    replacing existing" );
             
      }else{
     
          // only add new values if not diversified
       
        if ( diversification_state == DHT.DT_NONE ){
       
          addIndirectValue( originator_value_id, new_value );
        }
      } 
    }
  }

  private HashWrapper
  getOriginatorValueID(
    DHTDBValueImpl  value )
  {
    DHTTransportContact  originator  = value.getOriginator();
   
    byte[]  originator_id  = originator.getID();
   
      // relaxed this due to problems caused by multiple publishes by an originator
      // with the same key but variant values (e.g. seed/peer counts). Seeing as we
      // only accept cache-forwards from contacts that are "close" enough to us to
      // be performing such a forward, the DOS possibilities here are limited (a nasty
      // contact can only trash originator values for things it happens to be close to)
   
    return( new HashWrapper( originator_id ));
   
    /*
    byte[]  value_bytes   = value.getValue();

    byte[]  x = new byte[originator_id.length + value_bytes.length];
   
    System.arraycopy( originator_id, 0, x, 0, originator_id.length );
    System.arraycopy( value_bytes, 0, x, originator_id.length, value_bytes.length );
   
    HashWrapper  originator_value_id = new HashWrapper( new SHA1Hasher().calculateHash( x ));
   
    return( originator_value_id );
    */
  }
 
  protected void
  addHit()
  {
    hits++;
  }
 
  protected int
  getHits()
  {
    return( hits );
  }
 
  protected int
  getIndirectSize()
  {
    return( indirect_data_size );
  }
 
  protected int
  getDirectSize()
  {
      // our direct count includes local so remove that here
   
    return( direct_data_size - local_size );
  }
 
  protected int
  getLocalSize()
  {
    return( local_size );
  }
 
  protected DHTDBValueImpl[]
  get(
    DHTTransportContact    by_who,
    int            max,
    byte          flags )
  {
    if ((flags & DHT.FLAG_STATS) != 0 ){
     
      if ( adapter_key != null ){
                       
        try{
          ByteArrayOutputStream  baos = new ByteArrayOutputStream(64);
         
          DataOutputStream  dos = new DataOutputStream( baos );
         
          adapter_key.serialiseStats( dos );
         
          dos.close();
         
          return(
            new DHTDBValueImpl[]{
              new DHTDBValueImpl(
                SystemTime.getCurrentTime(),
                baos.toByteArray(),
                0,
                db.getLocalContact(),
                db.getLocalContact(),
                true,
                DHT.FLAG_STATS,
                0,
                DHT.REP_FACT_DEFAULT )});
         
        }catch( Throwable e ){
         
          Debug.printStackTrace(e);
        }
      }
       
      return( new DHTDBValueImpl[0] );
    }
   
    List<DHTDBValueImpl>  res     = new ArrayList<DHTDBValueImpl>();
   
    Set<HashWrapper>    duplicate_check = new HashSet<HashWrapper>();
   
    Map<HashWrapper,DHTDBValueImpl>[]  maps = new Map[]{ direct_originator_map_may_be_null, indirect_originator_value_map };
   
    for (int i=0;i<maps.length;i++){
     
      Map<HashWrapper,DHTDBValueImpl>      map  = maps[i];
     
      if ( map == null ){
       
        continue;
      }
     
      List<HashWrapper>  keys_used   = new ArrayList<HashWrapper>();

      Iterator<Map.Entry<HashWrapper,DHTDBValueImpl>>  it = map.entrySet().iterator();
   
      while( it.hasNext() && ( max==0 || res.size()< max )){
     
        Map.Entry<HashWrapper,DHTDBValueImpl>  entry = it.next();
       
        HashWrapper    entry_key  = entry.getKey();
       
        DHTDBValueImpl  entry_value = entry.getValue();
           
        HashWrapper  x = new HashWrapper( entry_value.getValue());
       
        if ( duplicate_check.contains( x )){
         
          continue;
        }
       
        duplicate_check.add( x );
               
          // zero length values imply deleted values so don't return them
       
        if ( entry_value.getValue().length > 0 ){
         
          res.add( entry_value );
       
          keys_used.add( entry_key );
        }
      }
     
        // now update the access order so values get cycled
     
      for (int j=0;j<keys_used.size();j++){
       
        map.get( keys_used.get(j));
      }
    }
   
    informRead( by_who );
   
    DHTDBValueImpl[]  v = new DHTDBValueImpl[res.size()];
   
    res.toArray( v );
   
    return( v );
  }
 
  protected DHTDBValueImpl
  get(
    DHTTransportContact   originator )
  {
      // local get
   
    if ( direct_originator_map_may_be_null == null ){
     
      return( null );
    }
   
    HashWrapper originator_id = new HashWrapper( originator.getID());
   
    DHTDBValueImpl  res = (DHTDBValueImpl)direct_originator_map_may_be_null.get( originator_id );
   
    return( res );
  }
 
  protected DHTDBValueImpl
  remove(
    DHTTransportContact   originator )
  {
      // local remove
   
    HashWrapper originator_id = new HashWrapper( originator.getID());
   
    DHTDBValueImpl  res = removeDirectValue( originator_id );
   
    return( res );
  }
   
 
  protected int
  getValueCount()
  {
    if ( direct_originator_map_may_be_null == null ){
     
      return( indirect_originator_value_map.size());
    }
   
    return( direct_originator_map_may_be_null.size() + indirect_originator_value_map.size());
  }
 
  protected int
  getDirectValueCount()
  {
    if ( direct_originator_map_may_be_null == null ){
     
      return( 0 );
    }
   
    return( direct_originator_map_may_be_null.size());
  }
 
  protected int
  getIndirectValueCount()
  {
    return( indirect_originator_value_map.size());
  }
 
  protected Iterator<DHTDBValueImpl>
  getValues()
  {
    return( new valueIterator( true, true ));
  }
 
  protected Iterator<DHTDBValueImpl>
  getDirectValues()
  {
    return( new valueIterator( true, false ));
  }
 
  protected Iterator<DHTDBValueImpl>
  getIndirectValues()
  {
    return( new valueIterator( false, true ));
  }
 
  protected byte
  getDiversificationType()
  {
    return( diversification_state );
  }
 
  protected void
  addDirectValue(
    HashWrapper    value_key,
    DHTDBValueImpl  value )
  {
    if ( direct_originator_map_may_be_null == null ){
     
      direct_originator_map_may_be_null = createLinkedMap();
    }
   
    DHTDBValueImpl  old = (DHTDBValueImpl)direct_originator_map_may_be_null.put( value_key, value );
       
    if ( old != null ){
     
      int  old_version = old.getVersion();
      int new_version = value.getVersion();
     
      if ( old_version != -1 && new_version != -1 && old_version >= new_version ){
       
        if ( old_version == new_version ){
     
          if ( TRACE_ADDS ){
            System.out.println( "addDirect[reset]:" + old.getString() + "/" + value.getString());
          }

          old.reset()// update store time as this means we don't need to republish
                  // as someone else has just done it
       
        }else{
         
            // its important to ignore old versions as a peer's increasing version sequence may
            // have been reset and if this is the case we want the "future" values to timeout
         
          if ( TRACE_ADDS ){
            System.out.println( "addDirect[ignore]:" + old.getString() + "/" + value.getString());
          }
        }
       
          // put the old value back!
       
        direct_originator_map_may_be_null.put( value_key, old );
       
        return;
      }
     
      if ( TRACE_ADDS ){
        System.out.println( "addDirect:" + old.getString() + "/" + value.getString());
      }
     
      direct_data_size -= old.getValue().length;
     
      if ( old.isLocal()){
       
        local_size -= old.getValue().length;
      }
    }else{
     
      if ( TRACE_ADDS ){
        System.out.println( "addDirect:[new]" +  value.getString());
      }
    }
   
    direct_data_size += value.getValue().length;
   
    if ( value.isLocal()){
     
      local_size += value.getValue().length;
    }
   
    if ( old == null ){
     
      informAdded( value );
     
    }else{
     
      informUpdated( old, value );
    }
  }
 
  protected DHTDBValueImpl
  removeDirectValue(
    HashWrapper    value_key )
  {
    if ( direct_originator_map_may_be_null == null ){
     
      return( null );
    }
   
    DHTDBValueImpl  old = (DHTDBValueImpl)direct_originator_map_may_be_null.remove( value_key );
   
    if ( old != null ){
     
      direct_data_size -= old.getValue().length;
     
      if ( old.isLocal()){
       
        local_size -= old.getValue().length;
      }
     
      informDeleted( old );
    }
   
    return( old );
  }

  protected void
  addIndirectValue(
    HashWrapper    value_key,
    DHTDBValueImpl  value )
  {
    DHTDBValueImpl  old = (DHTDBValueImpl)indirect_originator_value_map.put( value_key, value );
   
    if ( old != null ){
     
        // discard updates that are older than current value
     
      int  old_version = old.getVersion();
      int new_version = value.getVersion();
     
      if ( old_version != -1 && new_version != -1 && old_version >= new_version ){
       
        if ( old_version == new_version ){

          if ( TRACE_ADDS ){
            System.out.println( "addIndirect[reset]:" + old.getString() + "/" + value.getString());
          }

          old.reset()// update store time as this means we don't need to republish
                  // as someone else has just done it
       
        }else{
         
          if ( TRACE_ADDS ){
            System.out.println( "addIndirect[ignore]:" + old.getString() + "/" + value.getString());
          }
        }
       
          // put the old value back!
       
        indirect_originator_value_map.put( value_key, old );
       
        return;
      }
     
      // vague backwards compatability - if the creation date of the "new" value is significantly
      // less than the old then we ignore it (given that creation date is adjusted for time-skew you can
      // see the problem with this approach...)
     
      if ( old_version == -1 || new_version == -1 ){
       
        if ( old.getCreationTime() > value.getCreationTime() + 30000 ){
         
          if ( TRACE_ADDS ){
            System.out.println( "backward compat: ignoring store: " + old.getString() + "/" + value.getString());
          }
         
          // put the old value back!
         
          indirect_originator_value_map.put( value_key, old );

          return;
        }
      }
     
      if ( TRACE_ADDS ){
        System.out.println( "addIndirect:" + old.getString() + "/" + value.getString());
      }
   
      indirect_data_size -= old.getValue().length;
     
      if ( old.isLocal()){
       
        local_size -= old.getValue().length;
      }
    }else
      if ( TRACE_ADDS ){
        System.out.println( "addIndirect:[new]" +  value.getString());
      }
    }   
   
    indirect_data_size += value.getValue().length;
   
    if ( value.isLocal()){
     
      local_size += value.getValue().length;
    }
   
    if ( old == null ){
     
      informAdded( value );
     
    }else{
     
      informUpdated( old, value );
    }
  }
 
  protected DHTDBValueImpl
  removeIndirectValue(
    HashWrapper    value_key )
  {
    DHTDBValueImpl  old = (DHTDBValueImpl)indirect_originator_value_map.remove( value_key );
   
    if ( old != null ){
     
      indirect_data_size -= old.getValue().length;
     
      if ( old.isLocal()){
       
        local_size -= old.getValue().length;
      }
     
      informDeleted( old );
    }
   
    return( old );
  }
 
  protected void
  destroy()
  {
    try{
      if ( adapter_key != null ){
       
        Iterator<DHTDBValueImpl>  it = getValues();
       
        while( it.hasNext()){
         
          it.next();
         
          it.remove();
        }
       
        db.getAdapter().keyDeleted( adapter_key );
      }
     
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  private void
  informDeleted(
    DHTDBValueImpl    value )
  {
    boolean  direct =
      (!value.isLocal())&&   
      Arrays.equals( value.getOriginator().getID(), value.getSender().getID());
     
    if ( direct ){
       
      removeFromBloom( value );
    }
   
    try{
      if ( adapter_key != null ){
       
        db.getAdapter().valueDeleted( adapter_key, value );
       
        diversification_state  = adapter_key.getDiversificationType();
      }
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  private void
  informAdded
    DHTDBValueImpl    value )
  { 
    boolean  direct =
      (!value.isLocal()) &&    
      Arrays.equals( value.getOriginator().getID(), value.getSender().getID());
     
    if ( direct ){
       
      addToBloom( value );
    }

    try{
      if ( adapter_key != null ){
       
        db.getAdapter().valueAdded( adapter_key, value );
       
        diversification_state  = adapter_key.getDiversificationType();
      }
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  private void
  informUpdated(
    DHTDBValueImpl    old_value,
    DHTDBValueImpl    new_value)
  {
    boolean  old_direct =
      (!old_value.isLocal()) &&       
      Arrays.equals( old_value.getOriginator().getID(), old_value.getSender().getID());
 
    boolean  new_direct =
      (!new_value.isLocal()) &&     
      Arrays.equals( new_value.getOriginator().getID(), new_value.getSender().getID());
     
    if ( new_direct && !old_direct ){
     
      addToBloom( new_value );
    }
   
    try{
      if ( adapter_key != null ){
       
        db.getAdapter().valueUpdated( adapter_key, old_value, new_value );
       
        diversification_state  = adapter_key.getDiversificationType();
      }
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  private void
  informRead(
    DHTTransportContact    contact ){
   
    try{
      if ( adapter_key != null && contact != null ){
       
        db.getAdapter().keyRead( adapter_key, contact );
       
        diversification_state  = adapter_key.getDiversificationType();
      }
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  protected void
  addToBloom(
    DHTDBValueImpl  value )
  {
    // we don't check for flooding on indirect stores as this could be used to force a
    // direct store to be bounced (flood a node with indirect stores before the direct
    // store occurs)
 

    DHTTransportContact  originator = value.getOriginator();

    byte[] address_bytes = originator.getAddress().getAddress().getAddress();
   
    // System.out.println( "addToBloom: existing=" + ip_count_bloom_filter );

    if ( ip_count_bloom_filter == null ){
     
      ip_count_bloom_filter = address_bytes;
     
      return;
    }
   
    BloomFilter filter;
   
    if ( ip_count_bloom_filter instanceof byte[] ){
     
      byte[]  existing_address = (byte[])ip_count_bloom_filter;
     
      ip_count_bloom_filter = filter = BloomFilterFactory.createAddRemove4Bit( IP_COUNT_BLOOM_SIZE_INCREASE_CHUNK );
     
      filter.add( existing_address );
     
    }else{
     
      filter = (BloomFilter)ip_count_bloom_filter;
    }
               
    int  hit_count = filter.add( address_bytes );
   
    if ( DHTLog.LOCAL_BLOOM_TRACE ){
   
      System.out.println( "direct local add from " + originator.getAddress() + ", hit count = " + hit_count );
    }

      // allow up to 10% bloom filter utilisation
   
    if ( filter.getSize() / filter.getEntryCount() < 10 ){
     
      rebuildIPBloomFilter( true );
    }
   
    if ( hit_count >= 15 ){
   
      db.banContact( originator, "local flood on '" + DHTLog.getFullString( key.getBytes()) + "'" );
    }
  }
 
  protected void
  removeFromBloom(
    DHTDBValueImpl  value )
  {
    DHTTransportContact  originator = value.getOriginator();
   
    if ( ip_count_bloom_filter == null ){

      return;
    }
   
    byte[] address_bytes = originator.getAddress().getAddress().getAddress();

    if ( ip_count_bloom_filter instanceof byte[] ){

      byte[]  existing_address = (byte[])ip_count_bloom_filter;

      if ( Arrays.equals( address_bytes, existing_address )){
       
        ip_count_bloom_filter = null;
      }
     
      return;
    }
   
    BloomFilter filter = (BloomFilter)ip_count_bloom_filter;
   
    int  hit_count = filter.remove( address_bytes );
   
    if DHTLog.LOCAL_BLOOM_TRACE ){
   
      System.out.println( "direct local remove from " + originator.getAddress() + ", hit count = " + hit_count );
   
  }
 
  protected void
  rebuildIPBloomFilter(
    boolean  increase_size )
  {
    BloomFilter  new_filter;
   
    int  old_size;
   
    if ( ip_count_bloom_filter instanceof BloomFilter ){
     
      old_size = ((BloomFilter)ip_count_bloom_filter).getSize();
     
    }else{
     
      old_size = IP_COUNT_BLOOM_SIZE_INCREASE_CHUNK;
    }
   
    if ( increase_size ){
     
      new_filter = BloomFilterFactory.createAddRemove4Bit( old_size + IP_COUNT_BLOOM_SIZE_INCREASE_CHUNK );
     
    }else{
     
      new_filter = BloomFilterFactory.createAddRemove4Bit( old_size );
    }
   
    try{
        // only do flood prevention on direct stores as we can't trust the originator
        // details for indirect and this can be used to DOS a direct store later
     
      Iterator<DHTDBValueImpl>  it = getDirectValues();
     
      int  max_hits  = 0;
     
      while( it.hasNext()){
       
        DHTDBValueImpl  val = it.next();
       
        if ( !val.isLocal()){
         
          // logger.log( "    adding " + val.getOriginator().getAddress());
         
          int  hits = new_filter.add( val.getOriginator().getAddress().getAddress().getAddress());
 
          if ( hits > max_hits ){
           
            max_hits  = hits;
          }
        }
      }
     
      if DHTLog.LOCAL_BLOOM_TRACE ){

        db.log( "Rebuilt local IP bloom filter, size = " + new_filter.getSize() + ", entries =" + new_filter.getEntryCount()+", max hits = " + max_hits );
      }

    }finally{
     
      ip_count_bloom_filter = new_filter;
    }
  }
 
  protected void
  print()
  {
    int  entries;
   
    if ( ip_count_bloom_filter == null ){
     
      entries = 0;
     
    }else if ( ip_count_bloom_filter instanceof byte[] ){
     
      entries = 1;
     
    }else{
     
      entries = ((BloomFilter)ip_count_bloom_filter).getEntryCount();
    }
   
    System.out.println(
      ByteFormatter.encodeString( key.getBytes()) + ": " +
      "dir=" + (direct_originator_map_may_be_null==null?0:direct_originator_map_may_be_null.size()) + "," +
      "indir=" + indirect_originator_value_map.size() + "," +
      "bloom=" + entries )
   
    System.out.println( "    indirect" );
   
    Iterator<DHTDBValueImpl> it = getIndirectValues();
       
    while( it.hasNext()){
     
      DHTDBValueImpl val = (DHTDBValueImpl)it.next();
     
      System.out.println( "        " + val.getOriginator().getString() + ": " + new String( val.getValue()));
    }
  }
 
  protected class
  valueIterator
    implements Iterator<DHTDBValueImpl>
  {
    private List<Map<HashWrapper,DHTDBValueImpl>>  maps     =  new ArrayList<Map<HashWrapper,DHTDBValueImpl>>(2);
   
    private int    map_index   = 0;
   
    private Map<HashWrapper,DHTDBValueImpl>    map;
    private Iterator<DHTDBValueImpl>      it;
    private DHTDBValueImpl            value;
   
    protected
    valueIterator(
      boolean    direct,
      boolean    indirect )
    {
      if ( direct && direct_originator_map_may_be_null != null ){
        maps.add( direct_originator_map_may_be_null );
      }
     
      if ( indirect ){
        maps.add( indirect_originator_value_map );
      }
    }
   
    public boolean
    hasNext()
    {
      if ( it != null && it.hasNext()){
       
        return( true );
      }
     
      while( map_index < maps.size() ){
       
        map = maps.get(map_index++);
       
        it = map.values().iterator();
       
        if ( it.hasNext()){
         
          return( true );
        }
      }
     
      return( false );
    }
   
    public DHTDBValueImpl
    next()
    {
      if ( hasNext()){
     
        value = (DHTDBValueImpl)it.next();
       
        return( value );
      }
     
      throw( new NoSuchElementException());
    }
   
    public void
    remove()
    {
      if ( it == null ){
             
        throw( new IllegalStateException());
     
     
      if ( value != null ){
       
        if( value.isLocal()){
         
          local_size -= value.getValue().length;
        }
       
        if map == indirect_originator_value_map ){
       
          indirect_data_size -= value.getValue().length;
         
        }else{
         
          direct_data_size -= value.getValue().length;
        }
       
          // remove before informing
       
        it.remove();
       
        informDeleted( value );
       
        value = null;
       
      }else{
     
        throw( new IllegalStateException());
      }
    }
  }
 
  public static class
  ShortHash
  {
    private byte[]  bytes;
    private int    hash_code;
   
    protected
    ShortHash(
      byte[]    _bytes )
    {
      bytes  = _bytes;
     
      int  hc = 0;
           
      for (int i=0; i<DHTDBImpl.QUERY_STORE_REQUEST_ENTRY_SIZE; i++) {

        hc = 31*hc + bytes[i];
      }
     
      hash_code = hc;
    }
   
    public final boolean
    equals(
      Object o)
    {
      if( !( o instanceof ShortHash )){
       
        return false;
      }
     
      ShortHash other = (ShortHash)o;
           
      byte[]  other_hash     = other.bytes;
     
      for ( int i=0;i<DHTDBImpl.QUERY_STORE_REQUEST_ENTRY_SIZE;i++){
       
        if ( bytes[i] != other_hash[i] ){
         
          return( false );
        }
      }
     
      return( true );
    }

    public int  
    hashCode()
    {
      return( hash_code );
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.dht.db.impl.DHTDBMapping

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.