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.test.microbench;
  
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.nio.ByteBuffer;
  import java.nio.charset.StandardCharsets;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Random;
  import java.util.concurrent.TimeUnit;
  import java.util.function.BiFunction;
  
  import net.nicoulaj.compilecommand.annotations.Inline;
  import org.apache.cassandra.db.marshal.AbstractType;
  import org.apache.cassandra.db.marshal.BytesType;
  import org.apache.cassandra.db.marshal.DecimalType;
  import org.apache.cassandra.db.marshal.IntegerType;
  import org.apache.cassandra.db.marshal.TypeParser;
  import org.apache.cassandra.db.marshal.UTF8Type;
  import org.apache.cassandra.utils.bytecomparable.ByteComparable;
  import org.apache.cassandra.utils.bytecomparable.ByteSource;
  import org.openjdk.jmh.annotations.Benchmark;
  import org.openjdk.jmh.annotations.BenchmarkMode;
  import org.openjdk.jmh.annotations.Fork;
  import org.openjdk.jmh.annotations.Level;
  import org.openjdk.jmh.annotations.Measurement;
  import org.openjdk.jmh.annotations.Mode;
  import org.openjdk.jmh.annotations.OutputTimeUnit;
  import org.openjdk.jmh.annotations.Param;
  import org.openjdk.jmh.annotations.Scope;
  import org.openjdk.jmh.annotations.Setup;
  import org.openjdk.jmh.annotations.State;
  import org.openjdk.jmh.annotations.Threads;
  import org.openjdk.jmh.annotations.Warmup;
  
  @BenchmarkMode(Mode.AverageTime)
  @OutputTimeUnit(TimeUnit.MICROSECONDS)
  @Warmup(iterations = 5, time = 1)
  @Measurement(iterations = 5, time = 2)
  @Fork(value = 1,jvmArgsAppend = { "-Xmx4G", "-Xms4G", "-Djmh.executor=CUSTOM", "-Djmh.executor.class=org.apache.cassandra.test.microbench.FastThreadExecutor"})
  @Threads(1)
  @State(Scope.Benchmark)
  public class AbstractTypeByteSourceDecodingBench
  {
  
      private static final ByteComparable.Version LATEST = ByteComparable.Version.OSS50;
  
      private static final Map<AbstractType, BiFunction<Random, Integer, ByteSource.Peekable>> PEEKABLE_GENERATOR_BY_TYPE = new HashMap<>();
      static
      {
          PEEKABLE_GENERATOR_BY_TYPE.put(UTF8Type.instance, (prng, length) ->
          {
              byte[] randomBytes = new byte[length];
              prng.nextBytes(randomBytes);
              return ByteSource.peekable(ByteSource.of(new String(randomBytes, StandardCharsets.UTF_8), LATEST));
          });
          PEEKABLE_GENERATOR_BY_TYPE.put(BytesType.instance, (prng, length) ->
          {
              byte[] randomBytes = new byte[length];
              prng.nextBytes(randomBytes);
              return ByteSource.peekable(ByteSource.of(randomBytes, LATEST));
          });
          PEEKABLE_GENERATOR_BY_TYPE.put(IntegerType.instance, (prng, length) ->
          {
              BigInteger randomVarint = BigInteger.valueOf(prng.nextLong());
              for (int i = 1; i < length / 8; ++i)
                  randomVarint = randomVarint.multiply(BigInteger.valueOf(prng.nextLong()));
              return ByteSource.peekable(IntegerType.instance.asComparableBytes(IntegerType.instance.decompose(randomVarint), LATEST));
          });
          PEEKABLE_GENERATOR_BY_TYPE.put(DecimalType.instance, (prng, length) ->
          {
              BigInteger randomMantissa = BigInteger.valueOf(prng.nextLong());
              for (int i = 1; i < length / 8; ++i)
                  randomMantissa = randomMantissa.multiply(BigInteger.valueOf(prng.nextLong()));
              int randomScale = prng.nextInt(Integer.MAX_VALUE >> 1) + Integer.MAX_VALUE >> 1;
              BigDecimal randomDecimal = new BigDecimal(randomMantissa, randomScale);
              return ByteSource.peekable(DecimalType.instance.asComparableBytes(DecimalType.instance.decompose(randomDecimal), LATEST));
          });
      }
  
      private Random prng = new Random();
  
      @Param({"32", "128", "512"})
      private int length;
  
      @Param({"UTF8Type", "BytesType", "IntegerType", "DecimalType"})
      private String abstractTypeName;
  
      private AbstractType abstractType;
      private BiFunction<Random, Integer, ByteSource.Peekable> peekableGenerator;
  
      @Setup(Level.Trial)
      public void setup()
      {
          abstractType = TypeParser.parse(abstractTypeName);
          peekableGenerator = PEEKABLE_GENERATOR_BY_TYPE.get(abstractType);
      }
  
      @Inline
      private ByteSource.Peekable randomPeekableBytes()
      {
          return peekableGenerator.apply(prng, length);
      }
  
      @Benchmark
      public int baseline()
      {
          // Getting the source is not enough as its content is produced on next() calls.
          ByteSource.Peekable source = randomPeekableBytes();
          int count = 0;
          while (source.next() != ByteSource.END_OF_STREAM)
              ++count;
          return count;
      }
  
      @Benchmark
      public ByteBuffer fromComparableBytes()
      {
          ByteSource.Peekable peekableBytes = randomPeekableBytes();
          return abstractType.fromComparableBytes(peekableBytes, ByteComparable.Version.OSS50);
      }
  }
  