content: package com.alibaba.fastjson2;
  
  import com.alibaba.fastjson2.util.Fnv;
  
  import java.util.Arrays;
  import java.util.Iterator;
  import java.util.Set;
  import java.util.TreeSet;
  
  /**
   * Symbol table for fast name lookup.
   *
   * <p>This class provides a way to efficiently map names (strings) to ordinals and vice versa.
   * It uses FNV-1a hash algorithm for fast hashing and maintains sorted hash codes for binary search.
   *
   * <p>SymbolTable is designed to be immutable after construction, making it thread-safe.
   *
   * @since 2.0.58
   */
  public final class SymbolTable {
      private final String[] names;
      private final long hashCode64;
      private final short[] mapping;
  
      private final long[] hashCodes;
      private final long[] hashCodesOrigin;
  
      /**
       * Create a symbol table from class names.
       *
       * @param input classes whose names will be added to the symbol table
       * @since 2.0.58
       */
      public SymbolTable(Class<?>... input) {
          this(classNames(input));
      }
  
      /**
       * Extract class names from Class objects.
       *
       * @param input Class objects
       * @return array of class names
       */
      private static String[] classNames(Class<?>... input) {
          String[] names = new String[input.length];
          for (int i = 0; i < input.length; i++) {
              names[i] = input[i].getName();
          }
          return names;
      }
  
      /**
       * Create a symbol table from string names.
       *
       * <p>The names will be sorted and deduplicated. Each name is assigned a unique ordinal
       * starting from 1. The ordinal 0 is reserved and means "not found".
       *
       * @param input names to be added to the symbol table
       */
      public SymbolTable(String... input) {
          Set<String> set = new TreeSet<>(Arrays.asList(input));
          names = new String[set.size()];
          Iterator<String> it = set.iterator();
  
          for (int i = 0; i < names.length; i++) {
              if (it.hasNext()) {
                  names[i] = it.next();
              }
          }
  
          long[] hashCodes = new long[names.length];
          for (int i = 0; i < names.length; i++) {
              long hashCode = Fnv.hashCode64(names[i]);
              hashCodes[i] = hashCode;
          }
          this.hashCodesOrigin = hashCodes;
  
          this.hashCodes = Arrays.copyOf(hashCodes, hashCodes.length);
          Arrays.sort(this.hashCodes);
  
          mapping = new short[this.hashCodes.length];
          for (int i = 0; i < hashCodes.length; i++) {
              long hashCode = hashCodes[i];
              int index = Arrays.binarySearch(this.hashCodes, hashCode);
              mapping[index] = (short) i;
          }
  
          long hashCode64 = Fnv.MAGIC_HASH_CODE;
          for (long hashCode : hashCodes) {
              hashCode64 ^= hashCode;
              hashCode64 *= Fnv.MAGIC_PRIME;
          }
          this.hashCode64 = hashCode64;
      }
  
      /**
       * Get the number of names in this symbol table.
       *
       * @return the number of names
       */
      public int size() {
          return names.length;
      }
  
      /**
       * Get the 64-bit hash code of this symbol table.
       *
       * <p>The hash code is computed from all the names in the symbol table.
       * It can be used to quickly compare if two symbol tables have the same content.
       *
       * @return the 64-bit hash code of this symbol table
       */
      public long hashCode64() {
          return hashCode64;
      }
  
      /**
       * Get the name by its hash code.
       *
       * @param hashCode the FNV-1a 64-bit hash code of the name
       * @return the name if found, {@code null} otherwise
       */
      public String getNameByHashCode(long hashCode) {
          int m = Arrays.binarySearch(hashCodes, hashCode);
          if (m < 0) {
              return null;
          }
  
          int index = this.mapping[m];
          return names[index];
      }
  
      /**
       * Get the ordinal of a name by its hash code.
       *
       * @param hashCode the FNV-1a 64-bit hash code of the name
       * @return the ordinal (1-based) if found, -1 otherwise
       */
      public int getOrdinalByHashCode(long hashCode) {
          int m = Arrays.binarySearch(hashCodes, hashCode);
          if (m < 0) {
              return -1;
          }
  
          return this.mapping[m] + 1;
      }
  
      /**
       * Get the ordinal of a name.
       *
       * @param name the name to look up
       * @return the ordinal (1-based) if found, -1 otherwise
       */
      public int getOrdinal(String name) {
          int m = Arrays.binarySearch(hashCodes, Fnv.hashCode64(name));
          if (m < 0) {
              return -1;
          }
  
          return this.mapping[m] + 1;
      }
  
      /**
       * Get the name by its ordinal.
       *
       * @param ordinal the ordinal (1-based) of the name
       * @return the name at the specified ordinal
       * @throws ArrayIndexOutOfBoundsException if the ordinal is invalid
       */
      public String getName(int ordinal) {
          return names[ordinal - 1];
      }
  
      /**
       * Get the hash code of a name by its ordinal.
       *
       * @param ordinal the ordinal (1-based) of the name
       * @return the FNV-1a 64-bit hash code of the name at the specified ordinal
       * @throws ArrayIndexOutOfBoundsException if the ordinal is invalid
       */
      public long getHashCode(int ordinal) {
          return hashCodesOrigin[ordinal - 1];
      }
  }
  