/* * 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 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_v, "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(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(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(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::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(bc); } os << "]"; return os; } } // namespace art #endif // ART_LIBARTBASE_BASE_BIT_STRING_H_