content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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 org.apache.cassandra.utils;
  
  import java.nio.ByteBuffer;
  import java.util.Arrays;
  import java.util.Random;
  
  import org.junit.Test;
  
  public class FastByteOperationsTest
  {
  
      private static final FastByteOperations.PureJavaOperations PJO = new FastByteOperations.PureJavaOperations();
      private static final FastByteOperations.UnsafeOperations UO = new FastByteOperations.UnsafeOperations();
      private static final Random rand = new Random(0);
      private static final ByteBuffer dbuf1 = ByteBuffer.allocateDirect(150);
      private static final ByteBuffer dbuf2 = ByteBuffer.allocateDirect(150);
      private static final ByteBuffer hbuf1 = ByteBuffer.allocate(150);
      private static final ByteBuffer hbuf2 = ByteBuffer.allocate(150);
  
      @Test
      public void testFastByteCopy()
      {
          byte[] bytes1 = new byte[128];
          byte[] empty = new byte[128];
          rand.nextBytes(bytes1);
          testCopy(bytes1, wrap1(bytes1, true), wrap2(empty, true), PJO);
          testCopy(bytes1, wrap1(bytes1, true), wrap2(empty, false), PJO);
          testCopy(bytes1, wrap1(bytes1, false), wrap2(empty, true), PJO);
          testCopy(bytes1, wrap1(bytes1, false), wrap2(empty, false), PJO);
          testCopy(bytes1, wrap1(bytes1, true), wrap2(empty, true), UO);
          testCopy(bytes1, wrap1(bytes1, true), wrap2(empty, false), UO);
          testCopy(bytes1, wrap1(bytes1, false), wrap2(empty, true), UO);
          testCopy(bytes1, wrap1(bytes1, false), wrap2(empty, false), UO);
      }
  
      private void testCopy(byte[] canon, ByteBuffer src, ByteBuffer trg, FastByteOperations.ByteOperations ops)
      {
          byte[] result = new byte[src.remaining()];
          ops.copy(src, src.position(), trg, trg.position(), src.remaining());
          ops.copy(trg, trg.position(), result, 0, trg.remaining());
          assert firstdiff(canon, result) < 0;
      }
  
      private static int firstdiff(byte[] canon, byte[] test)
      {
          for (int i = 0 ; i < canon.length ; i++)
              if (canon[i] != test[i])
                  return i;
          return -1;
      }
  
      @Test
      public void testFastByteComparisons()
      {
          byte[] bytes1 = new byte[128];
          for (int i = 0 ; i < 1000 ; i++)
          {
              rand.nextBytes(bytes1);
              for (int j = 0 ; j < 16 ; j++)
              {
                  byte[] bytes2 = Arrays.copyOf(bytes1, bytes1.length - j);
                  testTwiddleOneByteComparisons(bytes1, bytes2, 16, true, 1);
                  testTwiddleOneByteComparisons(bytes1, bytes2, 16, true, -1);
                  testTwiddleOneByteComparisons(bytes1, bytes2, 16, false, 1);
                  testTwiddleOneByteComparisons(bytes1, bytes2, 16, false, -1);
                  testTwiddleOneByteComparisons(bytes1, bytes2, 16, true, 128);
                  testTwiddleOneByteComparisons(bytes1, bytes2, 16, false, 128);
              }
          }
      }
  
      private void testTwiddleOneByteComparisons(byte[] bytes1, byte[] bytes2, int count, boolean start, int inc)
      {
          for (int j = 0 ; j < count ; j++)
          {
              int index = start ? j : bytes2.length - (j + 1);
              bytes2[index] += inc;
              testComparisons(bytes1, bytes2);
              bytes2[index] -= inc;
          }
      }
  
      private static ByteBuffer wrap1(byte[] bytes, boolean direct)
      {
          return slice(bytes, direct ? dbuf1 : hbuf1);
      }
  
      private static ByteBuffer wrap2(byte[] bytes, boolean direct)
      {
          return slice(bytes, direct ? dbuf2 : hbuf2);
      }
  
      private static ByteBuffer slice(byte[] bytes, ByteBuffer buf)
      {
          buf = buf.duplicate();
          buf.position((buf.limit() - bytes.length) / 2);
          buf.limit(buf.position() + bytes.length);
          buf.duplicate().put(bytes);
          return buf;
      }
  
      private void testComparisons(byte[] bytes1, byte[] bytes2)
      {
          testComparison(bytes1, bytes2);
          testComparison(bytes2, bytes1);
          testComparison(wrap1(bytes1, false), bytes2);
          testComparison(wrap2(bytes2, false), bytes1);
          testComparison(wrap1(bytes1, false), wrap2(bytes2, false));
          testComparison(wrap2(bytes2, false), wrap1(bytes1, false));
          testComparison(wrap1(bytes1, true), bytes2);
          testComparison(wrap2(bytes2, true), bytes1);
          testComparison(wrap1(bytes1, true), wrap2(bytes2, true));
          testComparison(wrap2(bytes2, true), wrap1(bytes1, true));
          testComparison(wrap1(bytes1, true), wrap2(bytes2, false));
          testComparison(wrap1(bytes1, false), wrap2(bytes2, true));
          testComparison(wrap2(bytes2, true), wrap1(bytes1, false));
          testComparison(wrap2(bytes2, false), wrap1(bytes1, true));
      }
  
      private void testComparison(byte[] bytes1, byte[] bytes2)
      {
          assert sameComparisonResult(PJO.compare(bytes1, 0, bytes1.length, bytes2, 0, bytes2.length), UO.compare(bytes1, 0, bytes1.length, bytes2, 0, bytes2.length));
          assert sameComparisonResult(PJO.compare(bytes1, 10, bytes1.length - 10, bytes2, 10, bytes2.length - 10), UO.compare(bytes1, 10, bytes1.length - 10, bytes2, 10, bytes2.length - 10));
      }
  
      private void testComparison(ByteBuffer bytes1, byte[] bytes2)
      {
          assert sameComparisonResult(PJO.compare(bytes1, bytes2, 0, bytes2.length), UO.compare(bytes1, bytes2, 0, bytes2.length));
          assert sameComparisonResult(PJO.compare(bytes1, bytes2, 10, bytes2.length - 10), UO.compare(bytes1, bytes2, 10, bytes2.length - 10));
      }
  
      private void testComparison(ByteBuffer bytes1, ByteBuffer bytes2)
      {
          assert sameComparisonResult(PJO.compare(bytes1, bytes2), UO.compare(bytes1, bytes2));
      }
  
      static boolean sameComparisonResult(int exp, int act)
      {
          if (exp < 0)
              return act < 0;
          if (exp > 0)
              return act > 0;
          return act == 0;
      }
  }
  