SWITCH.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.generic;

  18. import java.util.Arrays;

  19. /**
  20.  * SWITCH - Branch depending on int value, generates either LOOKUPSWITCH or TABLESWITCH instruction, depending on
  21.  * whether the match values (int[]) can be sorted with no gaps between the numbers.
  22.  */
  23. public final class SWITCH implements CompoundInstruction {

  24.     /**
  25.      * @return match is sorted in ascending order with no gap bigger than maxGap?
  26.      */
  27.     private static boolean matchIsOrdered(final int[] match, final int matchLength, final int maxGap) {
  28.         for (int i = 1; i < matchLength; i++) {
  29.             if (match[i] - match[i - 1] > maxGap) {
  30.                 return false;
  31.             }
  32.         }
  33.         return true;
  34.     }

  35.     /**
  36.      * Sorts match and targets array with QuickSort.
  37.      */
  38.     private static void sort(final int l, final int r, final int[] match, final InstructionHandle[] targets) {
  39.         int i = l;
  40.         int j = r;
  41.         int h;
  42.         final int m = match[l + r >>> 1];
  43.         InstructionHandle h2;
  44.         do {
  45.             while (match[i] < m) {
  46.                 i++;
  47.             }
  48.             while (m < match[j]) {
  49.                 j--;
  50.             }
  51.             if (i <= j) {
  52.                 h = match[i];
  53.                 match[i] = match[j];
  54.                 match[j] = h; // Swap elements
  55.                 h2 = targets[i];
  56.                 targets[i] = targets[j];
  57.                 targets[j] = h2; // Swap instructions, too
  58.                 i++;
  59.                 j--;
  60.             }
  61.         } while (i <= j);
  62.         if (l < j) {
  63.             sort(l, j, match, targets);
  64.         }
  65.         if (i < r) {
  66.             sort(i, r, match, targets);
  67.         }
  68.     }

  69.     private final Select instruction;

  70.     public SWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle target) {
  71.         this(match, targets, target, 1);
  72.     }

  73.     /**
  74.      * Template for switch() constructs. If the match array can be sorted in ascending order with gaps no larger than
  75.      * maxGap between the numbers, a TABLESWITCH instruction is generated, and a LOOKUPSWITCH otherwise. The former may be
  76.      * more efficient, but needs more space.
  77.      *
  78.      * Note, that the key array always will be sorted, though we leave the original arrays unaltered.
  79.      *
  80.      * @param match array of match values (case 2: ... case 7: ..., etc.)
  81.      * @param targets the instructions to be branched to for each case
  82.      * @param target the default target
  83.      * @param maxGap maximum gap that may between case branches
  84.      */
  85.     public SWITCH(final int[] match, final InstructionHandle[] targets, final InstructionHandle target, final int maxGap) {
  86.         final int[] matchClone = match.clone();
  87.         final InstructionHandle[] targetsClone = targets.clone();
  88.         final int matchLength = match.length;
  89.         if (matchLength < 2) {
  90.             instruction = new TABLESWITCH(match, targets, target);
  91.         } else {
  92.             sort(0, matchLength - 1, matchClone, targetsClone);
  93.             if (matchIsOrdered(matchClone, matchLength, maxGap)) {
  94.                 final int maxSize = matchLength + matchLength * maxGap;
  95.                 final int[] mVec = new int[maxSize];
  96.                 final InstructionHandle[] tVec = new InstructionHandle[maxSize];
  97.                 int count = 1;
  98.                 mVec[0] = match[0];
  99.                 tVec[0] = targets[0];
  100.                 for (int i = 1; i < matchLength; i++) {
  101.                     final int prev = match[i - 1];
  102.                     final int gap = match[i] - prev;
  103.                     for (int j = 1; j < gap; j++) {
  104.                         mVec[count] = prev + j;
  105.                         tVec[count] = target;
  106.                         count++;
  107.                     }
  108.                     mVec[count] = match[i];
  109.                     tVec[count] = targets[i];
  110.                     count++;
  111.                 }
  112.                 instruction = new TABLESWITCH(Arrays.copyOf(mVec, count), Arrays.copyOf(tVec, count), target);
  113.             } else {
  114.                 instruction = new LOOKUPSWITCH(matchClone, targetsClone, target);
  115.             }
  116.         }
  117.     }

  118.     public Instruction getInstruction() {
  119.         return instruction;
  120.     }

  121.     @Override
  122.     public InstructionList getInstructionList() {
  123.         return new InstructionList(instruction);
  124.     }
  125. }