/*
 * Copyright (C) 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.collect;

import com.google.common.base.Function;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.ReentrantLock;

import javax.annotation.Nullable;

/**
 * A framework for concurrent hash map implementations. The
 * CustomConcurrentHashMap class itself is not extensible and does not contain
 * any methods. Use {@link Builder} to create a custom concurrent hash map
 * instance. Client libraries implement {@link Strategy}, and this class
 * provides the surrounding concurrent data structure which implements {@link
 * ConcurrentMap}. Additionally supports implementing maps where {@link
 * Map#get} atomically computes values on demand (see {@link
 * Builder#buildComputingMap(CustomConcurrentHashMap.ComputingStrategy,
 * Function)}).
 *
 * <p>The resulting hash table supports full concurrency of retrievals and
 * adjustable expected concurrency for updates. Even though all operations are
 * thread-safe, retrieval operations do <i>not</i> entail locking,
 * and there is <i>not</i> any support for locking the entire table
 * in a way that prevents all access.
 *
 * <p>Retrieval operations (including {@link Map#get}) generally do not
 * block, so may overlap with update operations (including
 * {@link Map#put} and {@link Map#remove}). Retrievals reflect the results
 * of the most recently <i>completed</i> update operations holding
 * upon their onset. For aggregate operations such as {@link Map#putAll}
 * and {@link Map#clear}, concurrent retrievals may reflect insertion or
 * removal of only some entries. Similarly, iterators return elements
 * reflecting the state of the hash table at some point at or since the
 * creation of the iterator. They do <i>not</i> throw
 * {@link java.util.ConcurrentModificationException}. However, iterators can
 * only be used by one thread at a time.
 *
 * <p>The resulting {@link ConcurrentMap} and its views and iterators implement
 * all of the <i>optional</i> methods of the {@link java.util.Map} and {@link
 * java.util.Iterator} interfaces. Partially reclaimed entries are never
 * exposed through the views or iterators.
 *
 * <p>For example, the following strategy emulates the behavior of
 * {@link java.util.concurrent.ConcurrentHashMap}:
 *
 * <pre> {@code
 * class ConcurrentHashMapStrategy<K, V>
 *     implements CustomConcurrentHashMap.Strategy<K, V,
 *     InternalEntry<K, V>>, Serializable {
 *   public InternalEntry<K, V> newEntry(K key, int hash,
 *       InternalEntry<K, V> next) {
 *     return new InternalEntry<K, V>(key, hash, null, next);
 *   }
 *   public InternalEntry<K, V> copyEntry(K key,
 *       InternalEntry<K, V> original, InternalEntry<K, V> next) {
 *     return new InternalEntry<K, V>(key, original.hash, original.value, next);
 *   }
 *   public void setValue(InternalEntry<K, V> entry, V value) {
 *     entry.value = value;
 *   }
 *   public V getValue(InternalEntry<K, V> entry) { return entry.value; }
 *   public boolean equalKeys(K a, Object b) { return a.equals(b); }
 *   public boolean equalValues(V a, Object b) { return a.equals(b); }
 *   public int hashKey(Object key) { return key.hashCode(); }
 *   public K getKey(InternalEntry<K, V> entry) { return entry.key; }
 *   public InternalEntry<K, V> getNext(InternalEntry<K, V> entry) {
 *     return entry.next;
 *   }
 *   public int getHash(InternalEntry<K, V> entry) { return entry.hash; }
 *   public void setInternals(CustomConcurrentHashMap.Internals<K, V,
 *       InternalEntry<K, V>> internals) {} // ignored
 * }
 *
 * class InternalEntry<K, V> {
 *   final K key;
 *   final int hash;
 *   volatile V value;
 *   final InternalEntry<K, V> next;
 *   InternalEntry(K key, int hash, V value, InternalEntry<K, V> next) {
 *     this.key = key;
 *     this.hash = hash;
 *     this.value = value;
 *     this.next = next;
 *   }
 * }
 * }</pre>
 *
 * To create a {@link java.util.concurrent.ConcurrentMap} using the strategy
 * above:
 *
 * <pre>{@code
 *   ConcurrentMap<K, V> map = new CustomConcurrentHashMap.Builder()
 *       .build(new ConcurrentHashMapStrategy<K, V>());
 * }</pre>
 *
 * @author Bob Lee
 * @author Doug Lea
 */
final class CustomConcurrentHashMap {

  /** Prevents instantiation. */
  private CustomConcurrentHashMap() {}

  /**
   * Builds a custom concurrent hash map.
   */
  static final class Builder {
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
    
    private static final int UNSET_INITIAL_CAPACITY = -1;
    private static final int UNSET_CONCURRENCY_LEVEL = -1;
    
    int initialCapacity = UNSET_INITIAL_CAPACITY;
    int concurrencyLevel = UNSET_CONCURRENCY_LEVEL;

    /**
     * Sets a custom initial capacity (defaults to 16). Resizing this or any
     * other kind of hash table is a relatively slow operation, so, when
     * possible, it is a good idea to provide estimates of expected table
     * sizes.
     *
     * @throws IllegalArgumentException if initialCapacity < 0
     */
    public Builder initialCapacity(int initialCapacity) {
      if (this.initialCapacity != UNSET_INITIAL_CAPACITY) {
        throw new IllegalStateException(
            "initial capacity was already set to " + this.initialCapacity);
      }
      if (initialCapacity < 0) {
        throw new IllegalArgumentException();
      }
      this.initialCapacity = initialCapacity;
      return this;
    }

    /**
     * Guides the allowed concurrency among update operations. Used as a
     * hint for internal sizing. The table is internally partitioned to try to
     * permit the indicated number of concurrent updates without contention.
     * Because placement in hash tables is essentially random, the actual
     * concurrency will vary. Ideally, you should choose a value to accommodate
     * as many threads as will ever concurrently modify the table. Using a
     * significantly higher value than you need can waste space and time,
     * and a significantly lower value can lead to thread contention. But
     * overestimates and underestimates within an order of magnitude do
     * not usually have much noticeable impact. A value of one is
     * appropriate when it is known that only one thread will modify and
     * all others will only read. Defaults to {@literal 16}.
     *
     * @throws IllegalArgumentException if concurrencyLevel < 0
     */
    public Builder concurrencyLevel(int concurrencyLevel) {
      if (this.concurrencyLevel != UNSET_CONCURRENCY_LEVEL) {
        throw new IllegalStateException(
            "concurrency level was already set to " + this.concurrencyLevel);
      }
      if (concurrencyLevel <= 0) {
        throw new IllegalArgumentException();
      }
      this.concurrencyLevel = concurrencyLevel;
      return this;
    }

    /**
     * Creates a new concurrent hash map backed by the given strategy.
     *
     * @param strategy used to implement and manipulate the entries
     *
     * @param <K> the type of keys to be stored in the returned map
     * @param <V> the type of values to be stored in the returned map
     * @param <E> the type of internal entry to be stored in the returned map
     *
     * @throws NullPointerException if strategy is null
     */
    public <K, V, E> ConcurrentMap<K, V> buildMap(Strategy<K, V, E> strategy) {
      if (strategy == null) {
        throw new NullPointerException("strategy");
      }
      return new Impl<K, V, E>(strategy, this);
    }

