/* * Copyright (C) 2015 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_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ #define ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_ #include #include "dex/dex_file_types.h" namespace art { class DexFile; /** * TypeLookupTable used to find class_def_idx by class descriptor quickly. * Implementation of TypeLookupTable is based on hash table. * This class instantiated at compile time by calling Create() method and written into OAT file. * At runtime, the raw data is read from memory-mapped file by calling Open() method. The table * memory remains clean. */ class TypeLookupTable { public: // Method creates lookup table for dex file. static TypeLookupTable Create(const DexFile& dex_file); // Method opens lookup table from binary data. Lookups will traverse strings and other // data contained in dex_file as well. Lookup table does not own raw_data or dex_file. static TypeLookupTable Open(const uint8_t* dex_data_pointer, const uint8_t* raw_data, uint32_t num_class_defs); // Create an invalid lookup table. TypeLookupTable() : dex_data_begin_(nullptr), mask_bits_(0u), entries_(nullptr), owned_entries_(nullptr) {} TypeLookupTable(TypeLookupTable&& src) noexcept = default; TypeLookupTable& operator=(TypeLookupTable&& src) noexcept = default; ~TypeLookupTable() { // Implicit deallocation by std::unique_ptr<> destructor. } // Returns whether the TypeLookupTable is valid. bool Valid() const { return entries_ != nullptr; } // Return the number of buckets in the lookup table. uint32_t Size() const { DCHECK(Valid()); return 1u << mask_bits_; } // Method search class_def_idx by class descriptor and it's hash. // If no data found then the method returns dex::kDexNoIndex. uint32_t Lookup(const char* str, uint32_t hash) const; // Method returns pointer to binary data of lookup table. Used by the oat writer. const uint8_t* RawData() const { DCHECK(Valid()); return reinterpret_cast(entries_); } // Method returns length of binary data. Used by the oat writer. uint32_t RawDataLength() const { DCHECK(Valid()); return Size() * sizeof(Entry); } // Method returns length of binary data for the specified number of class definitions. static uint32_t RawDataLength(uint32_t num_class_defs); private: /** * To find element we need to compare strings. * It is faster to compare first hashes and then strings itself. * But we have no full hash of element of table. But we can use 2 ideas. * 1. All minor bits of hash inside one bucket are equal. * (TODO: We're not actually using this, are we?) * 2. If the dex file contains N classes and the size of the hash table is 2^n (where N <= 2^n) * then we need n bits for the class def index and n bits for the next position delta. * So we can encode part of element's hash into the remaining 32-2*n (n <= 16) bits which * would be otherwise wasted as a padding. * So hash of element can be divided on three parts: * XXXX XXXX XXXY YYYY YYYY YZZZ ZZZZ ZZZZ (example with n=11) * Z - a part of hash encoded implicitly in the bucket index * (these bits are same for all elements in bucket) * Y - a part of hash that we can write into free 32-2*n bits * X - a part of hash that we can't use without increasing the size of the entry * So the `data` element of Entry is used to store the next position delta, class_def_index * and a part of hash of the entry. */ class Entry { public: Entry() : str_offset_(0u), data_(0u) {} Entry(uint32_t str_offset, uint32_t hash, uint32_t class_def_index, uint32_t mask_bits) : str_offset_(str_offset), data_(((hash & ~GetMask(mask_bits)) | class_def_index) << mask_bits) { DCHECK_EQ(class_def_index & ~GetMask(mask_bits), 0u); } void SetNextPosDelta(uint32_t next_pos_delta, uint32_t mask_bits) { DCHECK_EQ(GetNextPosDelta(mask_bits), 0u); DCHECK_EQ(next_pos_delta & ~GetMask(mask_bits), 0u); DCHECK_NE(next_pos_delta, 0u); data_ |= next_pos_delta; } bool IsEmpty() const { return str_offset_ == 0u; } bool IsLast(uint32_t mask_bits) const { return GetNextPosDelta(mask_bits) == 0u; } uint32_t GetStringOffset() const { return str_offset_; } uint32_t GetNextPosDelta(uint32_t mask_bits) const { return data_ & GetMask(mask_bits); } uint32_t GetClassDefIdx(uint32_t mask_bits) const { return (data_ >> mask_bits) & GetMask(mask_bits); } uint32_t GetHashBits(uint32_t mask_bits) const { DCHECK_LE(mask_bits, 16u); return data_ >> (2u * mask_bits); } static uint32_t GetMask(uint32_t mask_bits) { DCHECK_LE(mask_bits, 16u); return ~(std::numeric_limits::max() << mask_bits); } private: uint32_t str_offset_; uint32_t data_; }; static uint32_t CalculateMaskBits(uint32_t num_class_defs); static bool SupportedSize(uint32_t num_class_defs); // Construct the TypeLookupTable. TypeLookupTable(const uint8_t* dex_data_pointer, uint32_t mask_bits, const Entry* entries, std::unique_ptr owned_entries); const char* GetStringData(const Entry& entry) const; const uint8_t* dex_data_begin_; uint32_t mask_bits_; const Entry* entries_; // `owned_entries_` is either null (not owning `entries_`) or same pointer as `entries_`. std::unique_ptr owned_entries_; }; } // namespace art #endif // ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_