1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi.util; 18 19 import android.util.SparseIntArray; 20 21 import com.android.server.wifi.proto.nano.WifiMetricsProto.Int32Count; 22 23 import java.lang.reflect.Array; 24 import java.util.Iterator; 25 26 /** 27 * Utility class for counting occurrences of int keys using an int counter. 28 * Note: this class can also be used for counting occurrences of enum values. Just define a new 29 * Protobuf message, and call {@link #toProto(Class, ProtobufConverter)} with a 30 * {@link ProtobufConverter} that populates your custom Protobuf message type. 31 */ 32 public class IntCounter extends SparseIntArray implements Iterable<IntCounter.KeyCount> { 33 34 /** 35 * A class to represent the number of occurrences for an int key. 36 */ 37 public static class KeyCount { 38 public int key; 39 public int count; 40 KeyCount(int key, int count)41 public KeyCount(int key, int count) { 42 this.key = key; 43 this.count = count; 44 } 45 } 46 47 /** 48 * Calls to {@link #add(int, int)}/{@link #increment(int)} for all keys < keyLowerBound are 49 * instead attributed to keyLowerBound. 50 */ 51 public final int keyLowerBound; 52 /** 53 * Calls to {@link #add(int, int)}/{@link #increment(int)} for all keys > keyUpperBound are 54 * instead attributed to keyUpperBound. 55 */ 56 public final int keyUpperBound; 57 IntCounter()58 public IntCounter() { 59 this(Integer.MIN_VALUE, Integer.MAX_VALUE); 60 } 61 62 /** 63 * Clamps keys to the range between keyLowerBound and keyUpperBound. See {@link #keyLowerBound} 64 * and {@link #keyUpperBound}. 65 */ IntCounter(int keyLowerBound, int keyUpperBound)66 public IntCounter(int keyLowerBound, int keyUpperBound) { 67 this.keyLowerBound = keyLowerBound; 68 this.keyUpperBound = keyUpperBound; 69 } 70 71 /** 72 * Increments the count of a key by 1. 73 */ increment(int key)74 public void increment(int key) { 75 add(key, 1); 76 } 77 78 /** 79 * Increments the count of a key by <code>count</code>. 80 */ add(int key, int count)81 public void add(int key, int count) { 82 key = Math.max(keyLowerBound, Math.min(key, keyUpperBound)); 83 int curCount = get(key); // returns 0 if key not found 84 put(key, curCount + count); 85 } 86 87 /** 88 * Iterates over all (key, count) pairs. 89 */ 90 @Override iterator()91 public Iterator<KeyCount> iterator() { 92 return new Iterator<KeyCount>() { 93 private int mIndex = 0; 94 95 @Override 96 public boolean hasNext() { 97 return mIndex < size(); 98 } 99 100 @Override 101 public KeyCount next() { 102 KeyCount kc = new KeyCount(keyAt(mIndex), valueAt(mIndex)); 103 mIndex++; 104 return kc; 105 } 106 }; 107 } 108 109 /** 110 * Converter function that converts a single (key, count) pair to a Protobuf object. 111 * @param <T> the type of the Protobuf output. 112 */ 113 public interface ProtobufConverter<T> { 114 /** 115 * Converter function that converts a single (key, count) pair to a Protobuf object. 116 * @param key the key that we are counting occurrences for 117 * @param count the number of occurrences for this key 118 * @return the Protobuf output 119 */ convert(int key, int count)120 T convert(int key, int count); 121 } 122 123 /** 124 * Converts this object to a custom Protobuf representation. 125 * @param protoClass the class object for the Protobuf type. 126 * @param converter a conversion function. 127 * @param <T> the type of the Protobuf output. 128 * @return an array of Protobuf representation of buckets generated by the converter function. 129 */ toProto(Class<T> protoClass, ProtobufConverter<T> converter)130 public <T> T[] toProto(Class<T> protoClass, ProtobufConverter<T> converter) { 131 @SuppressWarnings("unchecked") 132 T[] output = (T[]) Array.newInstance(protoClass, size()); 133 int i = 0; 134 for (KeyCount kc : this) { 135 output[i] = converter.convert(kc.key, kc.count); 136 i++; 137 } 138 return output; 139 } 140 141 /** 142 * Converts this object to a standard Protobuf representation. 143 */ toProto()144 public Int32Count[] toProto() { 145 return toProto(Int32Count.class, (key, count) -> { 146 Int32Count entry = new Int32Count(); 147 entry.key = key; 148 entry.count = count; 149 return entry; 150 }); 151 } 152 } 153