/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.imagepipeline.memory;

import android.util.SparseIntArray;
import com.facebook.common.internal.Preconditions;
import com.facebook.common.memory.ByteArrayPool;
import com.facebook.common.memory.MemoryTrimmableRegistry;
import com.facebook.infer.annotation.Nullsafe;
import javax.annotation.concurrent.ThreadSafe;

/**
 * A pool of byte arrays. The pool manages a number of byte arrays of a predefined set of sizes.
 * This set of sizes is typically, but not required to be, based on powers of 2. The pool supports a
 * get/release paradigm. On a get request, the pool attempts to find an existing byte array whose
 * size is at least as big as the requested size. On a release request, the pool adds the byte array
 * to the appropriate bucket. This byte array can then be used for a subsequent get request.
 */
@Nullsafe(Nullsafe.Mode.LOCAL)
@ThreadSafe
public class GenericByteArrayPool extends BasePool<byte[]> implements ByteArrayPool {
  private final int[] mBucketSizes;

  /**
   * Creates a new instance of the GenericByteArrayPool class
   *
   * @param memoryTrimmableRegistry the memory manager to register with
   * @param poolParams provider for pool parameters
   * @param poolStatsTracker
   */
  public GenericByteArrayPool(
      MemoryTrimmableRegistry memoryTrimmableRegistry,
      PoolParams poolParams,
      PoolStatsTracker poolStatsTracker) {
    super(memoryTrimmableRegistry, poolParams, poolStatsTracker);
    final SparseIntArray bucketSizes = Preconditions.checkNotNull(poolParams.bucketSizes);
    this.mBucketSizes = new int[bucketSizes.size()];
    for (int i = 0; i < bucketSizes.size(); ++i) {
      mBucketSizes[i] = bucketSizes.keyAt(i);
    }
    initialize();
  }

  /**
   * Gets the smallest buffer size supported by the pool
   *
   * @return the smallest buffer size supported by the pool
   */
  public int getMinBufferSize() {
    return mBucketSizes[0];
  }

  /**
   * Allocate a buffer greater than or equal to the specified size
   *
   * @param bucketedSize size of the buffer requested
   * @return a byte array of the specified or larger size. Null if the size is invalid
   */
  @Override
  protected byte[] alloc(int bucketedSize) {
    return new byte[bucketedSize];
  }

  /**
   * Frees the 'value'
   *
   * @param value the value to free
   */
  @Override
  protected void free(byte[] value) {
    Preconditions.checkNotNull(value);
    // do nothing. Let the GC take care of this
  }

  /**
   * Gets the size in bytes for the given 'bucketed' size
   *
   * @param bucketedSize the bucketed size
   * @return size in bytes
   */
  @Override
  protected int getSizeInBytes(int bucketedSize) {
    return bucketedSize;
  }

  /**
   * Get the 'bucketed' size for the given request size. The 'bucketed' size is a size that is the
   * same or larger than the request size. We walk through our list of pre-defined bucket sizes, and
   * use that to determine the smallest bucket size that is larger than the requested size. If no
   * such 'bucketedSize' is found, then we simply return "requestSize"
   *
   * @param requestSize the logical request size
   * @return the bucketed size
   * @throws InvalidSizeException, if the requested size was invalid
   */
  @Override
  protected int getBucketedSize(int requestSize) {
    int intRequestSize = requestSize;
    if (intRequestSize <= 0) {
      throw new InvalidSizeException(requestSize);
    }

    // find the smallest bucketed size that is larger than the requested size
    for (int bucketedSize : mBucketSizes) {
      if (bucketedSize >= intRequestSize) {
        return bucketedSize;
      }
    }

    // requested size doesn't match our existing buckets - just return the requested size
    // this will eventually translate into a plain alloc/free paradigm
    return requestSize;
  }

  /**
   * Gets the bucketed size of the value
   *
   * @param value the value
   * @return just the length of the value
   */
  @Override
  protected int getBucketedSizeForValue(byte[] value) {
    Preconditions.checkNotNull(value);
    return value.length;
  }
}

@@ -1681,35 +1681,8 @@
 s = 
-Preconditions.checkNotNull(
 pool
@@ -1699,19 +1699,51 @@
 ketSizes
-)
 ;%0A
+    if (bucketSizes != null) %7B%0A  
     this
@@ -1787,24 +1787,26 @@
 ize()%5D;%0A    
+ 
+ 
 for (int i =
@@ -1846,16 +1846,18 @@
 %7B%0A      
+  
 mBucketS
@@ -1888,16 +1888,75 @@
 yAt(i);%0A
+      %7D%0A    %7D else %7B%0A      this.mBucketSizes = new int%5B0%5D;%0A
     %7D%0A  
@@ -2167,19 +2167,49 @@
 ketSizes
-%5B0%5D
+.length %3E 0 ? mBucketSizes%5B0%5D : 0
 ;%0A  %7D%0A%0A 

Remove non-null invariant on PoolParams bucket sizes