content: /*
   * Copyright (C) 2019 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 com.android.server.wifi.util;
  
  import android.util.SparseIntArray;
  
  import com.android.server.wifi.nano.WifiMetricsProto.Int32Count;
  
  import java.lang.reflect.Array;
  import java.util.Iterator;
  
  /**
   * Utility class for counting occurrences of int keys using an int counter.
   * Note: this class can also be used for counting occurrences of enum values. Just define a new
   * Protobuf message, and call {@link #toProto(Class, ProtobufConverter)} with a
   * {@link ProtobufConverter} that populates your custom Protobuf message type.
   */
  public class IntCounter extends SparseIntArray implements Iterable<IntCounter.KeyCount> {
  
      /**
       * A class to represent the number of occurrences for an int key.
       */
      public static class KeyCount {
          public int key;
          public int count;
  
          public KeyCount(int key, int count) {
              this.key = key;
              this.count = count;
          }
      }
  
      /**
       * Calls to {@link #add(int, int)}/{@link #increment(int)} for all keys < keyLowerBound are
       * instead attributed to keyLowerBound.
       */
      public final int keyLowerBound;
      /**
       * Calls to {@link #add(int, int)}/{@link #increment(int)} for all keys > keyUpperBound are
       * instead attributed to keyUpperBound.
       */
      public final int keyUpperBound;
  
      public IntCounter() {
          this(Integer.MIN_VALUE, Integer.MAX_VALUE);
      }
  
      /**
       * Clamps keys to the range between keyLowerBound and keyUpperBound. See {@link #keyLowerBound}
       * and {@link #keyUpperBound}.
       */
      public IntCounter(int keyLowerBound, int keyUpperBound) {
          this.keyLowerBound = keyLowerBound;
          this.keyUpperBound = keyUpperBound;
      }
  
      /**
       * Increments the count of a key by 1.
       */
      public void increment(int key) {
          add(key, 1);
      }
  
      /**
       * Increments the count of a key by <code>count</code>.
       */
      public void add(int key, int count) {
          key = Math.max(keyLowerBound, Math.min(key, keyUpperBound));
          int curCount = get(key); // returns 0 if key not found
          put(key, curCount + count);
      }
  
      /**
       * Iterates over all (key, count) pairs.
       */
      @Override
      public Iterator<KeyCount> iterator() {
          return new Iterator<KeyCount>() {
              private int mIndex = 0;
  
              @Override
              public boolean hasNext() {
                  return mIndex < size();
              }
  
              @Override
              public KeyCount next() {
                  KeyCount kc = new KeyCount(keyAt(mIndex), valueAt(mIndex));
                  mIndex++;
                  return kc;
              }
          };
      }
  
      /**
       * Converter function that converts a single (key, count) pair to a Protobuf object.
       * @param <T> the type of the Protobuf output.
       */
      public interface ProtobufConverter<T> {
          /**
           * Converter function that converts a single (key, count) pair to a Protobuf object.
           * @param key the key that we are counting occurrences for
           * @param count the number of occurrences for this key
           * @return the Protobuf output
           */
          T convert(int key, int count);
      }
  
      /**
       * Converts this object to a custom Protobuf representation.
       * @param protoClass the class object for the Protobuf type.
       * @param converter a conversion function.
       * @param <T> the type of the Protobuf output.
       * @return an array of Protobuf representation of buckets generated by the converter function.
       */
      public <T> T[] toProto(Class<T> protoClass, ProtobufConverter<T> converter) {
          @SuppressWarnings("unchecked")
          T[] output = (T[]) Array.newInstance(protoClass, size());
          int i = 0;
          for (KeyCount kc : this) {
              output[i] = converter.convert(kc.key, kc.count);
              i++;
          }
          return output;
      }
  
      /**
       * Converts this object to a standard Protobuf representation.
       */
      public Int32Count[] toProto() {
          return toProto(Int32Count.class, (key, count) -> {
              Int32Count entry = new Int32Count();
              entry.key = key;
              entry.count = count;
              return entry;
          });
      }
  }
  