    /**
     * Creates a {@link ConcurrentMap}, backed by the given strategy, that
     * supports atomic, on-demand computation of values. {@link Map#get}
     * returns the value corresponding to the given key, atomically computes
     * it using the computer function passed to this builder, or waits for
     * another thread to compute the value if necessary. Only one value will
     * be computed for each key at a given time.
     *
     * <p>If an entry's value has not finished computing yet, query methods
     * besides {@link java.util.Map#get} return immediately as if an entry
     * doesn't exist. In other words, an entry isn't externally visible until
     * the value's computation completes.
     *
     * <p>{@link Map#get} in the returned map implementation throws:
     * <ul>
     * <li>{@link NullPointerException} if the key is null or the
     *  computer returns null</li>
     * <li>or {@link ComputationException} wrapping an exception thrown by the
     *  computation</li>
     * </ul>
     *
     * <p><b>Note:</b> Callers of {@code get()} <i>must</i> ensure that the key
     *  argument is of type {@code K}. {@code Map.get()} takes {@code Object},
     *  so the key type is not checked at compile time. Passing an object of
     *  a type other than {@code K} can result in that object being unsafely
     *  passed to the computer function as type {@code K} not to mention the
     *  unsafe key being stored in the map.
     *
     * @param strategy used to implement and manipulate the entries
     * @param computer used to compute values for keys
     *
     * @param <K> the type of keys to be stored in the returned map
     * @param <V> the type of values to be stored in the returned map
     * @param <E> the type of internal entry to be stored in the returned map
     *
     * @throws NullPointerException if strategy or computer is null
     */
    public <K, V, E> ConcurrentMap<K, V> buildComputingMap(
        ComputingStrategy<K, V, E> strategy,
        Function<? super K, ? extends V> computer) {
      if (strategy == null) {
        throw new NullPointerException("strategy");
      }
      if (computer == null) {
        throw new NullPointerException("computer");
      }

      return new ComputingImpl<K, V, E>(strategy, this, computer);
    }
    
    int getInitialCapacity() {
      return (initialCapacity == UNSET_INITIAL_CAPACITY)
          ? DEFAULT_INITIAL_CAPACITY : initialCapacity;
    }

    int getConcurrencyLevel() {
      return (concurrencyLevel == UNSET_CONCURRENCY_LEVEL)
          ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel;
    }
  }

  /**
   * Implements behavior specific to the client's concurrent hash map
   * implementation. Used by the framework to create new entries and perform
   * operations on them.
   *
   * <p>Method parameters are never null unless otherwise specified.
   *
   * <h3>Partially Reclaimed Entries</h3>
   *
   * <p>This class does <i>not</i> allow {@code null} to be used as a key.
   * Setting values to null is not permitted, but entries may have null keys
   * or values for various reasons. For example, the key or value may have
   * been garbage collected or reclaimed through other means.
   * CustomConcurrentHashMap treats entries with null keys and values as
   * "partially reclaimed" and ignores them for the most part. Entries may
   * enter a partially reclaimed state but they must not leave it. Partially
   * reclaimed entries will not be copied over during table expansions, for
   * example. Strategy implementations should proactively remove partially
   * reclaimed entries so that {@link Map#isEmpty} and {@link Map#size()}
   * return up-to-date results.
   *
   * @param <K> the type of keys to be stored in the returned map
   * @param <V> the type of values to be stored in the returned map
   * @param <E> internal entry type, not directly exposed to clients in map
   *  views
   */
  public interface Strategy<K, V, E> {

    /**
     * Constructs a new entry for the given key with a pointer to the given
     * next entry.
     *
     * <p>This method may return different entry implementations
     * depending upon whether next is null or not. For example, if next is
     * null (as will often be the case), this factory might use an entry
     * class that doesn't waste memory on an unnecessary field.
     *
     * @param key for this entry
     * @param hash of key returned by {@link #hashKey}
     * @param next entry (used when implementing a hash bucket as a linked
     *  list, for example), possibly null
     * @return a new entry
     */
    abstract E newEntry(K key, int hash, E next);

    /**
     * Creates a copy of the given entry pointing to the given next entry.
     * Copies the value and any other implementation-specific state from
     * {@code original} to the returned entry. For example,
     * CustomConcurrentHashMap might use this method when removing other
     * entries or expanding the internal table.
     *
     * @param key for {@code original} as well as the returned entry.
     *  Explicitly provided here to prevent reclamation of the key at an
     *  inopportune time in implementations that don't otherwise keep
     *  a strong reference to the key.
     * @param original entry from which the value and other
     *  implementation-specific state should be copied
     * @param newNext the next entry the new entry should point to, possibly
     *  null
     */
    E copyEntry(K key, E original, E newNext);

    /**
     * Sets the value of an entry using volatile semantics. Values are set
     * synchronously on a per-entry basis.
     *
     * @param entry to set the value on
     * @param value to set
     */
    void setValue(E entry, V value);

    /**
     * Gets the value of an entry using volatile semantics.
     *
     * @param entry to get the value from
     */
    V getValue(E entry);

    /**
     * Returns true if the two given keys are equal, false otherwise. Neither
     * key will be null.
     *
     * @param a key from inside the map
     * @param b key passed from caller, not necesarily of type K
     *
     * @see Object#equals the same contractual obligations apply here
     */
    boolean equalKeys(K a, Object b);

    /**
     * Returns true if the two given values are equal, false otherwise. Neither
     * value will be null.
     *
     * @param a value from inside the map
     * @param b value passed from caller, not necesarily of type V
     *
     * @see Object#equals the same contractual obligations apply here
     */
    boolean equalValues(V a, Object b);

    /**
     * Returns a hash code for the given key.
     *
     * @see Object#hashCode the same contractual obligations apply here
     */
    int hashKey(Object key);

    /**
     * Gets the key for the given entry. This may return null (for example,
     * if the key was reclaimed by the garbage collector).
     *
     * @param entry to get key from
     * @return key from the given entry
     */
    K getKey(E entry);

    /**
     * Gets the next entry relative to the given entry, the exact same entry
     * that was provided to {@link Strategy#newEntry} when the given entry was
     * created.
     *
     * @return the next entry or null if null was passed to
     *  {@link Strategy#newEntry}
     */
    E getNext(E entry);

    /**
     * Returns the hash code that was passed to {@link Strategy#newEntry})
     * when the given entry was created.
     */
    int getHash(E entry);

// TODO:
//    /**
//     * Notifies the strategy that an entry has been removed from the map.
//     *
//     * @param entry that was recently removed
//     */
//    void remove(E entry);

    /**
     * Provides an API for interacting directly with the map's internal
     * entries to this strategy. Invoked once when the map is created.
     * Strategies that don't need access to the map's internal entries
     * can simply ignore the argument.
     *
     * @param internals of the map, enables direct interaction with the
     *  internal entries
     */
    void setInternals(Internals<K, V, E> internals);
  }

  /**
   * Provides access to a map's internal entries.
   */
  public interface Internals<K, V, E> {

// TODO:
//    /**
//     * Returns a set view of the internal entries.
//     */
//    Set<E> entrySet();

    /**
     * Returns the internal entry corresponding to the given key from the map.
     *
     * @param key to retrieve entry for
     *
     * @throws NullPointerException if key is null
     */
    E getEntry(K key);

    /**
     * Removes the given entry from the map if the value of the entry in the
     * map matches the given value.
     *
     * @param entry to remove
     * @param value entry must have for the removal to succeed
     *
     * @throws NullPointerException if entry is null
     */
    boolean removeEntry(E entry, @Nullable V value);

    /**
     * Removes the given entry from the map.
     *
     * @param entry to remove
     *
     * @throws NullPointerException if entry is null
     */
    boolean removeEntry(E entry);
  }

  /**
   * Extends {@link Strategy} to add support for computing values on-demand.
   * Implementations should typically intialize the entry's value to a
   * placeholder value in {@link #newEntry(Object, int, Object)} so that
   * {@link #waitForValue(Object)} can tell the difference between a
   * pre-intialized value and an in-progress computation. {@link
   * #copyEntry(Object, Object, Object)} must detect and handle the case where
   * an entry is copied in the middle of a computation. Implementations can
   * throw {@link UnsupportedOperationException} in {@link #setValue(Object,
   * Object)} if they wish to prevent users from setting values directly.
   *
   * @see Builder#buildComputingMap(CustomConcurrentHashMap.ComputingStrategy,
   *     Function)
   */
  public interface ComputingStrategy<K, V, E> extends Strategy<K, V, E> {

