1 /*
2  * Copyright (C) 2022 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.net.module.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 
22 /**
23  * @hide
24  */
25 public class BitUtils {
26     /**
27      * Unpacks long value into an array of bits.
28      */
unpackBits(long val)29     public static int[] unpackBits(long val) {
30         int size = Long.bitCount(val);
31         int[] result = new int[size];
32         int index = 0;
33         int bitPos = 0;
34         while (val != 0) {
35             if ((val & 1) == 1) result[index++] = bitPos;
36             val = val >>> 1;
37             bitPos++;
38         }
39         return result;
40     }
41 
42     /**
43      * Packs a list of ints in the same way as packBits()
44      *
45      * Each passed int is the rank of a bit that should be set in the returned long.
46      * Example : passing (1,3) will return in 0b00001010 and passing (5,6,0) will return 0b01100001
47      *
48      * @param bits bits to pack
49      * @return a long with the specified bits set.
50      */
packBitList(int... bits)51     public static long packBitList(int... bits) {
52         return packBits(bits);
53     }
54 
55     /**
56      * Packs array of bits into a long value.
57      *
58      * Each passed int is the rank of a bit that should be set in the returned long.
59      * Example : passing [1,3] will return in 0b00001010 and passing [5,6,0] will return 0b01100001
60      *
61      * @param bits bits to pack
62      * @return a long with the specified bits set.
63      */
packBits(int[] bits)64     public static long packBits(int[] bits) {
65         long packed = 0;
66         for (int b : bits) {
67             packed |= (1L << b);
68         }
69         return packed;
70     }
71 
72     /**
73      * An interface for a function that can retrieve a name associated with an int.
74      *
75      * This is useful for bitfields like network capabilities or network score policies.
76      */
77     @FunctionalInterface
78     public interface NameOf {
79         /** Retrieve the name associated with the passed value */
nameOf(int value)80         String nameOf(int value);
81     }
82 
83     /**
84      * Given a bitmask and a name fetcher, append names of all set bits to the builder
85      *
86      * This method takes all bit sets in the passed bitmask, will figure out the name associated
87      * with the weight of each bit with the passed name fetcher, and append each name to the
88      * passed StringBuilder, separated by the passed separator.
89      *
90      * For example, if the bitmask is 0110, and the name fetcher return "BIT_1" to "BIT_4" for
91      * numbers from 1 to 4, and the separator is "&", this method appends "BIT_2&BIT3" to the
92      * StringBuilder.
93      */
appendStringRepresentationOfBitMaskToStringBuilder(@onNull StringBuilder sb, long bitMask, @NonNull NameOf nameFetcher, @NonNull String separator)94     public static void appendStringRepresentationOfBitMaskToStringBuilder(@NonNull StringBuilder sb,
95             long bitMask, @NonNull NameOf nameFetcher, @NonNull String separator) {
96         int bitPos = 0;
97         boolean firstElementAdded = false;
98         while (bitMask != 0) {
99             if ((bitMask & 1) != 0) {
100                 if (firstElementAdded) {
101                     sb.append(separator);
102                 } else {
103                     firstElementAdded = true;
104                 }
105                 sb.append(nameFetcher.nameOf(bitPos));
106             }
107             bitMask >>>= 1;
108             ++bitPos;
109         }
110     }
111 
112     /**
113      * Returns a short but human-readable string of updates between an old and a new bit fields.
114      *
115      * @param oldVal the old bit field to diff from
116      * @param newVal the new bit field to diff to
117      * @return a string fit for logging differences, or null if no differences.
118      *         this method cannot return the empty string.
119      */
120     @Nullable
describeDifferences(final long oldVal, final long newVal, @NonNull final NameOf nameFetcher)121     public static String describeDifferences(final long oldVal, final long newVal,
122             @NonNull final NameOf nameFetcher) {
123         final long changed = oldVal ^ newVal;
124         if (0 == changed) return null;
125         // If the control reaches here, there are changes (additions, removals, or both) so
126         // the code below is guaranteed to add something to the string and can't return "".
127         final long removed = oldVal & changed;
128         final long added = newVal & changed;
129         final StringBuilder sb = new StringBuilder();
130         if (0 != removed) {
131             sb.append("-");
132             appendStringRepresentationOfBitMaskToStringBuilder(sb, removed, nameFetcher, "-");
133         }
134         if (0 != added) {
135             sb.append("+");
136             appendStringRepresentationOfBitMaskToStringBuilder(sb, added, nameFetcher, "+");
137         }
138         return sb.toString();
139     }
140 }
141