Coverage Report

Created: 2023-08-08 09:12

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/include/lldb/Core/MappedHash.h
Line
Count
Source (jump to first uncovered line)
1
//===-- MappedHash.h --------------------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#ifndef LLDB_CORE_MAPPEDHASH_H
10
#define LLDB_CORE_MAPPEDHASH_H
11
12
#include <cassert>
13
#include <cstdint>
14
15
#include <algorithm>
16
#include <functional>
17
#include <map>
18
#include <vector>
19
20
#include "lldb/Utility/DataExtractor.h"
21
#include "lldb/Utility/Stream.h"
22
#include "llvm/Support/DJB.h"
23
24
class MappedHash {
25
public:
26
  enum HashFunctionType {
27
    eHashFunctionDJB = 0u // Daniel J Bernstein hash function that is also used
28
                          // by the ELF GNU_HASH sections
29
  };
30
31
0
  static uint32_t HashString(uint32_t hash_function, llvm::StringRef s) {
32
0
    switch (hash_function) {
33
0
    case MappedHash::eHashFunctionDJB:
34
0
      return llvm::djbHash(s);
35
0
36
0
    default:
37
0
      break;
38
0
    }
39
0
    llvm_unreachable("Invalid hash function index");
40
0
  }
41
42
  static const uint32_t HASH_MAGIC = 0x48415348u;
43
  static const uint32_t HASH_CIGAM = 0x48534148u;
44
45
  template <typename T> struct Header {
46
    typedef T HeaderData;
47
48
    uint32_t
49
        magic; // HASH_MAGIC or HASH_CIGAM magic value to allow endian detection
50
    uint16_t version = 1; // Version number
51
    uint16_t hash_function =
52
        eHashFunctionDJB;      // The hash function enumeration that was used
53
    uint32_t bucket_count = 0; // The number of buckets in this hash table
54
    uint32_t hashes_count = 0; // The total number of unique hash values and
55
                               // hash data offsets in this table
56
    uint32_t header_data_len; // The size in bytes of the "header_data" template
57
                              // member below
58
    HeaderData header_data;   //
59
60
    Header() : magic(HASH_MAGIC), header_data_len(sizeof(T)), header_data() {}
61
62
    virtual ~Header() = default;
63
64
    size_t GetByteSize() const {
65
      return sizeof(magic) + sizeof(version) + sizeof(hash_function) +
66
             sizeof(bucket_count) + sizeof(hashes_count) +
67
             sizeof(header_data_len) + header_data_len;
68
    }
69
70
    virtual size_t GetByteSize(const HeaderData &header_data) = 0;
71
72
    void SetHeaderDataByteSize(uint32_t header_data_byte_size) {
73
      header_data_len = header_data_byte_size;
74
    }
75
76
    void Dump(lldb_private::Stream &s) {
77
      s.Printf("header.magic              = 0x%8.8x\n", magic);
78
      s.Printf("header.version            = 0x%4.4x\n", version);
79
      s.Printf("header.hash_function      = 0x%4.4x\n", hash_function);
80
      s.Printf("header.bucket_count       = 0x%8.8x %u\n", bucket_count,
81
               bucket_count);
82
      s.Printf("header.hashes_count       = 0x%8.8x %u\n", hashes_count,
83
               hashes_count);
84
      s.Printf("header.header_data_len    = 0x%8.8x %u\n", header_data_len,
85
               header_data_len);
86
    }
87
88
    virtual lldb::offset_t Read(lldb_private::DataExtractor &data,
89
                                lldb::offset_t offset) {
90
      if (data.ValidOffsetForDataOfSize(
91
              offset, sizeof(magic) + sizeof(version) + sizeof(hash_function) +
92
                          sizeof(bucket_count) + sizeof(hashes_count) +
93
                          sizeof(header_data_len))) {
94
        magic = data.GetU32(&offset);
95
        if (magic != HASH_MAGIC) {
96
          if (magic == HASH_CIGAM) {
97
            switch (data.GetByteOrder()) {
98
            case lldb::eByteOrderBig:
99
              data.SetByteOrder(lldb::eByteOrderLittle);
100
              break;
101
            case lldb::eByteOrderLittle:
102
              data.SetByteOrder(lldb::eByteOrderBig);
103
              break;
104
            default:
105
              return LLDB_INVALID_OFFSET;
106
            }
107
          } else {
108
            // Magic bytes didn't match
109
            version = 0;
110
            return LLDB_INVALID_OFFSET;
111
          }
112
        }
113
114
        version = data.GetU16(&offset);
115
        if (version != 1) {
116
          // Unsupported version
117
          return LLDB_INVALID_OFFSET;
118
        }
119
        hash_function = data.GetU16(&offset);
120
        if (hash_function == 4)
121
          hash_function = 0; // Deal with pre-release version of this table...
122
        bucket_count = data.GetU32(&offset);
123
        hashes_count = data.GetU32(&offset);
124
        header_data_len = data.GetU32(&offset);
125
        return offset;
126
      }
127
      return LLDB_INVALID_OFFSET;
128
    }
129
    //
130
    //        // Returns a buffer that contains a serialized version of this
131
    //        table
132
    //        // that must be freed with free().
133
    //        virtual void *
134
    //        Write (int fd);
135
  };
136
137
  // A class for reading and using a saved hash table from a block of data
138
  // in memory
139
  template <typename __KeyType, class __HeaderType, class __HashData>
140
  class MemoryTable {
141
  public:
142
    typedef __HeaderType HeaderType;
143
    typedef __KeyType KeyType;
144
    typedef __HashData HashData;
145
146
    enum Result {
147
      eResultKeyMatch = 0u, // The entry was found, key matched and "pair" was
148
                            // filled in successfully
149
      eResultKeyMismatch =
150
          1u, // Bucket hash data collision, but key didn't match
151
      eResultEndOfHashData = 2u, // The chain of items for this hash data in
152
                                 // this bucket is terminated, search no more
153
      eResultError = 3u          // Status parsing the hash data, abort
154
    };
155
156
    struct Pair {
157
      KeyType key;
158
      HashData value;
159
    };
160
161
    MemoryTable(lldb_private::DataExtractor &data)
162
        : m_header(), m_hash_indexes(nullptr), m_hash_values(nullptr),
163
          m_hash_offsets(nullptr) {
164
      lldb::offset_t offset = m_header.Read(data, 0);
165
      if (offset != LLDB_INVALID_OFFSET && IsValid()) {
166
        m_hash_indexes = (const uint32_t *)data.GetData(
167
            &offset, m_header.bucket_count * sizeof(uint32_t));
168
        m_hash_values = (const uint32_t *)data.GetData(
169
            &offset, m_header.hashes_count * sizeof(uint32_t));
170
        m_hash_offsets = (const uint32_t *)data.GetData(
171
            &offset, m_header.hashes_count * sizeof(uint32_t));
172
      }
173
    }
174
175
    virtual ~MemoryTable() = default;
176
177
    bool IsValid() const {
178
      return m_header.version == 1 &&
179
             m_header.hash_function == eHashFunctionDJB &&
180
             m_header.bucket_count > 0;
181
    }
182
183
    uint32_t GetHashIndex(uint32_t bucket_idx) const {
184
      uint32_t result = UINT32_MAX;
185
      if (m_hash_indexes && bucket_idx < m_header.bucket_count)
186
        memcpy(&result, m_hash_indexes + bucket_idx, sizeof(uint32_t));
187
      return result;
188
    }
189
190
    uint32_t GetHashValue(uint32_t hash_idx) const {
191
      uint32_t result = UINT32_MAX;
192
      if (m_hash_values && hash_idx < m_header.hashes_count)
193
        memcpy(&result, m_hash_values + hash_idx, sizeof(uint32_t));
194
      return result;
195
    }
196
197
    uint32_t GetHashDataOffset(uint32_t hash_idx) const {
198
      uint32_t result = UINT32_MAX;
199
      if (m_hash_offsets && hash_idx < m_header.hashes_count)
200
        memcpy(&result, m_hash_offsets + hash_idx, sizeof(uint32_t));
201
      return result;
202
    }
203
204
    bool Find(llvm::StringRef name, Pair &pair) const {
205
      if (name.empty())
206
        return false;
207
208
      if (IsValid()) {
209
        const uint32_t bucket_count = m_header.bucket_count;
210
        const uint32_t hash_count = m_header.hashes_count;
211
        const uint32_t hash_value =
212
            MappedHash::HashString(m_header.hash_function, name);
213
        const uint32_t bucket_idx = hash_value % bucket_count;
214
        uint32_t hash_idx = GetHashIndex(bucket_idx);
215
        if (hash_idx < hash_count) {
216
          for (; hash_idx < hash_count; ++hash_idx) {
217
            const uint32_t curr_hash_value = GetHashValue(hash_idx);
218
            if (curr_hash_value == hash_value) {
219
              lldb::offset_t hash_data_offset = GetHashDataOffset(hash_idx);
220
              while (hash_data_offset != UINT32_MAX) {
221
                const lldb::offset_t prev_hash_data_offset = hash_data_offset;
222
                Result hash_result =
223
                    GetHashDataForName(name, &hash_data_offset, pair);
224
                // Check the result of getting our hash data
225
                switch (hash_result) {
226
                case eResultKeyMatch:
227
                  return true;
228
229
                case eResultKeyMismatch:
230
                  if (prev_hash_data_offset == hash_data_offset)
231
                    return false;
232
                  break;
233
234
                case eResultEndOfHashData:
235
                  // The last HashData for this key has been reached, stop
236
                  // searching
237
                  return false;
238
                case eResultError:
239
                  // Status parsing the hash data, abort
240
                  return false;
241
                }
242
              }
243
            }
244
            if ((curr_hash_value % bucket_count) != bucket_idx)
245
              break;
246
          }
247
        }
248
      }
249
      return false;
250
    }
251
252
    // This method must be implemented in any subclasses. The KeyType is user
253
    // specified and must somehow result in a string value. For example, the
254
    // KeyType might be a string offset in a string table and subclasses can
255
    // store their string table as a member of the subclass and return a valie
256
    // "const char *" given a "key". The value could also be a C string
257
    // pointer, in which case just returning "key" will suffice.
258
    virtual const char *GetStringForKeyType(KeyType key) const = 0;
259
260
    virtual bool ReadHashData(uint32_t hash_data_offset,
261
                              HashData &hash_data) const = 0;
262
263
    // This method must be implemented in any subclasses and it must try to
264
    // read one "Pair" at the offset pointed to by the "hash_data_offset_ptr"
265
    // parameter. This offset should be updated as bytes are consumed and a
266
    // value "Result" enum should be returned. If the "name" matches the full
267
    // name for the "pair.key" (which must be filled in by this call), then the
268
    // HashData in the pair ("pair.value") should be extracted and filled in
269
    // and "eResultKeyMatch" should be returned. If "name" doesn't match this
270
    // string for the key, then "eResultKeyMismatch" should be returned and all
271
    // data for the current HashData must be consumed or skipped and the
272
    // "hash_data_offset_ptr" offset needs to be updated to point to the next
273
    // HashData. If the end of the HashData objects for a given hash value have
274
    // been reached, then "eResultEndOfHashData" should be returned. If
275
    // anything else goes wrong during parsing, return "eResultError" and the
276
    // corresponding "Find()" function will be canceled and return false.
277
    virtual Result GetHashDataForName(llvm::StringRef name,
278
                                      lldb::offset_t *hash_data_offset_ptr,
279
                                      Pair &pair) const = 0;
280
281
    const HeaderType &GetHeader() { return m_header; }
282
283
    void ForEach(
284
        std::function<bool(const HashData &hash_data)> const &callback) const {
285
      const size_t num_hash_offsets = m_header.hashes_count;
286
      for (size_t i = 0; i < num_hash_offsets; ++i) {
287
        uint32_t hash_data_offset = GetHashDataOffset(i);
288
        if (hash_data_offset != UINT32_MAX) {
289
          HashData hash_data;
290
          if (ReadHashData(hash_data_offset, hash_data)) {
291
            // If the callback returns false, then we are done and should stop
292
            if (callback(hash_data) == false)
293
              return;
294
          }
295
        }
296
      }
297
    }
298
299
  protected:
300
    // Implementation agnostic information
301
    HeaderType m_header;
302
    const uint32_t *m_hash_indexes;
303
    const uint32_t *m_hash_values;
304
    const uint32_t *m_hash_offsets;
305
  };
306
};
307
308
#endif // LLDB_CORE_MAPPEDHASH_H