blob: 8c9d4cc8253e2491febc8fe10f5dba6015c8ca04 [file] [log] [blame]
Zachary Turner0eace0b2016-05-02 18:09:14 +00001//===- NameHashTable.cpp - PDB Name Hash Table ------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/DebugInfo/PDB/Raw/NameHashTable.h"
11
12#include "llvm/ADT/ArrayRef.h"
Zachary Turnerd5d37dc2016-05-25 20:37:03 +000013#include "llvm/DebugInfo/CodeView/StreamReader.h"
Zachary Turner819e77d2016-05-06 20:51:57 +000014#include "llvm/DebugInfo/PDB/Raw/RawError.h"
Zachary Turner0eace0b2016-05-02 18:09:14 +000015#include "llvm/Support/Endian.h"
16
17using namespace llvm;
18using namespace llvm::support;
19using namespace llvm::pdb;
20
Rui Ueyama4a1ebae2016-06-07 00:59:04 +000021// Corresponds to `Hasher::lhashPbCb` in PDB/include/misc.h.
Zachary Turner0eace0b2016-05-02 18:09:14 +000022static inline uint32_t HashStringV1(StringRef Str) {
23 uint32_t Result = 0;
24 uint32_t Size = Str.size();
25
26 ArrayRef<ulittle32_t> Longs(reinterpret_cast<const ulittle32_t *>(Str.data()),
27 Size / 4);
28
29 for (auto Value : Longs)
30 Result ^= Value;
31
32 const uint8_t *Remainder = reinterpret_cast<const uint8_t *>(Longs.end());
33 uint32_t RemainderSize = Size - Longs.size() * 4;
34
35 // Maximum of 3 bytes left. Hash a 2 byte word if possible, then hash the
36 // possibly remaining 1 byte.
37 if (RemainderSize >= 2) {
Zachary Turnera801dc12016-05-02 18:36:58 +000038 uint16_t Value = *reinterpret_cast<const ulittle16_t *>(Remainder);
39 Result ^= static_cast<uint32_t>(Value);
Zachary Turner0eace0b2016-05-02 18:09:14 +000040 Remainder += 2;
41 RemainderSize -= 2;
42 }
43
44 // hash possible odd byte
45 if (RemainderSize == 1) {
46 Result ^= *(Remainder++);
47 }
48
49 const uint32_t toLowerMask = 0x20202020;
50 Result |= toLowerMask;
51 Result ^= (Result >> 11);
52
53 return Result ^ (Result >> 16);
54}
55
Rui Ueyama4a1ebae2016-06-07 00:59:04 +000056// Corresponds to `HasherV2::HashULONG` in PDB/include/misc.h.
Zachary Turner0eace0b2016-05-02 18:09:14 +000057static inline uint32_t HashStringV2(StringRef Str) {
58 uint32_t Hash = 0xb170a1bf;
59
60 ArrayRef<char> Buffer(Str.begin(), Str.end());
61
62 ArrayRef<ulittle32_t> Items(
63 reinterpret_cast<const ulittle32_t *>(Buffer.data()),
64 Buffer.size() / sizeof(ulittle32_t));
65 for (ulittle32_t Item : Items) {
66 Hash += Item;
67 Hash += (Hash << 10);
68 Hash ^= (Hash >> 6);
69 }
70 Buffer = Buffer.slice(Items.size() * sizeof(ulittle32_t));
71 for (uint8_t Item : Buffer) {
72 Hash += Item;
73 Hash += (Hash << 10);
74 Hash ^= (Hash >> 6);
75 }
76
77 return Hash * 1664525L + 1013904223L;
78}
79
80NameHashTable::NameHashTable() : Signature(0), HashVersion(0), NameCount(0) {}
81
Zachary Turnerd5d37dc2016-05-25 20:37:03 +000082Error NameHashTable::load(codeview::StreamReader &Stream) {
Zachary Turner0eace0b2016-05-02 18:09:14 +000083 struct Header {
84 support::ulittle32_t Signature;
85 support::ulittle32_t HashVersion;
86 support::ulittle32_t ByteSize;
87 };
88
Zachary Turner8dbe3622016-05-27 01:54:44 +000089 const Header *H;
90 if (auto EC = Stream.readObject(H))
Zachary Turner819e77d2016-05-06 20:51:57 +000091 return EC;
92
Zachary Turner8dbe3622016-05-27 01:54:44 +000093 if (H->Signature != 0xEFFEEFFE)
Zachary Turner819e77d2016-05-06 20:51:57 +000094 return make_error<RawError>(raw_error_code::corrupt_file,
95 "Invalid hash table signature");
Zachary Turner8dbe3622016-05-27 01:54:44 +000096 if (H->HashVersion != 1 && H->HashVersion != 2)
Zachary Turner819e77d2016-05-06 20:51:57 +000097 return make_error<RawError>(raw_error_code::corrupt_file,
98 "Unsupported hash version");
Zachary Turner0eace0b2016-05-02 18:09:14 +000099
Zachary Turner8dbe3622016-05-27 01:54:44 +0000100 Signature = H->Signature;
101 HashVersion = H->HashVersion;
102 if (auto EC = Stream.readStreamRef(NamesBuffer, H->ByteSize))
David Majnemer836937e2016-05-27 16:16:56 +0000103 return joinErrors(std::move(EC),
104 make_error<RawError>(raw_error_code::corrupt_file,
105 "Invalid hash table byte length"));
Zachary Turner0eace0b2016-05-02 18:09:14 +0000106
Zachary Turner8dbe3622016-05-27 01:54:44 +0000107 const support::ulittle32_t *HashCount;
108 if (auto EC = Stream.readObject(HashCount))
Zachary Turner819e77d2016-05-06 20:51:57 +0000109 return EC;
110
Zachary Turnerb393d952016-05-27 03:51:53 +0000111 if (auto EC = Stream.readArray(IDs, *HashCount))
David Majnemer836937e2016-05-27 16:16:56 +0000112 return joinErrors(std::move(EC),
113 make_error<RawError>(raw_error_code::corrupt_file,
114 "Could not read bucket array"));
Zachary Turner0eace0b2016-05-02 18:09:14 +0000115
116 if (Stream.bytesRemaining() < sizeof(support::ulittle32_t))
Zachary Turner819e77d2016-05-06 20:51:57 +0000117 return make_error<RawError>(raw_error_code::corrupt_file,
118 "Missing name count");
Zachary Turner0eace0b2016-05-02 18:09:14 +0000119
Zachary Turner819e77d2016-05-06 20:51:57 +0000120 if (auto EC = Stream.readInteger(NameCount))
121 return EC;
122 return Error::success();
Zachary Turner0eace0b2016-05-02 18:09:14 +0000123}
124
125StringRef NameHashTable::getStringForID(uint32_t ID) const {
126 if (ID == IDs[0])
127 return StringRef();
128
Zachary Turner8dbe3622016-05-27 01:54:44 +0000129 // NamesBuffer is a buffer of null terminated strings back to back. ID is
130 // the starting offset of the string we're looking for. So just seek into
131 // the desired offset and a read a null terminated stream from that offset.
132 StringRef Result;
133 codeview::StreamReader NameReader(NamesBuffer);
134 NameReader.setOffset(ID);
135 if (auto EC = NameReader.readZeroString(Result))
136 consumeError(std::move(EC));
137 return Result;
Zachary Turner0eace0b2016-05-02 18:09:14 +0000138}
139
140uint32_t NameHashTable::getIDForString(StringRef Str) const {
141 uint32_t Hash = (HashVersion == 1) ? HashStringV1(Str) : HashStringV2(Str);
142 size_t Count = IDs.size();
143 uint32_t Start = Hash % Count;
144 for (size_t I = 0; I < Count; ++I) {
145 // The hash is just a starting point for the search, but if it
146 // doesn't work we should find the string no matter what, because
147 // we iterate the entire array.
148 uint32_t Index = (Start + I) % Count;
149
150 uint32_t ID = IDs[Index];
151 StringRef S = getStringForID(ID);
152 if (S == Str)
153 return ID;
154 }
155 // IDs[0] contains the ID of the "invalid" entry.
156 return IDs[0];
157}
158
Zachary Turnerb393d952016-05-27 03:51:53 +0000159codeview::FixedStreamArray<support::ulittle32_t>
160NameHashTable::name_ids() const {
161 return IDs;
162}