content: /*
   * Copyright (C) 2007 The Android Open Source Project
   *
   * 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.taobao.android.dx.rop.code;
  
  import com.taobao.android.dx.rop.type.Type;
  import com.taobao.android.dx.rop.type.TypeList;
  import com.taobao.android.dx.util.FixedSizeList;
  import java.util.BitSet;
  
  /**
   * List of {@link RegisterSpec} instances.
   */
  public final class RegisterSpecList
          extends FixedSizeList implements TypeList {
      /** {@code non-null;} no-element instance */
      public static final RegisterSpecList EMPTY = new RegisterSpecList(0);
  
      /**
       * Makes a single-element instance.
       *
       * @param spec {@code non-null;} the element
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public static RegisterSpecList make(RegisterSpec spec) {
          RegisterSpecList result = new RegisterSpecList(1);
          result.set(0, spec);
          return result;
      }
  
      /**
       * Makes a two-element instance.
       *
       * @param spec0 {@code non-null;} the first element
       * @param spec1 {@code non-null;} the second element
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public static RegisterSpecList make(RegisterSpec spec0,
                                          RegisterSpec spec1) {
          RegisterSpecList result = new RegisterSpecList(2);
          result.set(0, spec0);
          result.set(1, spec1);
          return result;
      }
  
      /**
       * Makes a three-element instance.
       *
       * @param spec0 {@code non-null;} the first element
       * @param spec1 {@code non-null;} the second element
       * @param spec2 {@code non-null;} the third element
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
                                          RegisterSpec spec2) {
          RegisterSpecList result = new RegisterSpecList(3);
          result.set(0, spec0);
          result.set(1, spec1);
          result.set(2, spec2);
          return result;
      }
  
      /**
       * Makes a four-element instance.
       *
       * @param spec0 {@code non-null;} the first element
       * @param spec1 {@code non-null;} the second element
       * @param spec2 {@code non-null;} the third element
       * @param spec3 {@code non-null;} the fourth element
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
                                          RegisterSpec spec2,
                                          RegisterSpec spec3) {
          RegisterSpecList result = new RegisterSpecList(4);
          result.set(0, spec0);
          result.set(1, spec1);
          result.set(2, spec2);
          result.set(3, spec3);
          return result;
      }
  
      /**
       * Constructs an instance. All indices initially contain {@code null}.
       *
       * @param size the size of the list
       */
      public RegisterSpecList(int size) {
          super(size);
      }
  
      /** {@inheritDoc} */
      public Type getType(int n) {
          return get(n).getType().getType();
      }
  
      /** {@inheritDoc} */
      public int getWordCount() {
          int sz = size();
          int result = 0;
  
          for (int i = 0; i < sz; i++) {
              result += getType(i).getCategory();
          }
  
          return result;
      }
  
      /** {@inheritDoc} */
      public TypeList withAddedType(Type type) {
          throw new UnsupportedOperationException("unsupported");
      }
  
      /**
       * Gets the indicated element. It is an error to call this with the
       * index for an element which was never set; if you do that, this
       * will throw {@code NullPointerException}.
       *
       * @param n {@code >= 0, < size();} which element
       * @return {@code non-null;} the indicated element
       */
      public RegisterSpec get(int n) {
          return (RegisterSpec) get0(n);
      }
  
      /**
       * Returns a RegisterSpec in this list that uses the specified register,
       * or null if there is none in this list.
       * @param reg Register to find
       * @return RegisterSpec that uses argument or null.
       */
      public RegisterSpec specForRegister(int reg) {
          int sz = size();
          for (int i = 0; i < sz; i++) {
              RegisterSpec rs;
  
              rs = get(i);
  
              if (rs.getReg() == reg) {
                  return rs;
              }
          }
  
          return null;
      }
  
      /**
       * Returns the index of a RegisterSpec in this list that uses the specified
       * register, or -1 if none in this list uses the register.
       * @param reg Register to find
       * @return index of RegisterSpec or -1
       */
      public int indexOfRegister(int reg) {
          int sz = size();
          for (int i = 0; i < sz; i++) {
              RegisterSpec rs;
  
              rs = get(i);
  
              if (rs.getReg() == reg) {
                  return i;
              }
          }
  
          return -1;
      }
  
      /**
       * Sets the element at the given index.
       *
       * @param n {@code >= 0, < size();} which element
       * @param spec {@code non-null;} the value to store
       */
      public void set(int n, RegisterSpec spec) {
          set0(n, spec);
      }
  
      /**
       * Gets the minimum required register count implied by this
       * instance. This is equal to the highest register number referred
       * to plus the widest width (largest category) of the type used in
       * that register.
       *
       * @return {@code >= 0;} the required registers size
       */
      public int getRegistersSize() {
          int sz = size();
          int result = 0;
  
          for (int i = 0; i < sz; i++) {
              RegisterSpec spec = (RegisterSpec) get0(i);
              if (spec != null) {
                  int min = spec.getNextReg();
                  if (min > result) {
                      result = min;
                  }
              }
          }
  
          return result;
      }
  
      /**
       * Returns a new instance, which is the same as this instance,
       * except that it has an additional element prepended to the original.
       * Mutability of the result is inherited from the original.
       *
       * @param spec {@code non-null;} the new first spec (to prepend)
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public RegisterSpecList withFirst(RegisterSpec spec) {
          int sz = size();
          RegisterSpecList result = new RegisterSpecList(sz + 1);
  
          for (int i = 0; i < sz; i++) {
              result.set0(i + 1, get0(i));
          }
  
          result.set0(0, spec);
          if (isImmutable()) {
              result.setImmutable();
          }
  
          return result;
      }
  
      /**
       * Returns a new instance, which is the same as this instance,
       * except that its first element is removed. Mutability of the
       * result is inherited from the original.
       *
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public RegisterSpecList withoutFirst() {
          int newSize = size() - 1;
  
          if (newSize == 0) {
              return EMPTY;
          }
  
          RegisterSpecList result = new RegisterSpecList(newSize);
  
          for (int i = 0; i < newSize; i++) {
              result.set0(i, get0(i + 1));
          }
  
          if (isImmutable()) {
              result.setImmutable();
          }
  
          return result;
      }
  
      /**
       * Returns a new instance, which is the same as this instance,
       * except that its last element is removed. Mutability of the
       * result is inherited from the original.
       *
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public RegisterSpecList withoutLast() {
          int newSize = size() - 1;
  
          if (newSize == 0) {
              return EMPTY;
          }
  
          RegisterSpecList result = new RegisterSpecList(newSize);
  
          for (int i = 0; i < newSize; i++) {
              result.set0(i, get0(i));
          }
  
          if (isImmutable()) {
              result.setImmutable();
          }
  
          return result;
      }
  
      /**
       * Returns a new instance, which contains a subset of the elements
       * specified by the given BitSet. Indexes in the BitSet with a zero
       * are included, while indexes with a one are excluded. Mutability
       * of the result is inherited from the original.
       *
       * @param exclusionSet {@code non-null;} set of registers to exclude
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public RegisterSpecList subset(BitSet exclusionSet) {
          int newSize = size() - exclusionSet.cardinality();
  
          if (newSize == 0) {
              return EMPTY;
          }
  
          RegisterSpecList result = new RegisterSpecList(newSize);
  
          int newIndex = 0;
          for (int oldIndex = 0; oldIndex < size(); oldIndex++) {
              if (!exclusionSet.get(oldIndex)) {
                  result.set0(newIndex, get0(oldIndex));
                  newIndex++;
              }
          }
  
          if (isImmutable()) {
              result.setImmutable();
          }
  
          return result;
      }
  
      /**
       * Returns an instance that is identical to this one, except that
       * all register numbers are offset by the given amount. Mutability
       * of the result is inherited from the original.
       *
       * @param delta the amount to offset the register numbers by
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public RegisterSpecList withOffset(int delta) {
          int sz = size();
  
          if (sz == 0) {
              // Don't bother making a new zero-element instance.
              return this;
          }
  
          RegisterSpecList result = new RegisterSpecList(sz);
  
          for (int i = 0; i < sz; i++) {
              RegisterSpec one = (RegisterSpec) get0(i);
              if (one != null) {
                  result.set0(i, one.withOffset(delta));
              }
          }
  
          if (isImmutable()) {
              result.setImmutable();
          }
  
          return result;
      }
  
      /**
       * Returns an instance that is identical to this one, except that
       * all incompatible register numbers are renumbered sequentially from
       * the given base, with the first number duplicated if indicated. If
       * a null BitSet is given, it indicates all registers are incompatible.
       *
       * @param base the base register number
       * @param duplicateFirst whether to duplicate the first number
       * @param compatRegs {@code null-ok;} either a {@code non-null} set of
       * compatible registers, or {@code null} to indicate all registers are
       * incompatible
       * @return {@code non-null;} an appropriately-constructed instance
       */
      public RegisterSpecList withExpandedRegisters(int base,
                                                    boolean duplicateFirst,
                                                    BitSet compatRegs) {
          int sz = size();
  
          if (sz == 0) {
              // Don't bother making a new zero-element instance.
              return this;
          }
  
          Expander expander = new Expander(this, compatRegs, base, duplicateFirst);
  
          for (int regIdx = 0; regIdx < sz; regIdx++) {
            expander.expandRegister(regIdx);
          }
  
          return expander.getResult();
      }
  
      private static class Expander {
        private BitSet compatRegs;
        private RegisterSpecList regSpecList;
        private int base;
        private RegisterSpecList result;
        private boolean duplicateFirst;
  
        private Expander(RegisterSpecList regSpecList, BitSet compatRegs, int base,
            boolean duplicateFirst) {
          this.regSpecList = regSpecList;
          this.compatRegs = compatRegs;
          this.base = base;
          this.result = new RegisterSpecList(regSpecList.size());
          this.duplicateFirst = duplicateFirst;
        }
  
        private void expandRegister(int regIdx) {
          expandRegister(regIdx, (RegisterSpec) regSpecList.get0(regIdx));
        }
  
        private void expandRegister(int regIdx, RegisterSpec registerToExpand) {
          boolean replace = (compatRegs == null) ? true : !compatRegs.get(regIdx);
          RegisterSpec expandedReg;
  
          if (replace) {
            expandedReg = registerToExpand.withReg(base);
            if (!duplicateFirst) {
              base += expandedReg.getCategory();
            }
            duplicateFirst = false;
          } else {
            expandedReg = registerToExpand;
          }
  
          result.set0(regIdx, expandedReg);
        }
  
        private RegisterSpecList getResult() {
          if (regSpecList.isImmutable()) {
            result.setImmutable();
          }
  
          return result;
        }
      }
  }
  