| /* |
| * 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.proto.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; |
| }); |
| } |
| } |