content: /*
   * Copyright (C) 2017 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 android.text;
  
  import android.annotation.IntRange;
  import android.annotation.NonNull;
  
  import com.android.internal.util.ArrayUtils;
  
  import libcore.util.EmptyArray;
  
  /**
   * Implements a growing array of int primitives.
   *
   * These arrays are NOT thread safe.
   *
   * @hide
   */
  public final class AutoGrowArray {
      private static final int MIN_CAPACITY_INCREMENT = 12;
      private static final int MAX_CAPACITY_TO_BE_KEPT = 10000;
  
      /**
       * Returns next capacity size.
       *
       * The returned capacity is larger than requested capacity.
       */
      private static int computeNewCapacity(int currentSize, int requested) {
          final int targetCapacity = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2)
                  ?  MIN_CAPACITY_INCREMENT : currentSize >> 1);
          return targetCapacity > requested ? targetCapacity : requested;
      }
  
      /**
       * An auto growing byte array.
       */
      public static class ByteArray {
  
          private @NonNull byte[] mValues;
          private @IntRange(from = 0) int mSize;
  
          /**
           * Creates an empty ByteArray with the default initial capacity.
           */
          public ByteArray() {
              this(10);
          }
  
          /**
           * Creates an empty ByteArray with the specified initial capacity.
           */
          public ByteArray(@IntRange(from = 0) int initialCapacity) {
              if (initialCapacity == 0) {
                  mValues = EmptyArray.BYTE;
              } else {
                  mValues = ArrayUtils.newUnpaddedByteArray(initialCapacity);
              }
              mSize = 0;
          }
  
          /**
           * Changes the size of this ByteArray. If this ByteArray is shrinked, the backing array
           * capacity is unchanged.
           */
          public void resize(@IntRange(from = 0) int newSize) {
              if (newSize > mValues.length) {
                  ensureCapacity(newSize - mSize);
              }
              mSize = newSize;
          }
  
          /**
           * Appends the specified value to the end of this array.
           */
          public void append(byte value) {
              ensureCapacity(1);
              mValues[mSize++] = value;
          }
  
          /**
           * Ensures capacity to append at least <code>count</code> values.
           */
          private void ensureCapacity(@IntRange int count) {
              final int requestedSize = mSize + count;
              if (requestedSize >= mValues.length) {
                  final int newCapacity = computeNewCapacity(mSize, requestedSize);
                  final byte[] newValues = ArrayUtils.newUnpaddedByteArray(newCapacity);
                  System.arraycopy(mValues, 0, newValues, 0, mSize);
                  mValues = newValues;
              }
          }
  
          /**
           * Removes all values from this array.
           */
          public void clear() {
              mSize = 0;
          }
  
          /**
           * Removes all values from this array and release the internal array object if it is too
           * large.
           */
          public void clearWithReleasingLargeArray() {
              clear();
              if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
                  mValues = EmptyArray.BYTE;
              }
          }
  
          /**
           * Returns the value at the specified position in this array.
           */
          public byte get(@IntRange(from = 0) int index) {
              return mValues[index];
          }
  
          /**
           * Sets the value at the specified position in this array.
           */
          public void set(@IntRange(from = 0) int index, byte value) {
              mValues[index] = value;
          }
  
          /**
           * Returns the number of values in this array.
           */
          public @IntRange(from = 0) int size() {
              return mSize;
          }
  
          /**
           * Returns internal raw array.
           *
           * Note that this array may have larger size than you requested.
           * Use size() instead for getting the actual array size.
           */
          public @NonNull byte[] getRawArray() {
              return mValues;
          }
      }
  
      /**
       * An auto growing int array.
       */
      public static class IntArray {
  
          private @NonNull int[] mValues;
          private @IntRange(from = 0) int mSize;
  
          /**
           * Creates an empty IntArray with the default initial capacity.
           */
          public IntArray() {
              this(10);
          }
  
          /**
           * Creates an empty IntArray with the specified initial capacity.
           */
          public IntArray(@IntRange(from = 0) int initialCapacity) {
              if (initialCapacity == 0) {
                  mValues = EmptyArray.INT;
              } else {
                  mValues = ArrayUtils.newUnpaddedIntArray(initialCapacity);
              }
              mSize = 0;
          }
  
          /**
           * Changes the size of this IntArray. If this IntArray is shrinked, the backing array
           * capacity is unchanged.
           */
          public void resize(@IntRange(from = 0) int newSize) {
              if (newSize > mValues.length) {
                  ensureCapacity(newSize - mSize);
              }
              mSize = newSize;
          }
  
          /**
           * Appends the specified value to the end of this array.
           */
          public void append(int value) {
              ensureCapacity(1);
              mValues[mSize++] = value;
          }
  
          /**
           * Ensures capacity to append at least <code>count</code> values.
           */
          private void ensureCapacity(@IntRange(from = 0) int count) {
              final int requestedSize = mSize + count;
              if (requestedSize >= mValues.length) {
                  final int newCapacity = computeNewCapacity(mSize, requestedSize);
                  final int[] newValues = ArrayUtils.newUnpaddedIntArray(newCapacity);
                  System.arraycopy(mValues, 0, newValues, 0, mSize);
                  mValues = newValues;
              }
          }
  
          /**
           * Removes all values from this array.
           */
          public void clear() {
              mSize = 0;
          }
  
          /**
           * Removes all values from this array and release the internal array object if it is too
           * large.
           */
          public void clearWithReleasingLargeArray() {
              clear();
              if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
                  mValues = EmptyArray.INT;
              }
          }
  
          /**
           * Returns the value at the specified position in this array.
           */
          public int get(@IntRange(from = 0) int index) {
              return mValues[index];
          }
  
          /**
           * Sets the value at the specified position in this array.
           */
          public void set(@IntRange(from = 0) int index, int value) {
              mValues[index] = value;
          }
  
          /**
           * Returns the number of values in this array.
           */
          public @IntRange(from = 0) int size() {
              return mSize;
          }
  
          /**
           * Returns internal raw array.
           *
           * Note that this array may have larger size than you requested.
           * Use size() instead for getting the actual array size.
           */
          public @NonNull int[] getRawArray() {
              return mValues;
          }
      }
  
      /**
       * An auto growing float array.
       */
      public static class FloatArray {
  
          private @NonNull float[] mValues;
          private @IntRange(from = 0) int mSize;
  
          /**
           * Creates an empty FloatArray with the default initial capacity.
           */
          public FloatArray() {
              this(10);
          }
  
          /**
           * Creates an empty FloatArray with the specified initial capacity.
           */
          public FloatArray(@IntRange(from = 0) int initialCapacity) {
              if (initialCapacity == 0) {
                  mValues = EmptyArray.FLOAT;
              } else {
                  mValues = ArrayUtils.newUnpaddedFloatArray(initialCapacity);
              }
              mSize = 0;
          }
  
          /**
           * Changes the size of this FloatArray. If this FloatArray is shrinked, the backing array
           * capacity is unchanged.
           */
          public void resize(@IntRange(from = 0) int newSize) {
              if (newSize > mValues.length) {
                  ensureCapacity(newSize - mSize);
              }
              mSize = newSize;
          }
  
          /**
           * Appends the specified value to the end of this array.
           */
          public void append(float value) {
              ensureCapacity(1);
              mValues[mSize++] = value;
          }
  
          /**
           * Ensures capacity to append at least <code>count</code> values.
           */
          private void ensureCapacity(int count) {
              final int requestedSize = mSize + count;
              if (requestedSize >= mValues.length) {
                  final int newCapacity = computeNewCapacity(mSize, requestedSize);
                  final float[] newValues = ArrayUtils.newUnpaddedFloatArray(newCapacity);
                  System.arraycopy(mValues, 0, newValues, 0, mSize);
                  mValues = newValues;
              }
          }
  
          /**
           * Removes all values from this array.
           */
          public void clear() {
              mSize = 0;
          }
  
          /**
           * Removes all values from this array and release the internal array object if it is too
           * large.
           */
          public void clearWithReleasingLargeArray() {
              clear();
              if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
                  mValues = EmptyArray.FLOAT;
              }
          }
  
          /**
           * Returns the value at the specified position in this array.
           */
          public float get(@IntRange(from = 0) int index) {
              return mValues[index];
          }
  
          /**
           * Sets the value at the specified position in this array.
           */
          public void set(@IntRange(from = 0) int index, float value) {
              mValues[index] = value;
          }
  
          /**
           * Returns the number of values in this array.
           */
          public @IntRange(from = 0) int size() {
              return mSize;
          }
  
          /**
           * Returns internal raw array.
           *
           * Note that this array may have larger size than you requested.
           * Use size() instead for getting the actual array size.
           */
          public @NonNull float[] getRawArray() {
              return mValues;
          }
      }
  }
  