File: bit_string.h

package info (click to toggle)
android-platform-art 11.0.0%2Br48-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 78,932 kB
  • sloc: cpp: 459,858; java: 163,268; asm: 22,644; python: 9,815; sh: 6,330; ansic: 4,117; xml: 2,855; perl: 77; makefile: 73
file content (299 lines) | stat: -rw-r--r-- 10,424 bytes parent folder | download | duplicates (3)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
 * Copyright (C) 2017 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.
 */

#ifndef ART_LIBARTBASE_BASE_BIT_STRING_H_
#define ART_LIBARTBASE_BASE_BIT_STRING_H_

#include "bit_struct.h"
#include "bit_utils.h"

#include <ostream>

namespace art {

struct BitStringChar;
inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc);

/**
 * A BitStringChar is a light-weight wrapper to read/write a single character
 * into a BitString, while restricting the bitlength.
 *
 * This is only intended for reading/writing into temporaries, as the representation is
 * inefficient for memory (it uses a word for the character and another word for the bitlength).
 *
 * See also BitString below.
 */
struct BitStringChar {
  using StorageType = uint32_t;
  static_assert(std::is_unsigned<StorageType>::value, "BitStringChar::StorageType must be unsigned");

  // BitStringChars are always zero-initialized by default. Equivalent to BitStringChar(0,0).
  BitStringChar() : data_(0u), bitlength_(0u) { }

  // Create a new BitStringChar whose data bits can be at most bitlength.
  BitStringChar(StorageType data, size_t bitlength)
      : data_(data), bitlength_(bitlength) {
    // All bits higher than bitlength must be set to 0.
    DCHECK_EQ(0u, data & ~MaskLeastSignificant(bitlength_))
        << "BitStringChar data out of range, data: " << data << ", bitlength: " << bitlength;
  }

  // What is the bitlength constraint for this character?
  // (Data could use less bits, but this is the maximum bit capacity at that BitString position).
  size_t GetBitLength() const {
    return bitlength_;
  }

  // Is there any capacity in this BitStringChar to store any data?
  bool IsEmpty() const {
    return bitlength_ == 0;
  }

  explicit operator StorageType() const {
    return data_;
  }

  bool operator==(StorageType storage) const {
    return data_ == storage;
  }

  bool operator!=(StorageType storage) const {
    return !(*this == storage);
  }

  // Compare equality against another BitStringChar. Note: bitlength is ignored.
  bool operator==(const BitStringChar& other) const {
    return data_ == other.data_;
  }

  // Compare non-equality against another BitStringChar. Note: bitlength is ignored.
  bool operator!=(const BitStringChar& other) const {
    return !(*this == other);
  }

  // Add a BitStringChar with an integer. The resulting BitStringChar's data must still fit within
  // this BitStringChar's bit length.
  BitStringChar operator+(StorageType storage) const {
    DCHECK_LE(storage, MaximumValue().data_ - data_) << "Addition would overflow " << *this;
    return BitStringChar(data_ + storage, bitlength_);
  }

  // Get the maximum representible value with the same bitlength.
  // (Useful to figure out the maximum value for this BitString position.)
  BitStringChar MaximumValue() const {
    StorageType maximimum_data = MaxInt<StorageType>(bitlength_);
    return BitStringChar(maximimum_data, bitlength_);
  }

 private:
  StorageType data_;  // Unused bits (outside of bitlength) are 0.
  size_t bitlength_;
  // Logically const. Physically non-const so operator= still works.
};

// Print e.g. "BitStringChar<10>(123)" where 10=bitlength, 123=data.
inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc) {
  os << "BitStringChar<" << bc.GetBitLength() << ">("
     << static_cast<BitStringChar::StorageType>(bc) << ")";
  return os;
}

/**
 *                           BitString
 *
 * MSB (most significant bit)                                LSB
 *  +------------+-----+------------+------------+------------+
 *  |            |     |            |            |            |
 *  |   CharN    | ... |    Char2   |   Char1    |   Char0    |
 *  |            |     |            |            |            |
 *  +------------+-----+------------+------------+------------+
 *   <- len[N] ->  ...  <- len[2] -> <- len[1] -> <- len[0] ->
 *
 * Stores up to "N+1" characters in a subset of a machine word. Each character has a different
 * bitlength, as defined by len[pos]. This BitString can be nested inside of a BitStruct
 * (see e.g. SubtypeCheckBitsAndStatus).
 *
 * Definitions:
 *
 *  "ABCDE...K"       := [A,B,C,D,E, ... K] + [0]*(N-idx(K)) s.t. N >= K.
 *                    // Padded with trailing 0s to fit (N+1) bitstring chars.
 *  MaxBitstringLen   := N+1
 *  StrLen(Bitstring) := I s.t. (I == 0 OR Char(I-1) != 0)
 *                              AND forall char in CharI..CharN : char == 0
 *                    // = Maximum length - the # of consecutive trailing zeroes.
 *  Bitstring[N]      := CharN
 *  Bitstring[I..N)   := [CharI, CharI+1, ... CharN-1]
 *
 * (These are used by the SubtypeCheckInfo definitions and invariants, see subtype_check_info.h)
 */
struct BitString {
  using StorageType = BitStringChar::StorageType;

