content: /*******************************************************************************
   * Copyright 2014 Trevor Robinson
   * 
   * 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.scurrilous.circe;
  
  import java.util.Comparator;
  import java.util.EnumSet;
  import java.util.Iterator;
  
  /**
   * Flags indicating the support available for some set of hash algorithm.
   */
  public enum HashSupport {
      /**
       * Indicates that the hash algorithm is available in hardware-accelerated
       * native code as an {@link IncrementalIntHash} or
       * {@link IncrementalLongHash}, depending on which of {@link #INT_SIZED} or
       * {@link #LONG_SIZED} is set.
       */
      HARDWARE_INCREMENTAL(10),
      /**
       * Indicates that the hash algorithm is available in hardware-accelerated
       * native code.
       */
      HARDWARE(20),
      /**
       * Indicates that the hash algorithm is available in native code as a
       * {@link IncrementalIntHash} or {@link IncrementalLongHash}, depending on
       * which of {@link #INT_SIZED} or {@link #LONG_SIZED} is set.
       */
      NATIVE_INCREMENTAL(30),
      /**
       * Indicates that the hash algorithm is available in native code.
       */
      NATIVE(40),
      /**
       * Indicates that the incremental hash algorithm supports unsafe memory
       * access via {@link IncrementalIntHash#resume(int, long, long)} or
       * {@link IncrementalLongHash#resume(long, long, long)}, depending on which
       * of {@link #INT_SIZED} or {@link #LONG_SIZED} is set.
       */
      UNSAFE_INCREMENTAL(50),
      /**
       * Indicates that the stateful hash algorithm unsafe memory access via
       * {@link StatefulHash#update(long, long)}. If {@link #INT_SIZED} is also
       * set, the function returned by {@link StatefulIntHash#asStateless()} also
       * supports {@link StatelessIntHash#calculate(long, long)}. Similarly, if
       * {@link #LONG_SIZED} is also set, the function returned by
       * {@link StatefulLongHash#asStateless()} also supports
       * {@link StatelessLongHash#calculate(long, long)}.
       */
      UNSAFE(60),
      /**
       * Indicates that the hash algorithm is available as a
       * {@link IncrementalIntHash} or {@link IncrementalLongHash}, depending on
       * which of {@link #INT_SIZED} or {@link #LONG_SIZED} is set.
       */
      STATELESS_INCREMENTAL(70),
      /**
       * Indicates that the hash algorithm is available as an incremental stateful
       * hash function, for which {@link StatefulHash#supportsIncremental()}
       * returns {@code true}. This flag is implied by
       * {@link #STATELESS_INCREMENTAL}.
       */
      INCREMENTAL(80),
      /**
       * Indicates that the hash algorithm is available as a
       * {@link StatefulIntHash} and {@link StatelessIntHash}.
       */
      INT_SIZED(90),
      /**
       * Indicates that the hash algorithm is available as a
       * {@link StatefulLongHash} and {@link StatelessLongHash}.
       */
      LONG_SIZED(90),
      /**
       * Indicates that the hash algorithm is available as a {@link StatefulHash}.
       * If this flag is not set, the algorithm is not supported at all.
       */
      STATEFUL(100);
  
      /**
       * The minimum priority value, indicating the highest priority. All flags
       * have a priority value greater than this.
       */
      public static final int MIN_PRIORITY = 0;
  
      /**
       * The maximum priority value, indicating the lowest priority. All flags
       * have a priority value less than this.
       */
      public static final int MAX_PRIORITY = 110;
  
      private final int priority;
  
      private HashSupport(int priority) {
          this.priority = priority;
      }
  
      /**
       * Returns the relative priority of a hash algorithm support flag, which is
       * an indicator of its performance and flexibility. Lower values indicate
       * higher priority.
       * 
       * @return the priority of this flag (currently between 10 and 90)
       */
      public int getPriority() {
          return priority;
      }
  
      /**
       * Returns the {@linkplain #getPriority() priority} of the highest-priority
       * hash algorithm support flag in the given set of flags. If the set is
       * empty, {@link #MAX_PRIORITY} is returned.
       * 
       * @param set a set of hash algorithm support flags
       * @return the highest priority (lowest value) in the set, or
       *         {@link #MAX_PRIORITY} if empty
       */
      public static int getMaxPriority(EnumSet<HashSupport> set) {
          if (set.isEmpty())
              return MAX_PRIORITY;
          return set.iterator().next().getPriority();
      }
  
      /**
       * Compares the given sets of hash algorithm support flags for priority
       * order. The set with the highest priority flag without a flag of matching
       * priority in the other set has higher priority.
       * 
       * @param set1 the first set to be compared
       * @param set2 the second set to be compared
       * @return a negative integer, zero, or a positive integer if the first set
       *         has priority higher than, equal to, or lower than the second
       */
      public static int compare(EnumSet<HashSupport> set1, EnumSet<HashSupport> set2) {
          // assumes iterators return flags in priority order
          final Iterator<HashSupport> i1 = set1.iterator();
          final Iterator<HashSupport> i2 = set2.iterator();
          int floor = MIN_PRIORITY;
          while (i1.hasNext() || i2.hasNext()) {
              int p1, p2;
              do {
                  p1 = i1.hasNext() ? i1.next().getPriority() : MAX_PRIORITY;
              } while (p1 == floor);
              do {
                  p2 = i2.hasNext() ? i2.next().getPriority() : MAX_PRIORITY;
              } while (p2 == floor);
              if (p1 < p2)
                  return -1;
              if (p1 > p2)
                  return 1;
              floor = p1;
          }
          return 0;
      }
  
      /**
       * {@link Comparator} for {@link EnumSet EnumSets} for hash support flags.
       */
      static final class SetComparator implements Comparator<EnumSet<HashSupport>> {
          @Override
          public int compare(EnumSet<HashSupport> o1, EnumSet<HashSupport> o2) {
              return HashSupport.compare(o1, o2);
          }
      }
  }
  