    /**
     * Computes a value for the given key and stores it in the given entry.
     * Called as a result of {@link Map#get}. If this method throws an
     * exception, CustomConcurrentHashMap will remove the entry and retry
     * the computation on subsequent requests.
     *
     * @param entry that was created
     * @param computer passed to {@link Builder#buildMap}
     *
     * @throws ComputationException if the computation threw an exception
     * @throws NullPointerException if the computer returned null
     *
     * @return the computed value
     */
    V compute(K key, E entry, Function<? super K, ? extends V> computer);

    /**
     * Gets a value from an entry waiting for the value to be set by {@link
     * #compute} if necessary. Returns null if a value isn't available at
     * which point CustomConcurrentHashMap tries to compute a new value.
     *
     * @param entry to return value from
     * @return stored value or null if the value isn't available
     *
     * @throws InterruptedException if the thread was interrupted while
     *  waiting
     */
    V waitForValue(E entry) throws InterruptedException;
  }

  /**
   * Applies a supplemental hash function to a given hash code, which defends
   * against poor quality hash functions. This is critical when the
   * concurrent hash map uses power-of-two length hash tables, that otherwise
   * encounter collisions for hash codes that do not differ in lower or upper
   * bits.
   *
   * @param h hash code
   */
  private static int rehash(int h) {
    // Spread bits to regularize both segment and index locations,
    // using variant of single-word Wang/Jenkins hash.
    h += (h << 15) ^ 0xffffcd7d;
    h ^= (h >>> 10);
    h += (h << 3);
    h ^= (h >>> 6);
    h += (h << 2) + (h << 14);
    return h ^ (h >>> 16);
  }

