lin
2025-07-31 065ea569db06206874bbfa18eb25ff6121aec09b
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
/*
 * 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_