summaryrefslogtreecommitdiff
path: root/common/framework/com/android/net/module/util/BitUtils.java
blob: 3062d8cf31b6c17bc78815a82cee822285ade55c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
 * Copyright (C) 2022 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.net.module.util;

import android.annotation.NonNull;
import android.annotation.Nullable;

/**
 * @hide
 */
public class BitUtils {
    /**
     * Unpacks long value into an array of bits.
     */
    public static int[] unpackBits(long val) {
        int size = Long.bitCount(val);
        int[] result = new int[size];
        int index = 0;
        int bitPos = 0;
        while (val != 0) {
            if ((val & 1) == 1) result[index++] = bitPos;
            val = val >>> 1;
            bitPos++;
        }
        return result;
    }

    /**
     * Packs a list of ints in the same way as packBits()
     *
     * Each passed int is the rank of a bit that should be set in the returned long.
     * Example : passing (1,3) will return in 0b00001010 and passing (5,6,0) will return 0b01100001
     *
     * @param bits bits to pack
     * @return a long with the specified bits set.
     */
    public static long packBitList(int... bits) {
        return packBits(bits);
    }

    /**
     * Packs array of bits into a long value.
     *
     * Each passed int is the rank of a bit that should be set in the returned long.
     * Example : passing [1,3] will return in 0b00001010 and passing [5,6,0] will return 0b01100001
     *
     * @param bits bits to pack
     * @return a long with the specified bits set.
     */
    public static long packBits(int[] bits) {
        long packed = 0;
        for (int b : bits) {
            packed |= (1L << b);
        }
        return packed;
    }

    /**
     * An interface for a function that can retrieve a name associated with an int.
     *
     * This is useful for bitfields like network capabilities or network score policies.
     */
    @FunctionalInterface
    public interface NameOf {
        /** Retrieve the name associated with the passed value */
        String nameOf(int value);
    }

    /**
     * Given a bitmask and a name fetcher, append names of all set bits to the builder
     *
     * This method takes all bit sets in the passed bitmask, will figure out the name associated
     * with the weight of each bit with the passed name fetcher, and append each name to the
     * passed StringBuilder, separated by the passed separator.
     *
     * For example, if the bitmask is 0110, and the name fetcher return "BIT_1" to "BIT_4" for
     * numbers from 1 to 4, and the separator is "&", this method appends "BIT_2&BIT3" to the
     * StringBuilder.
     */
    public static void appendStringRepresentationOfBitMaskToStringBuilder(@NonNull StringBuilder sb,
            long bitMask, @NonNull NameOf nameFetcher, @NonNull String separator) {
        int bitPos = 0;
        boolean firstElementAdded = false;
        while (bitMask != 0) {
            if ((bitMask & 1) != 0) {
                if (firstElementAdded) {
                    sb.append(separator);
                } else {
                    firstElementAdded = true;
                }
                sb.append(nameFetcher.nameOf(bitPos));
            }
            bitMask >>>= 1;
            ++bitPos;
        }
    }

    /**
     * Returns a short but human-readable string of updates between an old and a new bit fields.
     *
     * @param oldVal the old bit field to diff from
     * @param newVal the new bit field to diff to
     * @return a string fit for logging differences, or null if no differences.
     *         this method cannot return the empty string.
     */
    @Nullable
    public static String describeDifferences(final long oldVal, final long newVal,
            @NonNull final NameOf nameFetcher) {
        final long changed = oldVal ^ newVal;
        if (0 == changed) return null;
        // If the control reaches here, there are changes (additions, removals, or both) so
        // the code below is guaranteed to add something to the string and can't return "".
        final long removed = oldVal & changed;
        final long added = newVal & changed;
        final StringBuilder sb = new StringBuilder();
        if (0 != removed) {
            sb.append("-");
            appendStringRepresentationOfBitMaskToStringBuilder(sb, removed, nameFetcher, "-");
        }
        if (0 != added) {
            sb.append("+");
            appendStringRepresentationOfBitMaskToStringBuilder(sb, added, nameFetcher, "+");
        }
        return sb.toString();
    }
}