  /** The concurrent hash map implementation. */
  static class Impl<K, V, E> extends AbstractMap<K, V>
      implements ConcurrentMap<K, V>, Serializable {

    /*
     * The basic strategy is to subdivide the table among Segments,
     * each of which itself is a concurrently readable hash table.
     */

    /* ---------------- Constants -------------- */

    /**
     * The maximum capacity, used if a higher value is implicitly specified by
     * either of the constructors with arguments.  MUST be a power of two <=
     * 1<<30 to ensure that entries are indexable using ints.
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * The maximum number of segments to allow; used to bound constructor
     * arguments.
     */
    static final int MAX_SEGMENTS = 1 << 16; // slightly conservative

    /**
     * Number of unsynchronized retries in size and containsValue methods before
     * resorting to locking. This is used to avoid unbounded retries if tables
     * undergo continuous modification which would make it impossible to obtain
     * an accurate result.
     */
    static final int RETRIES_BEFORE_LOCK = 2;

    /* ---------------- Fields -------------- */

    /**
     * The strategy used to implement this map.
     */
    final Strategy<K, V, E> strategy;

    /**
     * Mask value for indexing into segments. The upper bits of a key's hash
     * code are used to choose the segment.
     */
    final int segmentMask;

    /**
     * Shift value for indexing within segments. Helps prevent entries that
     * end up in the same segment from also ending up in the same bucket.
     */
    final int segmentShift;

    /**
     * The segments, each of which is a specialized hash table
     */
    final Segment[] segments;

    /**
     * Creates a new, empty map with the specified strategy, initial capacity,
     * load factor and concurrency level.
     */
    Impl(Strategy<K, V, E> strategy, Builder builder) {
      int concurrencyLevel = builder.getConcurrencyLevel();
      int initialCapacity = builder.getInitialCapacity();

      if (concurrencyLevel > MAX_SEGMENTS) {
        concurrencyLevel = MAX_SEGMENTS;
      }

      // Find power-of-two sizes best matching arguments
      int segmentShift = 0;
      int segmentCount = 1;
      while (segmentCount < concurrencyLevel) {
        ++segmentShift;
        segmentCount <<= 1;
      }
      this.segmentShift = 32 - segmentShift;
      segmentMask = segmentCount - 1;
      this.segments = newSegmentArray(segmentCount);

      if (initialCapacity > MAXIMUM_CAPACITY) {
        initialCapacity = MAXIMUM_CAPACITY;
      }

      int segmentCapacity = initialCapacity / segmentCount;
      if (segmentCapacity * segmentCount < initialCapacity) {
        ++segmentCapacity;
      }

      int segmentSize = 1;
      while (segmentSize < segmentCapacity) {
          segmentSize <<= 1;
      }
      for (int i = 0; i < this.segments.length; ++i) {
        this.segments[i] = new Segment(segmentSize);
      }

      this.strategy = strategy;

      strategy.setInternals(new InternalsImpl());
    }

    int hash(Object key) {
      int h = strategy.hashKey(key);
      return rehash(h);
    }

    class InternalsImpl implements Internals<K, V, E>, Serializable {

      static final long serialVersionUID = 0;

      public E getEntry(K key) {
        if (key == null) {
          throw new NullPointerException("key");
        }
        int hash = hash(key);
        return segmentFor(hash).getEntry(key, hash);
      }

      public boolean removeEntry(E entry, V value) {
        if (entry == null) {
          throw new NullPointerException("entry");
        }
        int hash = strategy.getHash(entry);
        return segmentFor(hash).removeEntry(entry, hash, value);
      }

      public boolean removeEntry(E entry) {
        if (entry == null) {
          throw new NullPointerException("entry");
        }
        int hash = strategy.getHash(entry);
        return segmentFor(hash).removeEntry(entry, hash);
      }
    }

    @SuppressWarnings("unchecked")
    Segment[] newSegmentArray(int ssize) {
      // Note: This is the only way I could figure out how to create
      // a segment array (the compile has a tough time with arrays of
      // inner classes of generic types apparently). Safe because we're
      // restricting what can go in the array and no one has an
      // unrestricted reference.
      return (Segment[]) Array.newInstance(Segment.class, ssize);
    }

    /* ---------------- Small Utilities -------------- */

    /**
     * Returns the segment that should be used for key with given hash
     *
     * @param hash the hash code for the key
     * @return the segment
     */
    Segment segmentFor(int hash) {
      return segments[(hash >>> segmentShift) & segmentMask];
    }

    /* ---------------- Inner Classes -------------- */

    /**
     * Segments are specialized versions of hash tables.  This subclasses from
     * ReentrantLock opportunistically, just to simplify some locking and avoid
     * separate construction.
     */
    @SuppressWarnings("serial") // This class is never serialized.
    final class Segment extends ReentrantLock {

      /*
       * Segments maintain a table of entry lists that are ALWAYS
       * kept in a consistent state, so can be read without locking.
       * Next fields of nodes are immutable (final).  All list
       * additions are performed at the front of each bin. This
       * makes it easy to check changes, and also fast to traverse.
       * When nodes would otherwise be changed, new nodes are
       * created to replace them. This works well for hash tables
       * since the bin lists tend to be short. (The average length
       * is less than two for the default load factor threshold.)
       *
       * Read operations can thus proceed without locking, but rely
       * on selected uses of volatiles to ensure that completed
       * write operations performed by other threads are
       * noticed. For most purposes, the "count" field, tracking the
       * number of elements, serves as that volatile variable
       * ensuring visibility.  This is convenient because this field
       * needs to be read in many read operations anyway:
       *
       *   - All (unsynchronized) read operations must first read the
       *     "count" field, and should not look at table entries if
       *     it is 0.
       *
       *   - All (synchronized) write operations should write to
       *     the "count" field after structurally changing any bin.
       *     The operations must not take any action that could even
       *     momentarily cause a concurrent read operation to see
       *     inconsistent data. This is made easier by the nature of
       *     the read operations in Map. For example, no operation
       *     can reveal that the table has grown but the threshold
       *     has not yet been updated, so there are no atomicity
       *     requirements for this with respect to reads.
       *
       * As a guide, all critical volatile reads and writes to the
       * count field are marked in code comments.
       */

      /**
       * The number of elements in this segment's region.
       */
      volatile int count;

      /**
       * Number of updates that alter the size of the table. This is used
       * during bulk-read methods to make sure they see a consistent snapshot:
       * If modCounts change during a traversal of segments computing size or
       * checking containsValue, then we might have an inconsistent view of
       * state so (usually) must retry.
       */
      int modCount;

      /**
       * The table is expanded when its size exceeds this threshold. (The
       * value of this field is always {@code (int)(capacity * loadFactor)}.)
       */
      int threshold;

      /**
       * The per-segment table.
       */
      volatile AtomicReferenceArray<E> table;

      Segment(int initialCapacity) {
        setTable(newEntryArray(initialCapacity));
      }

      AtomicReferenceArray<E> newEntryArray(int size) {
        return new AtomicReferenceArray<E>(size);
      }

      /**
       * Sets table to new HashEntry array. Call only while holding lock or in
       * constructor.
       */
      void setTable(AtomicReferenceArray<E> newTable) {
        this.threshold = newTable.length() * 3 / 4;
        this.table = newTable;
      }

      /**
       * Returns properly casted first entry of bin for given hash.
       */
      E getFirst(int hash) {
        AtomicReferenceArray<E> table = this.table;
        return table.get(hash & (table.length() - 1));
      }

      /* Specialized implementations of map methods */

      public E getEntry(Object key, int hash) {
        Strategy<K, V, E> s = Impl.this.strategy;
        if (count != 0) { // read-volatile
          for (E e = getFirst(hash); e != null; e = s.getNext(e)) {
            if (s.getHash(e) != hash) {
              continue;
            }

            K entryKey = s.getKey(e);
            if (entryKey == null) {
              continue;
            }

            if (s.equalKeys(entryKey, key)) {
              return e;
            }
          }
        }

        return null;
      }

      V get(Object key, int hash) {
        E entry = getEntry(key, hash);
        if (entry == null) {
          return null;
        }

        return strategy.getValue(entry);
      }

      boolean containsKey(Object key, int hash) {
        Strategy<K, V, E> s = Impl.this.strategy;
        if (count != 0) { // read-volatile
          for (E e = getFirst(hash); e != null; e = s.getNext(e)) {
            if (s.getHash(e) != hash) {
              continue;
            }

            K entryKey = s.getKey(e);
            if (entryKey == null) {
              continue;
            }

            if (s.equalKeys(entryKey, key)) {
              // Return true only if this entry has a value.
              return s.getValue(e) != null;
            }
          }
        }

        return false;
      }

      boolean containsValue(Object value) {
        Strategy<K, V, E> s = Impl.this.strategy;
        if (count != 0) { // read-volatile
          AtomicReferenceArray<E> table = this.table;
          int length = table.length();
          for (int i = 0; i < length; i++) {
            for (E e = table.get(i); e != null; e = s.getNext(e)) {
              V entryValue = s.getValue(e);

              // If the value disappeared, this entry is partially collected,
              // and we should skip it.
              if (entryValue == null) {
                continue;
              }

              if (s.equalValues(entryValue, value)) {
                return true;
              }
            }
          }
        }

        return false;
      }

      boolean replace(K key, int hash, V oldValue, V newValue) {
        Strategy<K, V, E> s = Impl.this.strategy;
        lock();
        try {
          for (E e = getFirst(hash); e != null; e = s.getNext(e)) {
            K entryKey = s.getKey(e);
            if (s.getHash(e) == hash && entryKey != null
                && s.equalKeys(key, entryKey)) {
              // If the value disappeared, this entry is partially collected,
              // and we should pretend like it doesn't exist.
              V entryValue = s.getValue(e);
              if (entryValue == null) {
                return false;
              }

              if (s.equalValues(entryValue, oldValue)) {
                s.setValue(e, newValue);
                return true;
              }
            }
          }

          return false;
        } finally {
          unlock();
        }
      }

      V replace(K key, int hash, V newValue) {
        Strategy<K, V, E> s = Impl.this.strategy;
        lock();
        try {
          for (E e = getFirst(hash); e != null; e = s.getNext(e)) {
            K entryKey = s.getKey(e);
            if (s.getHash(e) == hash && entryKey != null
                && s.equalKeys(key, entryKey)) {
              // If the value disappeared, this entry is partially collected,
              // and we should pretend like it doesn't exist.
              V entryValue = s.getValue(e);
              if (entryValue == null) {
                return null;
              }

              s.setValue(e, newValue);
              return entryValue;
            }
          }

          return null;
        } finally {
          unlock();
        }
      }

      V put(K key, int hash, V value, boolean onlyIfAbsent) {
        Strategy<K, V, E> s = Impl.this.strategy;
        lock();
        try {
          int count = this.count;
          if (count++ > this.threshold) { // ensure capacity
            expand();
          }

          AtomicReferenceArray<E> table = this.table;
          int index = hash & (table.length() - 1);

          E first = table.get(index);

          // Look for an existing entry.
          for (E e = first; e != null; e = s.getNext(e)) {
            K entryKey = s.getKey(e);
            if (s.getHash(e) == hash && entryKey != null
                && s.equalKeys(key, entryKey)) {
              // We found an existing entry.

              // If the value disappeared, this entry is partially collected,
              // and we should pretend like it doesn't exist.
              V entryValue = s.getValue(e);
              if (onlyIfAbsent && entryValue != null) {
                return entryValue;
              }

              s.setValue(e, value);
              return entryValue;
            }
          }

          // Create a new entry.
          ++modCount;
          E newEntry = s.newEntry(key, hash, first);
          s.setValue(newEntry, value);
          table.set(index, newEntry);
          this.count = count; // write-volatile
          return null;
        } finally {
          unlock();
        }
      }

      /**
       * Expands the table if possible.
       */
      void expand() {
        AtomicReferenceArray<E> oldTable = table;
        int oldCapacity = oldTable.length();
        if (oldCapacity >= MAXIMUM_CAPACITY) {
          return;
        }

        /*
         * Reclassify nodes in each list to new Map.  Because we are
         * using power-of-two expansion, the elements from each bin
         * must either stay at same index, or move with a power of two
         * offset. We eliminate unnecessary node creation by catching
         * cases where old nodes can be reused because their next
         * fields won't change. Statistically, at the default
         * threshold, only about one-sixth of them need cloning when
         * a table doubles. The nodes they replace will be garbage
         * collectable as soon as they are no longer referenced by any
         * reader thread that may be in the midst of traversing table
         * right now.
         */

        Strategy<K, V, E> s = Impl.this.strategy;
        AtomicReferenceArray<E> newTable = newEntryArray(oldCapacity << 1);
        threshold = newTable.length() * 3 / 4;
        int newMask = newTable.length() - 1;
        for (int oldIndex = 0; oldIndex < oldCapacity; oldIndex++) {
          // We need to guarantee that any existing reads of old Map can
          //  proceed. So we cannot yet null out each bin.
          E head = oldTable.get(oldIndex);

          if (head != null) {
            E next = s.getNext(head);
            int headIndex = s.getHash(head) & newMask;

            // Single node on list
            if (next == null) {
              newTable.set(headIndex, head);
            } else {
              // Reuse the consecutive sequence of nodes with the same target
              // index from the end of the list. tail points to the first
              // entry in the reusable list.
              E tail = head;
              int tailIndex = headIndex;
              for (E last = next; last != null; last = s.getNext(last)) {
                int newIndex = s.getHash(last) & newMask;
                if (newIndex != tailIndex) {
                  // The index changed. We'll need to copy the previous entry.
                  tailIndex = newIndex;
                  tail = last;
                }
              }
              newTable.set(tailIndex, tail);

              // Clone nodes leading up to the tail.
              for (E e = head; e != tail; e = s.getNext(e)) {
                K key = s.getKey(e);
                if (key != null) {
                  int newIndex = s.getHash(e) & newMask;
                  E newNext = newTable.get(newIndex);
                  newTable.set(newIndex, s.copyEntry(key, e, newNext));
                } else {
                  // Key was reclaimed. Skip entry.
                }
              }
            }
          }
        }
        table = newTable;
      }

      V remove(Object key, int hash) {
        Strategy<K, V, E> s = Impl.this.strategy;
        lock();
        try {
          int count = this.count - 1;
          AtomicReferenceArray<E> table = this.table;
          int index = hash & (table.length() - 1);
          E first = table.get(index);

          for (E e = first; e != null; e = s.getNext(e)) {
            K entryKey = s.getKey(e);
            if (s.getHash(e) == hash && entryKey != null
                && s.equalKeys(entryKey, key)) {
              V entryValue = strategy.getValue(e);
              // All entries following removed node can stay
              // in list, but all preceding ones need to be
              // cloned.
              ++modCount;
              E newFirst = s.getNext(e);
              for (E p = first; p != e; p = s.getNext(p)) {
                K pKey = s.getKey(p);
                if (pKey != null) {
                  newFirst = s.copyEntry(pKey, p, newFirst);
                } else {
                  // Key was reclaimed. Skip entry.
                }
              }
              table.set(index, newFirst);
              this.count = count; // write-volatile
              return entryValue;
            }
          }

          return null;
        } finally {
          unlock();
        }
      }

      boolean remove(Object key, int hash, Object value) {
        Strategy<K, V, E> s = Impl.this.strategy;
        lock();
        try {
          int count = this.count - 1;
          AtomicReferenceArray<E> table = this.table;
          int index = hash & (table.length() - 1);
          E first = table.get(index);

          for (E e = first; e != null; e = s.getNext(e)) {
            K entryKey = s.getKey(e);
            if (s.getHash(e) == hash && entryKey != null
                && s.equalKeys(entryKey, key)) {
              V entryValue = strategy.getValue(e);
              if (value == entryValue || (value != null && entryValue != null
                  && s.equalValues(entryValue, value))) {
                // All entries following removed node can stay
                // in list, but all preceding ones need to be
                // cloned.
                ++modCount;
                E newFirst = s.getNext(e);
                for (E p = first; p != e; p = s.getNext(p)) {
                  K pKey = s.getKey(p);
                  if (pKey != null) {
                    newFirst = s.copyEntry(pKey, p, newFirst);
                  } else {
                    // Key was reclaimed. Skip entry.
                  }
                }
                table.set(index, newFirst);
                this.count = count; // write-volatile
                return true;
              } else {
                return false;
              }
            }
          }

          return false;
        } finally {
          unlock();
        }
      }

      public boolean removeEntry(E entry, int hash, V value) {
        Strategy<K, V, E> s = Impl.this.strategy;
        lock();
        try {
          int count = this.count - 1;
          AtomicReferenceArray<E> table = this.table;
          int index = hash & (table.length() - 1);
          E first = table.get(index);

          for (E e = first; e != null; e = s.getNext(e)) {
            if (s.getHash(e) == hash && entry.equals(e)) {
              V entryValue = s.getValue(e);
              if (entryValue == value || (value != null
                  && s.equalValues(entryValue, value))) {
                // All entries following removed node can stay
                // in list, but all preceding ones need to be
                // cloned.
                ++modCount;
                E newFirst = s.getNext(e);
                for (E p = first; p != e; p = s.getNext(p)) {
                  K pKey = s.getKey(p);
                  if (pKey != null) {
                    newFirst = s.copyEntry(pKey, p, newFirst);
                  } else {
                    // Key was reclaimed. Skip entry.
                  }
                }
                table.set(index, newFirst);
                this.count = count; // write-volatile
                return true;
              } else {
                return false;
              }
            }
          }

          return false;
        } finally {
          unlock();
        }
      }

      public boolean removeEntry(E entry, int hash) {
        Strategy<K, V, E> s = Impl.this.strategy;
        lock();
        try {
          int count = this.count - 1;
          AtomicReferenceArray<E> table = this.table;
          int index = hash & (table.length() - 1);
          E first = table.get(index);

          for (E e = first; e != null; e = s.getNext(e)) {
            if (s.getHash(e) == hash && entry.equals(e)) {
              // All entries following removed node can stay
              // in list, but all preceding ones need to be
              // cloned.
              ++modCount;
              E newFirst = s.getNext(e);
              for (E p = first; p != e; p = s.getNext(p)) {
                K pKey = s.getKey(p);
                if (pKey != null) {
                  newFirst = s.copyEntry(pKey, p, newFirst);
                } else {
                  // Key was reclaimed. Skip entry.
                }
              }
              table.set(index, newFirst);
              this.count = count; // write-volatile
              return true;
            }
          }

          return false;
        } finally {
          unlock();
        }
      }

      void clear() {
        if (count != 0) {
          lock();
          try {
            AtomicReferenceArray<E> table = this.table;
            for (int i = 0; i < table.length(); i++) {
              table.set(i, null);
            }
            ++modCount;
            count = 0; // write-volatile
          } finally {
            unlock();
          }
        }
      }
    }

    /* ---------------- Public operations -------------- */

    /**
     * Returns {@code true} if this map contains no key-value mappings.
     *
     * @return {@code true} if this map contains no key-value mappings
     */
    @Override public boolean isEmpty() {
      final Segment[] segments = this.segments;
      /*
       * We keep track of per-segment modCounts to avoid ABA
       * problems in which an element in one segment was added and
       * in another removed during traversal, in which case the
       * table was never actually empty at any point. Note the
       * similar use of modCounts in the size() and containsValue()
       * methods, which are the only other methods also susceptible
       * to ABA problems.
       */
      int[] mc = new int[segments.length];
      int mcsum = 0;
      for (int i = 0; i < segments.length; ++i) {
        if (segments[i].count != 0) {
          return false;
        } else {
          mcsum += mc[i] = segments[i].modCount;
        }
      }
      // If mcsum happens to be zero, then we know we got a snapshot
      // before any modifications at all were made.  This is
      // probably common enough to bother tracking.
      if (mcsum != 0) {
        for (int i = 0; i < segments.length; ++i) {
          if (segments[i].count != 0 ||
              mc[i] != segments[i].modCount) {
            return false;
          }
        }
      }
      return true;
    }

    /**
     * Returns the number of key-value mappings in this map.  If the map
     * contains more than {@code Integer.MAX_VALUE} elements, returns
     * {@code Integer.MAX_VALUE}.
     *
     * @return the number of key-value mappings in this map
     */
    @Override public int size() {
      final Segment[] segments = this.segments;
      long sum = 0;
      long check = 0;
      int[] mc = new int[segments.length];
      // Try a few times to get accurate count. On failure due to
      // continuous async changes in table, resort to locking.
      for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
        check = 0;
        sum = 0;
        int mcsum = 0;
        for (int i = 0; i < segments.length; ++i) {
          sum += segments[i].count;
          mcsum += mc[i] = segments[i].modCount;
        }
        if (mcsum != 0) {
          for (int i = 0; i < segments.length; ++i) {
            check += segments[i].count;
            if (mc[i] != segments[i].modCount) {
              check = -1; // force retry
              break;
            }
          }
        }
        if (check == sum) {
          break;
        }
      }
      if (check != sum) { // Resort to locking all segments
        sum = 0;
        for (Segment segment : segments) {
          segment.lock();
        }
        for (Segment segment : segments) {
          sum += segment.count;
        }
        for (Segment segment : segments) {
          segment.unlock();
        }
      }
      if (sum > Integer.MAX_VALUE) {
        return Integer.MAX_VALUE;
      } else {
        return (int) sum;
      }
    }

