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