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