    /**
     * Returns the value to which the specified key is mapped, or {@code null}
     * if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key {@code k} to
     * a value {@code v} such that {@code key.equals(k)}, then this method
     * returns {@code v}; otherwise it returns {@code null}.  (There can be at
     * most one such mapping.)
     *
     * @throws NullPointerException if the specified key is null
     */
    @Override public V get(Object key) {
      if (key == null) {
        throw new NullPointerException("key");
      }
      int hash = hash(key);
      return segmentFor(hash).get(key, hash);
    }

    /**
     * Tests if the specified object is a key in this table.
     *
     * @param key possible key
     * @return {@code true} if and only if the specified object is a key in
     *         this table, as determined by the {@code equals} method;
     *         {@code false} otherwise.
     * @throws NullPointerException if the specified key is null
     */
    @Override public boolean containsKey(Object key) {
      if (key == null) {
        throw new NullPointerException("key");
      }
      int hash = hash(key);
      return segmentFor(hash).containsKey(key, hash);
    }

    /**
     * Returns {@code true} if this map maps one or more keys to the specified
     * value. Note: This method requires a full internal traversal of the hash
     * table, and so is much slower than method {@code containsKey}.
     *
     * @param value value whose presence in this map is to be tested
     * @return {@code true} if this map maps one or more keys to the specified
     *         value
     * @throws NullPointerException if the specified value is null
     */
    @Override public boolean containsValue(Object value) {
      if (value == null) {
        throw new NullPointerException("value");
      }

      // See explanation of modCount use above

      final Segment[] segments = this.segments;
      int[] mc = new int[segments.length];

      // Try a few times without locking
      for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
        int mcsum = 0;
        for (int i = 0; i < segments.length; ++i) {
          @SuppressWarnings("UnusedDeclaration")
          int c = segments[i].count;
          mcsum += mc[i] = segments[i].modCount;
          if (segments[i].containsValue(value)) {
            return true;
          }
        }
        boolean cleanSweep = true;
        if (mcsum != 0) {
          for (int i = 0; i < segments.length; ++i) {
            @SuppressWarnings("UnusedDeclaration")
            int c = segments[i].count;
            if (mc[i] != segments[i].modCount) {
              cleanSweep = false;
              break;
            }
          }
        }
        if (cleanSweep) {
          return false;
        }
      }
      // Resort to locking all segments
      for (Segment segment : segments) {
        segment.lock();
      }
      boolean found = false;
      try {
        for (Segment segment : segments) {
          if (segment.containsValue(value)) {
            found = true;
            break;
          }
        }
      } finally {
        for (Segment segment : segments) {
          segment.unlock();
        }
      }
      return found;
    }

    /**
     * Maps the specified key to the specified value in this table. Neither the
     * key nor the value can be null.
     *
     * <p> The value can be retrieved by calling the {@code get} method with a
     * key that is equal to the original key.
     *
     * @param key   key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with {@code key}, or {@code null}
     *         if there was no mapping for {@code key}
     * @throws NullPointerException if the specified key or value is null
     */
    @Override public V put(K key, V value) {
      if (key == null) {
        throw new NullPointerException("key");
      }
      if (value == null) {
        throw new NullPointerException("value");
      }
      int hash = hash(key);
      return segmentFor(hash).put(key, hash, value, false);
    }

    /**
     * {@inheritDoc}
     *
     * @return the previous value associated with the specified key, or
     *         {@code null} if there was no mapping for the key
     * @throws NullPointerException if the specified key or value is null
     */
    public V putIfAbsent(K key, V value) {
      if (key == null) {
        throw new NullPointerException("key");
      }
      if (value == null) {
        throw new NullPointerException("value");
      }
      int hash = hash(key);
      return segmentFor(hash).put(key, hash, value, true);
    }

    /**
     * Copies all of the mappings from the specified map to this one. These
     * mappings replace any mappings that this map had for any of the keys
     * currently in the specified map.
     *
     * @param m mappings to be stored in this map
     */
    @Override public void putAll(Map<? extends K, ? extends V> m) {
      for (Entry<? extends K, ? extends V> e : m.entrySet()) {
        put(e.getKey(), e.getValue());
      }
    }

    /**
     * Removes the key (and its corresponding value) from this map. This method
     * does nothing if the key is not in the map.
     *
     * @param key the key that needs to be removed
     * @return the previous value associated with {@code key}, or {@code null}
     *         if there was no mapping for {@code key}
     * @throws NullPointerException if the specified key is null
     */
    @Override public V remove(Object key) {
      if (key == null) {
        throw new NullPointerException("key");
      }
      int hash = hash(key);
      return segmentFor(hash).remove(key, hash);
    }

    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException if the specified key is null
     */
    public boolean remove(Object key, Object value) {
      if (key == null) {
        throw new NullPointerException("key");
      }
      int hash = hash(key);
      return segmentFor(hash).remove(key, hash, value);
    }

    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException if any of the arguments are null
     */
    public boolean replace(K key, V oldValue, V newValue) {
      if (key == null) {
        throw new NullPointerException("key");
      }
      if (oldValue == null) {
        throw new NullPointerException("oldValue");
      }
      if (newValue == null) {
        throw new NullPointerException("newValue");
      }
      int hash = hash(key);
      return segmentFor(hash).replace(key, hash, oldValue, newValue);
    }

    /**
     * {@inheritDoc}
     *
     * @return the previous value associated with the specified key, or
     *         {@code null} if there was no mapping for the key
     * @throws NullPointerException if the specified key or value is null
     */
    public V replace(K key, V value) {
      if (key == null) {
        throw new NullPointerException("key");
      }
      if (value == null) {
        throw new NullPointerException("value");
      }
      int hash = hash(key);
      return segmentFor(hash).replace(key, hash, value);
    }

    /**
     * Removes all of the mappings from this map.
     */
    @Override public void clear() {
      for (Segment segment : segments) {
        segment.clear();
      }
    }

    Set<K> keySet;

    /**
     * Returns a {@link java.util.Set} view of the keys contained in this map.
     * The set is backed by the map, so changes to the map are reflected in the
     * set, and vice-versa. The set supports element removal, which removes the
     * corresponding mapping from this map, via the {@code Iterator.remove},
     * {@code Set.remove}, {@code removeAll}, {@code retainAll}, and
     * {@code clear} operations. It does not support the {@code add} or
     * {@code addAll} operations.
     *
     * <p>The view's {@code iterator} is a "weakly consistent" iterator that
     * will never throw {@link java.util.ConcurrentModificationException}, and
     * guarantees to traverse elements as they existed upon construction of the
     * iterator, and may (but is not guaranteed to) reflect any modifications
     * subsequent to construction.
     */
    @Override public Set<K> keySet() {
      Set<K> ks = keySet;
      return (ks != null) ? ks : (keySet = new KeySet());
    }

    Collection<V> values;

    /**
     * Returns a {@link java.util.Collection} view of the values contained in
     * this map. The collection is backed by the map, so changes to the map are
     * reflected in the collection, and vice-versa. The collection supports
     * element removal, which removes the corresponding mapping from this map,
     * via the {@code Iterator.remove}, {@code Collection.remove},
     * {@code removeAll}, {@code retainAll}, and {@code clear} operations. It
     * does not support the {@code add} or {@code addAll} operations.
     *
     * <p>The view's {@code iterator} is a "weakly consistent" iterator that
     * will never throw {@link java.util.ConcurrentModificationException}, and
     * guarantees to traverse elements as they existed upon construction of the
     * iterator, and may (but is not guaranteed to) reflect any modifications
     * subsequent to construction.
     */
    @Override public Collection<V> values() {
      Collection<V> vs = values;
      return (vs != null) ? vs : (values = new Values());
    }

    Set<Entry<K, V>> entrySet;

    /**
     * Returns a {@link java.util.Set} view of the mappings contained in this
     * map. The set is backed by the map, so changes to the map are reflected in
     * the set, and vice-versa. The set supports element removal, which removes
     * the corresponding mapping from the map, via the {@code Iterator.remove},
     * {@code Set.remove}, {@code removeAll}, {@code retainAll}, and
     * {@code clear} operations. It does not support the {@code add} or
     * {@code addAll} operations.
     *
     * <p>The view's {@code iterator} is a "weakly consistent" iterator that
     * will never throw {@link java.util.ConcurrentModificationException}, and
     * guarantees to traverse elements as they existed upon construction of the
     * iterator, and may (but is not guaranteed to) reflect any modifications
     * subsequent to construction.
     */
    @Override public Set<Entry<K, V>> entrySet() {
      Set<Entry<K, V>> es = entrySet;
      return (es != null) ? es : (entrySet = new EntrySet());
    }

    /* ---------------- Iterator Support -------------- */

    abstract class HashIterator {

      int nextSegmentIndex;
      int nextTableIndex;
      AtomicReferenceArray<E> currentTable;
      E nextEntry;
      WriteThroughEntry nextExternal;
      WriteThroughEntry lastReturned;

      HashIterator() {
        nextSegmentIndex = segments.length - 1;
        nextTableIndex = -1;
        advance();
      }

      public boolean hasMoreElements() {
        return hasNext();
      }

      final void advance() {
        nextExternal = null;

        if (nextInChain()) {
          return;
        }

        if (nextInTable()) {
          return;
        }

        while (nextSegmentIndex >= 0) {
          Segment seg = segments[nextSegmentIndex--];
          if (seg.count != 0) {
            currentTable = seg.table;
            nextTableIndex = currentTable.length() - 1;
            if (nextInTable()) {
              return;
            }
          }
        }
      }

      /**
       * Finds the next entry in the current chain. Returns true if an entry
       * was found.
       */
      boolean nextInChain() {
        Strategy<K, V, E> s = Impl.this.strategy;
        if (nextEntry != null) {
          for (nextEntry = s.getNext(nextEntry); nextEntry != null;
              nextEntry = s.getNext(nextEntry)) {
            if (advanceTo(nextEntry)) {
              return true;
            }
          }
        }
        return false;
      }

      /**
       * Finds the next entry in the current table. Returns true if an entry
       * was found.
       */
      boolean nextInTable() {
        while (nextTableIndex >= 0) {
          if ((nextEntry = currentTable.get(nextTableIndex--)) != null) {
            if (advanceTo(nextEntry) || nextInChain()) {
              return true;
            }
          }
        }
        return false;
      }

      /**
       * Advances to the given entry. Returns true if the entry was valid,
       * false if it should be skipped.
       */
      boolean advanceTo(E entry) {
        Strategy<K, V, E> s = Impl.this.strategy;
        K key = s.getKey(entry);
        V value = s.getValue(entry);
        if (key != null && value != null) {
          nextExternal = new WriteThroughEntry(key, value);
          return true;
        } else {
          // Skip partially reclaimed entry.
          return false;
        }
      }

      public boolean hasNext() {
        return nextExternal != null;
      }

      WriteThroughEntry nextEntry() {
        if (nextExternal == null) {
          throw new NoSuchElementException();
        }
        lastReturned = nextExternal;
        advance();
        return lastReturned;
      }

      public void remove() {
        if (lastReturned == null) {
          throw new IllegalStateException();
        }
        Impl.this.remove(lastReturned.getKey());
        lastReturned = null;
      }
    }

    final class KeyIterator extends HashIterator implements Iterator<K> {

      public K next() {
        return super.nextEntry().getKey();
      }
    }

    final class ValueIterator extends HashIterator implements Iterator<V> {

      public V next() {
        return super.nextEntry().getValue();
      }
    }

    /**
     * Custom Entry class used by EntryIterator.next(), that relays setValue
     * changes to the underlying map.
     */
    final class WriteThroughEntry extends AbstractMapEntry<K, V> {
      final K key;
      V value;

      WriteThroughEntry(K key, V value) {
        this.key = key;
        this.value = value;
      }

      @Override public K getKey() {
        return key;
      }

      @Override public V getValue() {
        return value;
      }

      /**
       * Set our entry's value and write through to the map. The value to
       * return is somewhat arbitrary here. Since a WriteThroughEntry does not
       * necessarily track asynchronous changes, the most recent "previous"
       * value could be different from what we return (or could even have been
       * removed in which case the put will re-establish). We do not and
       * cannot guarantee more.
       */
      @Override public V setValue(V value) {
        if (value == null) {
          throw new NullPointerException();
        }
        V oldValue = Impl.this.put(getKey(), value);
        this.value = value;
        return oldValue;
      }
    }

    final class EntryIterator extends HashIterator
        implements Iterator<Entry<K, V>> {

      public Entry<K, V> next() {
        return nextEntry();
      }
    }

    final class KeySet extends AbstractSet<K> {

      @Override public Iterator<K> iterator() {
        return new KeyIterator();
      }

      @Override public int size() {
        return Impl.this.size();
      }

      @Override public boolean isEmpty() {
        return Impl.this.isEmpty();
      }

      @Override public boolean contains(Object o) {
        return Impl.this.containsKey(o);
      }

      @Override public boolean remove(Object o) {
        return Impl.this.remove(o) != null;
      }

      @Override public void clear() {
        Impl.this.clear();
      }
    }

    final class Values extends AbstractCollection<V> {

      @Override public Iterator<V> iterator() {
        return new ValueIterator();
      }

      @Override public int size() {
        return Impl.this.size();
      }

      @Override public boolean isEmpty() {
        return Impl.this.isEmpty();
      }

      @Override public boolean contains(Object o) {
        return Impl.this.containsValue(o);
      }

      @Override public void clear() {
        Impl.this.clear();
      }
    }

    final class EntrySet extends AbstractSet<Entry<K, V>> {

      @Override public Iterator<Entry<K, V>> iterator() {
        return new EntryIterator();
      }

      @Override public boolean contains(Object o) {
        if (!(o instanceof Entry)) {
          return false;
        }
        Entry<?, ?> e = (Entry<?, ?>) o;
        Object key = e.getKey();
        if (key == null) {
          return false;
        }
        V v = Impl.this.get(key);

        return v != null && strategy.equalValues(v, e.getValue());
      }

      @Override public boolean remove(Object o) {
        if (!(o instanceof Entry)) {
          return false;
        }
        Entry<?, ?> e = (Entry<?, ?>) o;
        Object key = e.getKey();
        return key != null && Impl.this.remove(key, e.getValue());
      }

      @Override public int size() {
        return Impl.this.size();
      }

      @Override public boolean isEmpty() {
        return Impl.this.isEmpty();
      }

      @Override public void clear() {
        Impl.this.clear();
      }
    }

    /* ---------------- Serialization Support -------------- */

    private static final long serialVersionUID = 1;

    private void writeObject(java.io.ObjectOutputStream out)
        throws IOException {
      out.writeInt(size());
      out.writeInt(segments.length); // concurrencyLevel
      out.writeObject(strategy);
      for (Entry<K, V> entry : entrySet()) {
        out.writeObject(entry.getKey());
        out.writeObject(entry.getValue());
      }
      out.writeObject(null); // terminate entries
    }

    /**
     * Fields used during deserialization. We use a nested class so we don't
     * load them until we need them. We need to use reflection to set final
     * fields outside of the constructor.
     */
    static class Fields {

      static final Field segmentShift = findField("segmentShift");
      static final Field segmentMask = findField("segmentMask");
      static final Field segments = findField("segments");
      static final Field strategy = findField("strategy");

      static Field findField(String name) {
        try {
          Field f = Impl.class.getDeclaredField(name);
          f.setAccessible(true);
          return f;
        } catch (NoSuchFieldException e) {
          throw new AssertionError(e);
        }
      }
    }

    @SuppressWarnings("unchecked")
    private void readObject(java.io.ObjectInputStream in)
        throws IOException, ClassNotFoundException {
      try {
        int initialCapacity = in.readInt();
        int concurrencyLevel = in.readInt();
        Strategy<K, V, E> strategy = (Strategy<K, V, E>) in.readObject();

        if (concurrencyLevel > MAX_SEGMENTS) {
          concurrencyLevel = MAX_SEGMENTS;
        }

        // Find power-of-two sizes best matching arguments
        int segmentShift = 0;
        int segmentCount = 1;
        while (segmentCount < concurrencyLevel) {
          ++segmentShift;
          segmentCount <<= 1;
        }
        Fields.segmentShift.set(this, 32 - segmentShift);
        Fields.segmentMask.set(this, segmentCount - 1);
        Fields.segments.set(this, newSegmentArray(segmentCount));

        if (initialCapacity > MAXIMUM_CAPACITY) {
          initialCapacity = MAXIMUM_CAPACITY;
        }

        int segmentCapacity = initialCapacity / segmentCount;
        if (segmentCapacity * segmentCount < initialCapacity) {
          ++segmentCapacity;
        }

        int segmentSize = 1;
        while (segmentSize < segmentCapacity) {
            segmentSize <<= 1;
        }
        for (int i = 0; i < this.segments.length; ++i) {
          this.segments[i] = new Segment(segmentSize);
        }

        Fields.strategy.set(this, strategy);

        while (true) {
          K key = (K) in.readObject();
          if (key == null) {
            break; // terminator
          }
          V value = (V) in.readObject();
          put(key, value);
        }
      } catch (IllegalAccessException e) {
        throw new AssertionError(e);
      }
    }
  }

  static class ComputingImpl<K, V, E> extends Impl<K, V, E> {

    static final long serialVersionUID = 0;

    final ComputingStrategy<K, V, E> computingStrategy;
    final Function<? super K, ? extends V> computer;

    /**
     * Creates a new, empty map with the specified strategy, initial capacity,
     * load factor and concurrency level.
     */
    ComputingImpl(ComputingStrategy<K, V, E> strategy, Builder builder,
        Function<? super K, ? extends V> computer) {
      super(strategy, builder);
      this.computingStrategy = strategy;
      this.computer = computer;
    }

    @Override public V get(Object k) {
      /*
       * This cast isn't safe, but we can rely on the fact that K is almost
       * always passed to Map.get(), and tools like IDEs and Findbugs can
       * catch situations where this isn't the case.
       *
       * The alternative is to add an overloaded method, but the chances of
       * a user calling get() instead of the new API and the risks inherent
       * in adding a new API outweigh this little hole.
       */
      @SuppressWarnings("unchecked")
      K key = (K) k;

      if (key == null) {
        throw new NullPointerException("key");
      }

      int hash = hash(key);
      Segment segment = segmentFor(hash);
      outer: while (true) {
        E entry = segment.getEntry(key, hash);
        if (entry == null) {
          boolean created = false;
          segment.lock();
          try {
            // Try again--an entry could have materialized in the interim.
            entry = segment.getEntry(key, hash);
            if (entry == null) {
              // Create a new entry.
              created = true;
              int count = segment.count;
              if (count++ > segment.threshold) { // ensure capacity
                segment.expand();
              }
              AtomicReferenceArray<E> table = segment.table;
              int index = hash & (table.length() - 1);
              E first = table.get(index);
              ++segment.modCount;
              entry = computingStrategy.newEntry(key, hash, first);
              table.set(index, entry);
              segment.count = count; // write-volatile
            }
          } finally {
            segment.unlock();
          }

          if (created) {
            // This thread solely created the entry.
            boolean success = false;
            try {
              V value = computingStrategy.compute(key, entry, computer);
              if (value == null) {
                throw new NullPointerException(
                    "compute() returned null unexpectedly");
              }
              success = true;
              return value;
            } finally {
              if (!success) {
                segment.removeEntry(entry, hash);
              }
            }
          }
        }

        // The entry already exists. Wait for the computation.
        boolean interrupted = false;
        try {
          while (true) {
            try {
              V value = computingStrategy.waitForValue(entry);
              if (value == null) {
                // Purge entry and try again.
                segment.removeEntry(entry, hash);
                continue outer;
              }
              return value;
            } catch (InterruptedException e) {
              interrupted = true;
            }
          }
        } finally {
          if (interrupted) {
            Thread.currentThread().interrupt();
          }
        }
      }
    }
  }

  /**
   * A basic, no-frills implementation of {@code Strategy} using {@link
   * SimpleInternalEntry}. Intended to be subclassed to provide customized
   * behavior. For example, the following creates a map that uses byte arrays as
   * keys: <pre>   {@code
   *
   *   return new CustomConcurrentHashMap.Builder().buildMap(
   *       new SimpleStrategy<byte[], V>() {
   *         public int hashKey(Object key) {
   *           return Arrays.hashCode((byte[]) key);
   *         }
   *         public boolean equalKeys(byte[] a, Object b) {
   *           return Arrays.equals(a, (byte[]) b);
   *         }
   *       });}</pre>
   *
   * With nothing overridden, it yields map behavior equivalent to that of
   * {@link ConcurrentHashMap}.
   */
  static class SimpleStrategy<K, V>
      implements Strategy<K, V, SimpleInternalEntry<K, V>> {
    public SimpleInternalEntry<K, V> newEntry(
        K key, int hash, SimpleInternalEntry<K, V> next) {
      return new SimpleInternalEntry<K, V>(key, hash, null, next);
    }
    public SimpleInternalEntry<K, V> copyEntry(K key,
        SimpleInternalEntry<K, V> original, SimpleInternalEntry<K, V> next) {
      return new SimpleInternalEntry<K, V>(
          key, original.hash, original.value, next);
    }
    public void setValue(SimpleInternalEntry<K, V> entry, V value) {
      entry.value = value;
    }
    public V getValue(SimpleInternalEntry<K, V> entry) {
      return entry.value;
    }
    public boolean equalKeys(K a, Object b) {
      return a.equals(b);
    }
    public boolean equalValues(V a, Object b) {
      return a.equals(b);
    }
    public int hashKey(Object key) {
      return key.hashCode();
    }
    public K getKey(SimpleInternalEntry<K, V> entry) {
      return entry.key;
    }
    public SimpleInternalEntry<K, V> getNext(SimpleInternalEntry<K, V> entry) {
      return entry.next;
    }
    public int getHash(SimpleInternalEntry<K, V> entry) {
      return entry.hash;
    }
    public void setInternals(
        Internals<K, V, SimpleInternalEntry<K, V>> internals) {
      // ignore?
    }
  }

  /**
   * A basic, no-frills entry class used by {@link SimpleInternalEntry}.
   */
  static class SimpleInternalEntry<K, V> {
    final K key;
    final int hash;
    final SimpleInternalEntry<K, V> next;
    volatile V value;
    SimpleInternalEntry(
        K key, int hash, @Nullable V value, SimpleInternalEntry<K, V> next) {
      this.key = key;
      this.hash = hash;
      this.value = value;
      this.next = next;
    }
  }
}
