blob: 5f002d1d7272ea8f74ba3b402200a9b1658e7b94 [file] [log] [blame]
/*
* 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 <android-base/logging.h>
#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<const uint8_t*>(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<uint32_t>::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<Entry[]> 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<Entry[]> owned_entries_;
};
} // namespace art
#endif // ART_LIBDEXFILE_DEX_TYPE_LOOKUP_TABLE_H_