  // As this is meant to be used only with "SubtypeCheckInfo",
  // the bitlengths and the maximum string length is tuned by maximizing the coverage of "Assigned"
  // bitstrings for instance-of and check-cast targets during Optimizing compilation.
  static constexpr size_t kBitSizeAtPosition[] = {12, 4, 11};         // len[] from header docs.
  static constexpr size_t kCapacity = arraysize(kBitSizeAtPosition);  // MaxBitstringLen above.

  // How many bits are needed to represent BitString[0..position)?
  static constexpr size_t GetBitLengthTotalAtPosition(size_t position) {
    size_t idx = 0;
    size_t sum = 0;
    while (idx < position && idx < kCapacity) {
      sum += kBitSizeAtPosition[idx];
      ++idx;
    }
    // TODO: precompute with CreateArray helper.

    return sum;
  }

  // What is the least-significant-bit for a position?
  // (e.g. to use with BitField{Insert,Extract,Clear}.)
  static constexpr size_t GetLsbForPosition(size_t position) {
    DCHECK_GE(kCapacity, position);
    return GetBitLengthTotalAtPosition(position);
  }

  // How many bits are needed for a BitStringChar at the position?
  // Returns 0 if the position is out of range.
  static constexpr size_t MaybeGetBitLengthAtPosition(size_t position) {
    if (position >= kCapacity) {
      return 0;
    }
    return kBitSizeAtPosition[position];
  }

  // Read a bitchar at some index within the capacity.
  // See also "BitString[N]" in the doc header.
  BitStringChar operator[](size_t idx) const {
    DCHECK_LT(idx, kCapacity);

    StorageType data = BitFieldExtract(storage_, GetLsbForPosition(idx), kBitSizeAtPosition[idx]);

    return BitStringChar(data, kBitSizeAtPosition[idx]);
  }

  // Overwrite a bitchar at a position with a new one.
  //
  // The `bitchar` bitlength must be no more than the maximum bitlength for that position.
  void SetAt(size_t idx, BitStringChar bitchar) {
    DCHECK_LT(idx, kCapacity);
    DCHECK_LE(bitchar.GetBitLength(), kBitSizeAtPosition[idx]);

    // Read the bitchar: Bits > bitlength in bitchar are defined to be 0.
    storage_ = BitFieldInsert(storage_,
                              static_cast<StorageType>(bitchar),
                              GetLsbForPosition(idx),
                              kBitSizeAtPosition[idx]);
  }

  // How many characters are there in this bitstring?
  // Trailing 0s are ignored, but 0s in-between are counted.
  // See also "StrLen(BitString)" in the doc header.
  size_t Length() const {
    size_t num_trailing_zeros = 0;
    size_t i;
    for (i = kCapacity - 1u; ; --i) {
      BitStringChar bc = (*this)[i];
      if (bc != 0u) {
        break;  // Found first trailing non-zero.
      }

      ++num_trailing_zeros;
      if (i == 0u) {
        break;  // No more bitchars remaining: don't underflow.
      }
    }

    return kCapacity - num_trailing_zeros;
  }

  // Cast to the underlying integral storage type.
  explicit operator StorageType() const {
    return storage_;
  }

  // Get the # of bits this would use if it was nested inside of a BitStruct.
  static constexpr size_t BitStructSizeOf() {
    return GetBitLengthTotalAtPosition(kCapacity);
  }

  BitString() = default;

  // Efficient O(1) comparison: Equal if both bitstring words are the same.
  bool operator==(const BitString& other) const {
    return storage_ == other.storage_;
  }

  // Efficient O(1) negative comparison: Not-equal if both bitstring words are different.
  bool operator!=(const BitString& other) const {
    return !(*this == other);
  }

  // Does this bitstring contain exactly 0 characters?
  bool IsEmpty() const {
    return (*this) == BitString{};
  }

  // Remove all BitStringChars starting at end.
  // Returns the BitString[0..end) substring as a copy.
  // See also "BitString[I..N)" in the doc header.
  BitString Truncate(size_t end) {
    DCHECK_GE(kCapacity, end);
    BitString copy = *this;

    if (end < kCapacity) {
      size_t lsb = GetLsbForPosition(end);
      size_t bit_size = GetLsbForPosition(kCapacity) - lsb;
      StorageType data = BitFieldClear(copy.storage_, lsb, bit_size);
      copy.storage_ = data;
    }

    return copy;
  }

 private:
  friend std::ostream& operator<<(std::ostream& os, const BitString& bit_string);

  // Data is stored with the first character in the least-significant-bit.
  // Unused bits are zero.
  StorageType storage_;
};

static_assert(BitSizeOf<BitString::StorageType>() >=
                  BitString::GetBitLengthTotalAtPosition(BitString::kCapacity),
              "Storage type is too small for the # of bits requested");

// Print e.g. "BitString[1,0,3]". Trailing 0s are dropped.
inline std::ostream& operator<<(std::ostream& os, const BitString& bit_string) {
  const size_t length = bit_string.Length();

  os << "BitString[";
  for (size_t i = 0; i < length; ++i) {
    BitStringChar bc = bit_string[i];
    if (i != 0) {
      os << ",";
    }
    os << static_cast<BitString::StorageType>(bc);
  }
  os << "]";
  return os;
}

}  // namespace art

#endif  // ART_LIBARTBASE_BASE_BIT_STRING_H_